diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2a4d53ab6..4019cf4b3 100755 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -29,7 +29,7 @@ Stack trace or log information > The simulator is a valid device as well. If all versions or devices seem to be affected, simply enter 'Any' * Application version: _version_ -* iOS version: _version_ +* iOS / tvOS version: _version_ * Device: _model_ ### Reproducibility diff --git a/Application/Resources/Apps/Play RSI/ApplicationConfiguration.json b/Application/Resources/Apps/Play RSI/ApplicationConfiguration.json index 777d67a56..144f3d2fd 100755 --- a/Application/Resources/Apps/Play RSI/ApplicationConfiguration.json +++ b/Application/Resources/Apps/Play RSI/ApplicationConfiguration.json @@ -1,7 +1,8 @@ { "businessUnit": "rsi", "container": 10, - "comScoreVirtualSite": "rsi-player-ios-v", + "siteName": "rsi-player-ios-v", + "tvSiteName": "rsi-player-tvos-apple", "netMetrixIdentifier": "srgplayer", "voiceOverLanguageCode": "it", "appStoreProductIdentifier": 920753497, diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/Contents.json +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/logo_rete_due-60.imageset/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/logo_rete_due-60.imageset/Contents.json new file mode 100644 index 000000000..90879b402 --- /dev/null +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/logo_rete_due-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_rsiDUE_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/logo_rete_due-60.imageset/logosradio_rsiDUE_60.pdf b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/logo_rete_due-60.imageset/logosradio_rsiDUE_60.pdf new file mode 100644 index 000000000..ee0b74788 Binary files /dev/null and b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Due/logo_rete_due-60.imageset/logosradio_rsiDUE_60.pdf differ diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/Contents.json +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/logo_rete_tre-60.imageset/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/logo_rete_tre-60.imageset/Contents.json new file mode 100644 index 000000000..879fb4184 --- /dev/null +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/logo_rete_tre-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_rsiTRE_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/logo_rete_tre-60.imageset/logosradio_rsiTRE_60.pdf b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/logo_rete_tre-60.imageset/logosradio_rsiTRE_60.pdf new file mode 100644 index 000000000..c46b2360e Binary files /dev/null and b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Tre/logo_rete_tre-60.imageset/logosradio_rsiTRE_60.pdf differ diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/Contents.json +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/logo_rete_uno-60.imageset/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/logo_rete_uno-60.imageset/Contents.json new file mode 100644 index 000000000..03c7b7933 --- /dev/null +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/logo_rete_uno-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_rsiUNO_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/logo_rete_uno-60.imageset/logosradio_rsiUNO_60.pdf b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/logo_rete_uno-60.imageset/logosradio_rsiUNO_60.pdf new file mode 100644 index 000000000..7f361010b Binary files /dev/null and b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/Radio/Rete Uno/logo_rete_uno-60.imageset/logosradio_rsiUNO_60.pdf differ diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/Contents.json +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/logo_la1-60.imageset/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/logo_la1-60.imageset/Contents.json new file mode 100644 index 000000000..803363895 --- /dev/null +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/logo_la1-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_rsiLA1_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/logo_la1-60.imageset/logos_rsiLA1_60.pdf b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/logo_la1-60.imageset/logos_rsiLA1_60.pdf new file mode 100644 index 000000000..2dfcfd284 Binary files /dev/null and b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La1/logo_la1-60.imageset/logos_rsiLA1_60.pdf differ diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/Contents.json +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/logo_la2-60.imageset/Contents.json b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/logo_la2-60.imageset/Contents.json new file mode 100644 index 000000000..a8ab86375 --- /dev/null +++ b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/logo_la2-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_rsiLA2_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/logo_la2-60.imageset/logos_rsiLA2_60.pdf b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/logo_la2-60.imageset/logos_rsiLA2_60.pdf new file mode 100644 index 000000000..5ac5ed0b6 Binary files /dev/null and b/Application/Resources/Apps/Play RSI/RSIResources.xcassets/TV/La2/logo_la2-60.imageset/logos_rsiLA2_60.pdf differ diff --git a/Application/Resources/Apps/Play RSI/it.lproj/Accessibility.strings b/Application/Resources/Apps/Play RSI/it.lproj/Accessibility.strings index 6480c2ac4..15480db38 100755 --- a/Application/Resources/Apps/Play RSI/it.lproj/Accessibility.strings +++ b/Application/Resources/Apps/Play RSI/it.lproj/Accessibility.strings @@ -14,11 +14,14 @@ /* Label displaying the number of views on the player */ "%@ views" = "%@ visualizzazioni"; +/* Title pronounced in home pages on shows A to Z button. */ +"A to Z shows" = "Programmi dalla A alla Z"; + /* Favorite show label when not in favorites, in the player view Favorite show label when not in favorites, in the show view */ "Add to favorites" = "Aggiungere ai preferiti"; -/* Media watch later creation label */ +/* Media watch later addition label */ "Add to the watch later list" = "Aggiungere a \"Guardare dopo\""; /* Title of the first cell of a media list on homepage. */ @@ -66,9 +69,6 @@ /* Introductory title for information notifications */ "Information" = "Informazioni"; -/* Label on recently played livestreams */ -"Last played" = "Ultimo contenuto visionato"; - /* Accessibility introductory text for the logged in user */ "Logged in user: %@" = "Utente connesso: %@"; @@ -123,12 +123,6 @@ /* Song cell hint */ "Plays the music." = "Riprodurre la musica."; -/* Title displayed in home page shows section. */ -"Programmes A-Z" = "Trasmissioni dalla A alla Z"; - -/* Title displayed in home page shows section. */ -"Programmes by date" = "Trasmissioni per data"; - /* Mini player label */ "Recently played: %@" = "Riprodotto di recente: %@"; @@ -160,6 +154,9 @@ /* Homepage header action hint */ "Shows all contents." = "Mostra tutti i contenuti."; +/* Title pronounced in home pages on shows by date button. */ +"Shows by date" = "Programmi per data"; + /* Stop button label */ "Stop" = "Stop"; diff --git a/Application/Resources/Apps/Play RSI/it.lproj/Localizable.strings b/Application/Resources/Apps/Play RSI/it.lproj/Localizable.strings index 7a4261f0d..b30e29270 100755 --- a/Application/Resources/Apps/Play RSI/it.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play RSI/it.lproj/Localizable.strings @@ -16,6 +16,9 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (placeholder is the device name) */ "%@ is idle." = "%@ è inattivo."; +/* Short label displayed on a media expiring soon */ +"%@ left" = "Ancora per %@"; + /* Label displaying the number of listenings on the player */ "%@ listenings" = "%@ ascolti"; @@ -34,7 +37,7 @@ /* More than 3 min option */ "> 30 min" = "> 30 min"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "A to Z" = "Dalla A alla Z"; /* Button label to add a media to the watch later list, from the media long-press menu @@ -59,6 +62,9 @@ /* Title of the first cell of a media list on homepage. */ "All content" = "Mostra tutto"; +/* Title label used to present already available videos, usually badged as web first */ +"Already available" = "Già disponibile"; + /* No comment provided by engineer. */ "Always send" = "Inviare sempre"; @@ -78,6 +84,7 @@ "Are you sure you want to delete the selected items?" = "Sei sicuro di voler eliminare gli elementi selezionati?"; /* Audios option + Audios tab title Header for audio search results Title displayed at the top of the audio view */ "Audios" = "Audio"; @@ -92,7 +99,7 @@ /* Title label used to present the soon expiring videos */ "Available for a limited time" = "Disponibile per un periodo limitato"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "By date" = "Per data"; /* Label of the button to close the media long-press menu @@ -165,6 +172,9 @@ /* Short label identifying content which has expired. */ "Expired" = "Scaduto"; +/* Show favorite button label */ +"Favorite" = "Preferito"; + /* Favorite show label when added ,in the show view Label to present Favorites Title label used to present the radio favorite shows @@ -184,12 +194,17 @@ /* Total free space size, display at the bottom of download list */ "Free space: %@" = "Spazio libero: %@"; -/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available */ +/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available + Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available */ "Highlights" = "Highlights"; -/* Label to present history */ +/* Label to present history + Profile tab title */ "History" = "Cronologia"; +/* Home tab title */ +"Home" = "Home"; + /* Image copyright introductory label */ "Image credit: %@" = "Immagine: %@"; @@ -205,7 +220,8 @@ /* Period setting option */ "Last week" = "Ultima settimana"; -/* Label for the button for deciding to opt-in for background video playback at a later time */ +/* Label for the button for deciding to opt-in for background video playback at a later time + Watch or listen later button label in media detail view */ "Later" = "Dopo"; /* Title label used to present the latest videos @@ -218,6 +234,9 @@ /* Explains that a content has expired, will expire or will be available in less than one hour. Displayed in the media player view. */ "less than 1 hour" = "meno di 1 ora"; +/* Play button label for audio in media detail view */ +"Listen" = "Ascolta"; + /* Suggested invocation phrase to listen to a show Suggested invocation phrase to listen to an audio User activity title when listening to an audio */ @@ -228,6 +247,7 @@ "Live" = "Live"; /* Label to present the Livestreams view + Livestreams tab title Title displayed at the top of the livestream view */ "Livestreams" = "Live"; @@ -248,6 +268,7 @@ /* Button label to open the show episode page from the long-press menu Button label to open the show episode page from the preview window + Button to access more episodes from the media detail view Context menu action to open more episodes associated with a media */ "More episodes" = "Più episodi"; @@ -349,15 +370,10 @@ /* Title of the button to proceed to the previous onboarding page */ "Previous" = "Precedente"; -/* Title displayed at the top of the profile view */ +/* Profile tab title + Title displayed at the top of the profile view */ "Profile" = "Profilo"; -/* Label to present shows A to Z (radio or TV) */ -"Programmes A-Z" = "Trasmissioni dalla A alla Z"; - -/* Label to present programmes by date */ -"Programmes by date" = "Trasmissioni per data"; - /* Settings section header */ "Properties" = "Proprietà"; @@ -424,8 +440,12 @@ /* Title of the reset search settings button */ "Reset" = "Reset"; +/* Resume playback button label */ +"Resume" = "Riprendi"; + /* Label to present the search view Search placeholder text + Search tab title Title displayed when there is no search criterium entered */ "Search" = "Ricerca"; @@ -459,9 +479,16 @@ Shows search setting option list view title Title label used to present radio associated shows Title label used to present the radio shows AZ and radio shows by date access buttons - Title label used to present the TV shows AZ and TV shows by date access buttons */ + Title label used to present the TV shows AZ and TV shows by date access buttons + Shows tab title */ "Shows" = "Programmi"; +/* Label to present shows A to Z (radio or TV) */ +"Shows A to Z" = "Programmi dalla A alla Z"; + +/* Label to present shows (episodes) by date (radio or TV) */ +"Shows by date" = "Programmi per data"; + /* Title of the button to skip updating the application */ "Skip" = "Salta"; @@ -505,6 +532,9 @@ Message displayed when song title and artist name have been copied to the pasteboard */ "The content has been copied to the clipboard." = "Il contenuto è stato copiato negli Appunti."; +/* Error message returned for invalid data */ +"The data is invalid" = "I dati non sono validi"; + /* Button label to share the entire episode being played. */ "The entire episode" = "L'intero episodio"; @@ -546,6 +576,9 @@ /* Message displayed when attempting to play some content not allowed to be played with Google Cast */ "This content is not allowed to be played with Google Cast." = "Non è permesso riprodurre questo contenuto con Google Cast."; +/* Related content media list title */ +"This might interest you" = "Potrebbero interessarti"; + /* Period setting option */ "This week" = "Questa settimana"; @@ -585,6 +618,9 @@ /* Header for video and audio search results */ "Videos and audios" = "Video e audio"; +/* Play button label for video in media detail view */ +"Watch" = "Guarda"; + /* Suggested invocation phrase to watch a show Suggested invocation phrase to watch a video User activity title when watching a video */ @@ -593,7 +629,8 @@ /* Label to present the watch later list */ "Watch later" = "Guardare dopo"; -/* Web first label on media cells */ +/* Web first label on media cells + Web first label on media detail page */ "Web first" = "Web first"; /* Title displayed at the top of the What's new view */ diff --git a/Application/Resources/Apps/Play RTR/ApplicationConfiguration.json b/Application/Resources/Apps/Play RTR/ApplicationConfiguration.json index 9393be416..be9ff30ec 100755 --- a/Application/Resources/Apps/Play RTR/ApplicationConfiguration.json +++ b/Application/Resources/Apps/Play RTR/ApplicationConfiguration.json @@ -1,7 +1,8 @@ { "businessUnit": "rtr", "container": 10, - "comScoreVirtualSite": "rtr-player-ios-v", + "siteName": "rtr-player-ios-v", + "tvSiteName": "rtr-player-tvos-apple", "netMetrixIdentifier": "srgplayer", "appStoreProductIdentifier": 920754925, "playURL": "https://www.rtr.ch/play/", diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/Contents.json +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/logo_radio_rtr-60.imageset/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/logo_radio_rtr-60.imageset/Contents.json new file mode 100644 index 000000000..391950bf7 --- /dev/null +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/logo_radio_rtr-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_rtr_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/logo_radio_rtr-60.imageset/logosradio_rtr_60.pdf b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/logo_radio_rtr-60.imageset/logosradio_rtr_60.pdf new file mode 100644 index 000000000..d56828ac9 Binary files /dev/null and b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/Radio/Radio RTR/logo_radio_rtr-60.imageset/logosradio_rtr_60.pdf differ diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/Contents.json +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/logo_rtr_srf_info-60.imageset/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/logo_rtr_srf_info-60.imageset/Contents.json new file mode 100644 index 000000000..c88d90c38 --- /dev/null +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/logo_rtr_srf_info-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_srfInfo_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/logo_rtr_srf_info-60.imageset/logos_srfInfo_60.pdf b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/logo_rtr_srf_info-60.imageset/logos_srfInfo_60.pdf new file mode 100644 index 000000000..d03dd42bc Binary files /dev/null and b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF Info/logo_rtr_srf_info-60.imageset/logos_srfInfo_60.pdf differ diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/Contents.json +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/logo_rtr_srf1-60.imageset/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/logo_rtr_srf1-60.imageset/Contents.json new file mode 100644 index 000000000..6e2e77a8f --- /dev/null +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/logo_rtr_srf1-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_srf1_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/logo_rtr_srf1-60.imageset/logos_srf1_60.pdf b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/logo_rtr_srf1-60.imageset/logos_srf1_60.pdf new file mode 100644 index 000000000..10756e375 Binary files /dev/null and b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF1/logo_rtr_srf1-60.imageset/logos_srf1_60.pdf differ diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/Contents.json +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/logo_rtr_srf2-60.imageset/Contents.json b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/logo_rtr_srf2-60.imageset/Contents.json new file mode 100644 index 000000000..e4b3c8dea --- /dev/null +++ b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/logo_rtr_srf2-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_srfzwei_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/logo_rtr_srf2-60.imageset/logos_srfzwei_60.pdf b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/logo_rtr_srf2-60.imageset/logos_srfzwei_60.pdf new file mode 100644 index 000000000..29944e4fc Binary files /dev/null and b/Application/Resources/Apps/Play RTR/RTRResources.xcassets/TV/RTR auf SRF2/logo_rtr_srf2-60.imageset/logos_srfzwei_60.pdf differ diff --git a/Application/Resources/Apps/Play RTR/rm.lproj/Accessibility.strings b/Application/Resources/Apps/Play RTR/rm.lproj/Accessibility.strings index c8abddbaf..1bb2869d0 100755 --- a/Application/Resources/Apps/Play RTR/rm.lproj/Accessibility.strings +++ b/Application/Resources/Apps/Play RTR/rm.lproj/Accessibility.strings @@ -14,11 +14,14 @@ /* Label displaying the number of views on the player */ "%@ views" = "%@ vis"; +/* Title pronounced in home pages on shows A to Z button. */ +"A to Z shows" = "Emissiuns dad A fin Z"; + /* Favorite show label when not in favorites, in the player view Favorite show label when not in favorites, in the show view */ "Add to favorites" = "Agiunscher a favurits"; -/* Media watch later creation label */ +/* Media watch later addition label */ "Add to the watch later list" = "Agiuntar a la glista per guardar pli tard"; /* Title of the first cell of a media list on homepage. */ @@ -66,9 +69,6 @@ /* Introductory title for information notifications */ "Information" = "Infurmaziun"; -/* Label on recently played livestreams */ -"Last played" = "Ultim guardà"; - /* Accessibility introductory text for the logged in user */ "Logged in user: %@" = "Utilisader annunzià: %@"; @@ -123,12 +123,6 @@ /* Song cell hint */ "Plays the music." = "Lascha tadlar musica."; -/* Title displayed in home page shows section. */ -"Programmes A-Z" = "Emissiuns dad A fin Z"; - -/* Title displayed in home page shows section. */ -"Programmes by date" = "Emissiuns tenor data"; - /* Mini player label */ "Recently played: %@" = "Laschà ì ultimamain: %@"; @@ -160,6 +154,9 @@ /* Homepage header action hint */ "Shows all contents." = "Mussar tuts cuntegns."; +/* Title pronounced in home pages on shows by date button. */ +"Shows by date" = "Emissiuns tenor data"; + /* Stop button label */ "Stop" = "Stop"; diff --git a/Application/Resources/Apps/Play RTR/rm.lproj/Localizable.strings b/Application/Resources/Apps/Play RTR/rm.lproj/Localizable.strings index b8bedd1d7..25632d857 100755 --- a/Application/Resources/Apps/Play RTR/rm.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play RTR/rm.lproj/Localizable.strings @@ -16,6 +16,9 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (placeholder is the device name) */ "%@ is idle." = "%@ord funcziun."; +/* Short label displayed on a media expiring soon */ +"%@ left" = "Anc %@"; + /* Label displaying the number of listenings on the player */ "%@ listenings" = "%@ consumà"; @@ -34,7 +37,7 @@ /* More than 3 min option */ "> 30 min" = "> 30 min"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "A to Z" = "Dad A fin Z"; /* Button label to add a media to the watch later list, from the media long-press menu @@ -59,6 +62,9 @@ /* Title of the first cell of a media list on homepage. */ "All content" = "Entir cuntegn"; +/* Title label used to present already available videos, usually badged as web first */ +"Already available" = "Gia disponibel"; + /* No comment provided by engineer. */ "Always send" = "Trametter adina"; @@ -78,6 +84,7 @@ "Are you sure you want to delete the selected items?" = "Vulais Vus propi stizzar il elements selectà?"; /* Audios option + Audios tab title Header for audio search results Title displayed at the top of the audio view */ "Audios" = "Audios"; @@ -92,7 +99,7 @@ /* Title label used to present the soon expiring videos */ "Available for a limited time" = "Accessibel per in tschert temp"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "By date" = "Tenor data"; /* Label of the button to close the media long-press menu @@ -165,6 +172,9 @@ /* Short label identifying content which has expired. */ "Expired" = "Expirà"; +/* Show favorite button label */ +"Favorite" = "Favorit"; + /* Favorite show label when added ,in the show view Label to present Favorites Title label used to present the radio favorite shows @@ -184,12 +194,17 @@ /* Total free space size, display at the bottom of download list */ "Free space: %@" = "Spaci liber: %@"; -/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available */ +/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available + Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available */ "Highlights" = "Highlights"; -/* Label to present history */ +/* Label to present history + Profile tab title */ "History" = "Cronologia"; +/* Home tab title */ +"Home" = "Home"; + /* Image copyright introductory label */ "Image credit: %@" = "Maletg:%@"; @@ -205,7 +220,8 @@ /* Period setting option */ "Last week" = "Ultima emna"; -/* Label for the button for deciding to opt-in for background video playback at a later time */ +/* Label for the button for deciding to opt-in for background video playback at a later time + Watch or listen later button label in media detail view */ "Later" = "Pli tard"; /* Title label used to present the latest videos @@ -218,6 +234,9 @@ /* Explains that a content has expired, will expire or will be available in less than one hour. Displayed in the media player view. */ "less than 1 hour" = "main ch'ina ura"; +/* Play button label for audio in media detail view */ +"Listen" = "Tadlar"; + /* Suggested invocation phrase to listen to a show Suggested invocation phrase to listen to an audio User activity title when listening to an audio */ @@ -228,6 +247,7 @@ "Live" = "Live"; /* Label to present the Livestreams view + Livestreams tab title Title displayed at the top of the livestream view */ "Livestreams" = "Live"; @@ -248,6 +268,7 @@ /* Button label to open the show episode page from the long-press menu Button label to open the show episode page from the preview window + Button to access more episodes from the media detail view Context menu action to open more episodes associated with a media */ "More episodes" = "Dapli episodas"; @@ -349,15 +370,10 @@ /* Title of the button to proceed to the previous onboarding page */ "Previous" = "Precedent"; -/* Title displayed at the top of the profile view */ +/* Profile tab title + Title displayed at the top of the profile view */ "Profile" = "Profils"; -/* Label to present shows A to Z (radio or TV) */ -"Programmes A-Z" = "Emissiuns dad A fin Z"; - -/* Label to present programmes by date */ -"Programmes by date" = "Emissiuns tenor data"; - /* Settings section header */ "Properties" = "Funcziunalitads"; @@ -424,8 +440,12 @@ /* Title of the reset search settings button */ "Reset" = "Reset"; +/* Resume playback button label */ +"Resume" = "Guardar vinavant"; + /* Label to present the search view Search placeholder text + Search tab title Title displayed when there is no search criterium entered */ "Search" = "Tschertga"; @@ -459,9 +479,16 @@ Shows search setting option list view title Title label used to present radio associated shows Title label used to present the radio shows AZ and radio shows by date access buttons - Title label used to present the TV shows AZ and TV shows by date access buttons */ + Title label used to present the TV shows AZ and TV shows by date access buttons + Shows tab title */ "Shows" = "Emissiuns"; +/* Label to present shows A to Z (radio or TV) */ +"Shows A to Z" = "Emissiuns dad A fin Z"; + +/* Label to present shows (episodes) by date (radio or TV) */ +"Shows by date" = "Emissiuns tenor data"; + /* Title of the button to skip updating the application */ "Skip" = "Finir"; @@ -505,6 +532,9 @@ Message displayed when song title and artist name have been copied to the pasteboard */ "The content has been copied to the clipboard." = "Il cuntegn è copià en communicaziuns."; +/* Error message returned for invalid data */ +"The data is invalid" = "Las datas n'èn betg valaivlas"; + /* Button label to share the entire episode being played. */ "The entire episode" = "Episoda entira"; @@ -546,6 +576,9 @@ /* Message displayed when attempting to play some content not allowed to be played with Google Cast */ "This content is not allowed to be played with Google Cast." = "Il cuntegn n'è betg lubì da Google Cast."; +/* Related content media list title */ +"This might interest you" = "Quai pudess interessar Vus"; + /* Period setting option */ "This week" = "Emna actuala"; @@ -585,6 +618,9 @@ /* Header for video and audio search results */ "Videos and audios" = "Videos ed audios"; +/* Play button label for video in media detail view */ +"Watch" = "Guardar"; + /* Suggested invocation phrase to watch a show Suggested invocation phrase to watch a video User activity title when watching a video */ @@ -593,7 +629,8 @@ /* Label to present the watch later list */ "Watch later" = "Guardar pli tard"; -/* Web first label on media cells */ +/* Web first label on media cells + Web first label on media detail page */ "Web first" = "Web l'emprim"; /* Title displayed at the top of the What's new view */ diff --git a/Application/Resources/Apps/Play RTS/ApplicationConfiguration.json b/Application/Resources/Apps/Play RTS/ApplicationConfiguration.json index 1fdeefb81..c5fff77dc 100755 --- a/Application/Resources/Apps/Play RTS/ApplicationConfiguration.json +++ b/Application/Resources/Apps/Play RTS/ApplicationConfiguration.json @@ -1,7 +1,8 @@ { "businessUnit": "rts", "container": 10, - "comScoreVirtualSite": "rts-player-ios-v", + "siteName": "rts-player-ios-v", + "tvSiteName": "rts-player-tvos-apple", "netMetrixIdentifier": "srgplayer", "voiceOverLanguageCode": "fr", "appStoreProductIdentifier": 920754415, @@ -10,7 +11,7 @@ "identityWebserviceURL": "https://hummingbird.rts.ch/api/profile", "identityWebsiteURL": "https://www.rts.ch/profile", "userDataServiceURL": "https://profil.rts.ch/api", - "videoHomeSections": "tvTrending,tvTopicsAccess,tvShowsAccess,tvFavoriteShows,tvEvents,tvTopics", + "videoHomeSections": "tvTrending,tvTopicsAccess,tvShowsAccess,tvFavoriteShows,tvWebFirst,tvEvents,tvTopics", "liveHomeSections": "tvLive,radioLive", "tvTrendingEpisodesOnly": false, "tvTrendingEditorialLimit": 3, @@ -33,7 +34,5 @@ "endToleranceRatio": 0.01, "hiddenOnboardings": "favorites,resume_playback,watch_later", "searchSettingSubtitledHidden": true, - "searchSortingCriteriumHidden": true, - "subtitleAvailabilityHidden": true, - "audioDescriptionAvailabilityHidden": true + "searchSortingCriteriumHidden": true } diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/logo_couleur3-60.imageset/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/logo_couleur3-60.imageset/Contents.json new file mode 100644 index 000000000..b0d9e9f09 --- /dev/null +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/logo_couleur3-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SecondaryLogos - Couleur3_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/logo_couleur3-60.imageset/SecondaryLogos - Couleur3_60.pdf b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/logo_couleur3-60.imageset/SecondaryLogos - Couleur3_60.pdf new file mode 100644 index 000000000..09c7ee36c Binary files /dev/null and b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Couleur 3/logo_couleur3-60.imageset/SecondaryLogos - Couleur3_60.pdf differ diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/logo_espace2-60.imageset/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/logo_espace2-60.imageset/Contents.json new file mode 100644 index 000000000..17ae90a2c --- /dev/null +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/logo_espace2-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SecondaryLogos - Espace2_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/logo_espace2-60.imageset/SecondaryLogos - Espace2_60.pdf b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/logo_espace2-60.imageset/SecondaryLogos - Espace2_60.pdf new file mode 100644 index 000000000..f173faf00 Binary files /dev/null and b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Espace 2/logo_espace2-60.imageset/SecondaryLogos - Espace2_60.pdf differ diff --git "a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/Contents.json" "b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/Contents.json" index da4a164c9..73c00596a 100755 --- "a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/Contents.json" +++ "b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/Contents.json" @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git "a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/logo_la1ere-60.imageset/Contents.json" "b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/logo_la1ere-60.imageset/Contents.json" new file mode 100644 index 000000000..65794bda8 --- /dev/null +++ "b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/logo_la1ere-60.imageset/Contents.json" @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SecondaryLogo - Premiere_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/logo_la1ere-60.imageset/SecondaryLogo - Premiere_60.pdf" "b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/logo_la1ere-60.imageset/SecondaryLogo - Premiere_60.pdf" new file mode 100644 index 000000000..ad7033434 Binary files /dev/null and "b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/La 1\303\250re/logo_la1ere-60.imageset/SecondaryLogo - Premiere_60.pdf" differ diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/logo_option_musique-60.imageset/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/logo_option_musique-60.imageset/Contents.json new file mode 100644 index 000000000..5ad2e745d --- /dev/null +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/logo_option_musique-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SecondaryLogos - OptionMusique_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/logo_option_musique-60.imageset/SecondaryLogos - OptionMusique_60.pdf b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/logo_option_musique-60.imageset/SecondaryLogos - OptionMusique_60.pdf new file mode 100644 index 000000000..410e1bfda Binary files /dev/null and b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Option Musique/logo_option_musique-60.imageset/SecondaryLogos - OptionMusique_60.pdf differ diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Podcasts Originaux/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Podcasts Originaux/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Podcasts Originaux/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/Radio/Podcasts Originaux/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/logo_rts_info-60.imageset/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/logo_rts_info-60.imageset/Contents.json new file mode 100644 index 000000000..396bc061a --- /dev/null +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/logo_rts_info-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Logos_rtsInfo_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/logo_rts_info-60.imageset/Logos_rtsInfo_60.pdf b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/logo_rts_info-60.imageset/Logos_rtsInfo_60.pdf new file mode 100644 index 000000000..2b309020f Binary files /dev/null and b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS Info/logo_rts_info-60.imageset/Logos_rtsInfo_60.pdf differ diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/logo_rts_un-60.imageset/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/logo_rts_un-60.imageset/Contents.json new file mode 100644 index 000000000..0c40a7648 --- /dev/null +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/logo_rts_un-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Logos_rts1_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/logo_rts_un-60.imageset/Logos_rts1_60.pdf b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/logo_rts_un-60.imageset/Logos_rts1_60.pdf new file mode 100644 index 000000000..5fcd9af4b Binary files /dev/null and b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS1/logo_rts_un-60.imageset/Logos_rts1_60.pdf differ diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/Contents.json +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/logo_rts_deux-60.imageset/Contents.json b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/logo_rts_deux-60.imageset/Contents.json new file mode 100644 index 000000000..1064b9f29 --- /dev/null +++ b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/logo_rts_deux-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Logos_rts2_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/logo_rts_deux-60.imageset/Logos_rts2_60.pdf b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/logo_rts_deux-60.imageset/Logos_rts2_60.pdf new file mode 100644 index 000000000..c734d9de2 Binary files /dev/null and b/Application/Resources/Apps/Play RTS/RTSResources.xcassets/TV/RTS2/logo_rts_deux-60.imageset/Logos_rts2_60.pdf differ diff --git a/Application/Resources/Apps/Play RTS/fr.lproj/Accessibility.strings b/Application/Resources/Apps/Play RTS/fr.lproj/Accessibility.strings index aeb502489..0c063ee86 100644 --- a/Application/Resources/Apps/Play RTS/fr.lproj/Accessibility.strings +++ b/Application/Resources/Apps/Play RTS/fr.lproj/Accessibility.strings @@ -14,11 +14,14 @@ /* Label displaying the number of views on the player */ "%@ views" = "%@ vues"; +/* Title pronounced in home pages on shows A to Z button. */ +"A to Z shows" = "Émissions de A à Z"; + /* Favorite show label when not in favorites, in the player view Favorite show label when not in favorites, in the show view */ "Add to favorites" = "Ajouter aux favoris"; -/* Media watch later creation label */ +/* Media watch later addition label */ "Add to the watch later list" = "Ajouter à \"Plus tard\""; /* Title of the first cell of a media list on homepage. */ @@ -66,9 +69,6 @@ /* Introductory title for information notifications */ "Information" = "Information"; -/* Label on recently played livestreams */ -"Last played" = "Joué en dernier"; - /* Accessibility introductory text for the logged in user */ "Logged in user: %@" = "Utilisateur connecté : %@"; @@ -123,12 +123,6 @@ /* Song cell hint */ "Plays the music." = "Joue la musique."; -/* Title displayed in home page shows section. */ -"Programmes A-Z" = "Émissions de A à Z"; - -/* Title displayed in home page shows section. */ -"Programmes by date" = "Émissions par date"; - /* Mini player label */ "Recently played: %@" = "Joué récemment : %@"; @@ -160,6 +154,9 @@ /* Homepage header action hint */ "Shows all contents." = "Affiche tout le contenu."; +/* Title pronounced in home pages on shows by date button. */ +"Shows by date" = "Émissions par date"; + /* Stop button label */ "Stop" = "Stop"; diff --git a/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings b/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings index dbdb14c3f..788acb747 100644 --- a/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings @@ -16,6 +16,9 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (placeholder is the device name) */ "%@ is idle." = "%@ est en attente."; +/* Short label displayed on a media expiring soon */ +"%@ left" = "Encore %@"; + /* Label displaying the number of listenings on the player */ "%@ listenings" = "%@ écoutes"; @@ -34,7 +37,7 @@ /* More than 3 min option */ "> 30 min" = "> 30 min"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "A to Z" = "De A à Z"; /* Button label to add a media to the watch later list, from the media long-press menu @@ -59,6 +62,9 @@ /* Title of the first cell of a media list on homepage. */ "All content" = "Tout le contenu"; +/* Title label used to present already available videos, usually badged as web first */ +"Already available" = "Déjà disponible"; + /* No comment provided by engineer. */ "Always send" = "Toujours envoyer"; @@ -78,6 +84,7 @@ "Are you sure you want to delete the selected items?" = "Etes-vous sûr de vouloir supprimer les éléments sélectionnés ?"; /* Audios option + Audios tab title Header for audio search results Title displayed at the top of the audio view */ "Audios" = "Audios"; @@ -92,7 +99,7 @@ /* Title label used to present the soon expiring videos */ "Available for a limited time" = "Disponible pour un temps limité"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "By date" = "Par date"; /* Label of the button to close the media long-press menu @@ -165,6 +172,9 @@ /* Short label identifying content which has expired. */ "Expired" = "Expiré"; +/* Show favorite button label */ +"Favorite" = "Favori"; + /* Favorite show label when added ,in the show view Label to present Favorites Title label used to present the radio favorite shows @@ -184,12 +194,17 @@ /* Total free space size, display at the bottom of download list */ "Free space: %@" = "Capacité disponible : %@"; -/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available */ +/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available + Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available */ "Highlights" = "Mises en avant"; -/* Label to present history */ +/* Label to present history + Profile tab title */ "History" = "Historique"; +/* Home tab title */ +"Home" = "Accueil"; + /* Image copyright introductory label */ "Image credit: %@" = "Crédit image : %@"; @@ -205,7 +220,8 @@ /* Period setting option */ "Last week" = "La semaine dernière"; -/* Label for the button for deciding to opt-in for background video playback at a later time */ +/* Label for the button for deciding to opt-in for background video playback at a later time + Watch or listen later button label in media detail view */ "Later" = "Plus tard"; /* Title label used to present the latest videos @@ -218,6 +234,9 @@ /* Explains that a content has expired, will expire or will be available in less than one hour. Displayed in the media player view. */ "less than 1 hour" = "moins d'une heure"; +/* Play button label for audio in media detail view */ +"Listen" = "Écouter"; + /* Suggested invocation phrase to listen to a show Suggested invocation phrase to listen to an audio User activity title when listening to an audio */ @@ -228,6 +247,7 @@ "Live" = "Direct"; /* Label to present the Livestreams view + Livestreams tab title Title displayed at the top of the livestream view */ "Livestreams" = "Directs"; @@ -248,6 +268,7 @@ /* Button label to open the show episode page from the long-press menu Button label to open the show episode page from the preview window + Button to access more episodes from the media detail view Context menu action to open more episodes associated with a media */ "More episodes" = "Autres épisodes"; @@ -349,15 +370,10 @@ /* Title of the button to proceed to the previous onboarding page */ "Previous" = "Précédent"; -/* Title displayed at the top of the profile view */ +/* Profile tab title + Title displayed at the top of the profile view */ "Profile" = "Profil"; -/* Label to present shows A to Z (radio or TV) */ -"Programmes A-Z" = "Émissions de A à Z"; - -/* Label to present programmes by date */ -"Programmes by date" = "Émissions par date"; - /* Settings section header */ "Properties" = "Propriétés"; @@ -424,8 +440,12 @@ /* Title of the reset search settings button */ "Reset" = "Effacer"; +/* Resume playback button label */ +"Resume" = "Reprendre"; + /* Label to present the search view Search placeholder text + Search tab title Title displayed when there is no search criterium entered */ "Search" = "Recherche"; @@ -459,9 +479,16 @@ Shows search setting option list view title Title label used to present radio associated shows Title label used to present the radio shows AZ and radio shows by date access buttons - Title label used to present the TV shows AZ and TV shows by date access buttons */ + Title label used to present the TV shows AZ and TV shows by date access buttons + Shows tab title */ "Shows" = "Émissions"; +/* Label to present shows A to Z (radio or TV) */ +"Shows A to Z" = "Émissions de A à Z"; + +/* Label to present shows (episodes) by date (radio or TV) */ +"Shows by date" = "Émissions par date"; + /* Title of the button to skip updating the application */ "Skip" = "Ignorer"; @@ -505,6 +532,9 @@ Message displayed when song title and artist name have been copied to the pasteboard */ "The content has been copied to the clipboard." = "Le contenu a été copié dans le presse-papier."; +/* Error message returned for invalid data */ +"The data is invalid" = "Les données ne sont pas valides"; + /* Button label to share the entire episode being played. */ "The entire episode" = "L'émission entière"; @@ -546,6 +576,9 @@ /* Message displayed when attempting to play some content not allowed to be played with Google Cast */ "This content is not allowed to be played with Google Cast." = "Ce contenu n'a pas le droit d'être lu par Google Cast."; +/* Related content media list title */ +"This might interest you" = "Cela pourrait vous intéresser"; + /* Period setting option */ "This week" = "Cette semaine"; @@ -585,6 +618,9 @@ /* Header for video and audio search results */ "Videos and audios" = "Vidéos et audios"; +/* Play button label for video in media detail view */ +"Watch" = "Regarder"; + /* Suggested invocation phrase to watch a show Suggested invocation phrase to watch a video User activity title when watching a video */ @@ -593,7 +629,8 @@ /* Label to present the watch later list */ "Watch later" = "Plus tard"; -/* Web first label on media cells */ +/* Web first label on media cells + Web first label on media detail page */ "Web first" = "Exclu"; /* Title displayed at the top of the What's new view */ diff --git a/Application/Resources/Apps/Play SRF/ApplicationConfiguration.json b/Application/Resources/Apps/Play SRF/ApplicationConfiguration.json index 0b60230ea..1503c8200 100755 --- a/Application/Resources/Apps/Play SRF/ApplicationConfiguration.json +++ b/Application/Resources/Apps/Play SRF/ApplicationConfiguration.json @@ -1,7 +1,8 @@ { "businessUnit": "srf", "container": 10, - "comScoreVirtualSite": "srf-player-ios-v", + "siteName": "srf-player-ios-v", + "tvSiteName": "srf-player-tvos-apple", "netMetrixIdentifier": "srgplayer", "voiceOverLanguageCode": "de", "appStoreProductIdentifier": 638194352, @@ -10,6 +11,7 @@ "videoHomeSections": "tvTrending,tvTopicsAccess,tvShowsAccess,tvFavoriteShows,tvEvents,tvTopics", "liveHomeSections": "tvLive,radioLive", "tvTrendingEpisodesOnly": true, + "tvTrendingPrefersHeroStage": true, "tvFeaturedHomeSectionHeaderHidden": true, "audioHomeSections": "radioLatest,radioShowsAccess,radioFavoriteShows,radioMostPopular,radioLatestEpisodes", "radioFeaturedHomeSectionHeaderHidden": true, diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/logo_srf1-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/logo_srf1-60.imageset/Contents.json new file mode 100644 index 000000000..9389fd95d --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/logo_srf1-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_srf1_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/logo_srf1-60.imageset/logosradio_srf1_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/logo_srf1-60.imageset/logosradio_srf1_60.pdf new file mode 100644 index 000000000..926def24d Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 1/logo_srf1-60.imageset/logosradio_srf1_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/logo_srf2-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/logo_srf2-60.imageset/Contents.json new file mode 100644 index 000000000..6bd8221df --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/logo_srf2-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_srf2_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/logo_srf2-60.imageset/logosradio_srf2_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/logo_srf2-60.imageset/logosradio_srf2_60.pdf new file mode 100644 index 000000000..69f9a3937 Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 2/logo_srf2-60.imageset/logosradio_srf2_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/logo_srf3-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/logo_srf3-60.imageset/Contents.json new file mode 100644 index 000000000..a14be2949 --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/logo_srf3-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_srf3_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/logo_srf3-60.imageset/logosradio_srf3_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/logo_srf3-60.imageset/logosradio_srf3_60.pdf new file mode 100644 index 000000000..b19d647f4 Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 3/logo_srf3-60.imageset/logosradio_srf3_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/logo_srf4-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/logo_srf4-60.imageset/Contents.json new file mode 100644 index 000000000..dcb9bd1bf --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/logo_srf4-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_srf4_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/logo_srf4-60.imageset/logosradio_srf4_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/logo_srf4-60.imageset/logosradio_srf4_60.pdf new file mode 100644 index 000000000..24208d92f Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF 4/logo_srf4-60.imageset/logosradio_srf4_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/logo_srf_musikwelle-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/logo_srf_musikwelle-60.imageset/Contents.json new file mode 100644 index 000000000..0e5521cc2 --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/logo_srf_musikwelle-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_MW_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/logo_srf_musikwelle-60.imageset/logosradio_MW_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/logo_srf_musikwelle-60.imageset/logosradio_MW_60.pdf new file mode 100644 index 000000000..5d3b2b57d Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/SRF Musikwelle/logo_srf_musikwelle-60.imageset/logosradio_MW_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/logo_virus-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/logo_virus-60.imageset/Contents.json new file mode 100644 index 000000000..d16daced2 --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/logo_virus-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logosradio_virus_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/logo_virus-60.imageset/logosradio_virus_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/logo_virus-60.imageset/logosradio_virus_60.pdf new file mode 100644 index 000000000..466503744 Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/Radio/Virus/logo_virus-60.imageset/logosradio_virus_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/logo_tv_srf_info-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/logo_tv_srf_info-60.imageset/Contents.json new file mode 100644 index 000000000..c88d90c38 --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/logo_tv_srf_info-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_srfInfo_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/logo_tv_srf_info-60.imageset/logos_srfInfo_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/logo_tv_srf_info-60.imageset/logos_srfInfo_60.pdf new file mode 100644 index 000000000..d03dd42bc Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF Info/logo_tv_srf_info-60.imageset/logos_srfInfo_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/logo_tv_srf1-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/logo_tv_srf1-60.imageset/Contents.json new file mode 100644 index 000000000..6e2e77a8f --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/logo_tv_srf1-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_srf1_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/logo_tv_srf1-60.imageset/logos_srf1_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/logo_tv_srf1-60.imageset/logos_srf1_60.pdf new file mode 100644 index 000000000..10756e375 Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF1/logo_tv_srf1-60.imageset/logos_srf1_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/Contents.json +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/logo_tv_srf2-60.imageset/Contents.json b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/logo_tv_srf2-60.imageset/Contents.json new file mode 100644 index 000000000..e4b3c8dea --- /dev/null +++ b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/logo_tv_srf2-60.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logos_srfzwei_60.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/logo_tv_srf2-60.imageset/logos_srfzwei_60.pdf b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/logo_tv_srf2-60.imageset/logos_srfzwei_60.pdf new file mode 100644 index 000000000..29944e4fc Binary files /dev/null and b/Application/Resources/Apps/Play SRF/SRFResources.xcassets/TV/SRF2/logo_tv_srf2-60.imageset/logos_srfzwei_60.pdf differ diff --git a/Application/Resources/Apps/Play SRF/de.lproj/Accessibility.strings b/Application/Resources/Apps/Play SRF/de.lproj/Accessibility.strings index 4415a55ad..e83ca381b 100755 --- a/Application/Resources/Apps/Play SRF/de.lproj/Accessibility.strings +++ b/Application/Resources/Apps/Play SRF/de.lproj/Accessibility.strings @@ -14,11 +14,14 @@ /* Label displaying the number of views on the player */ "%@ views" = "%@ gesehen"; +/* Title pronounced in home pages on shows A to Z button. */ +"A to Z shows" = "Sendungen A bis Z"; + /* Favorite show label when not in favorites, in the player view Favorite show label when not in favorites, in the show view */ "Add to favorites" = "Zu den Favoriten hinzufügen"; -/* Media watch later creation label */ +/* Media watch later addition label */ "Add to the watch later list" = "Zu Später schauen hinzufügen"; /* Title of the first cell of a media list on homepage. */ @@ -66,9 +69,6 @@ /* Introductory title for information notifications */ "Information" = "Information"; -/* Label on recently played livestreams */ -"Last played" = "Zuletzt abgespielt"; - /* Accessibility introductory text for the logged in user */ "Logged in user: %@" = "Angemeldeter Nutzer: %@"; @@ -79,7 +79,7 @@ "Manages account information" = "Anmeldeinformationen verwalten"; /* A more episode button label */ -"More episodes" = "Weitere Folgen"; +"More episodes" = "Mehr zur Sendung"; /* Text displayed when a user is logged in but no information has been retrieved yet */ "My account" = "Mein Konto"; @@ -123,12 +123,6 @@ /* Song cell hint */ "Plays the music." = "Spielt die Musik ab."; -/* Title displayed in home page shows section. */ -"Programmes A-Z" = "Sendungen A bis Z"; - -/* Title displayed in home page shows section. */ -"Programmes by date" = "Sendungen nach Datum"; - /* Mini player label */ "Recently played: %@" = "Kürzlich abgespielt: %@"; @@ -160,6 +154,9 @@ /* Homepage header action hint */ "Shows all contents." = "Alle Inhalte anzeigen"; +/* Title pronounced in home pages on shows by date button. */ +"Shows by date" = "Sendungen nach Datum"; + /* Stop button label */ "Stop" = "Anhalten"; diff --git a/Application/Resources/Apps/Play SRF/de.lproj/Localizable.strings b/Application/Resources/Apps/Play SRF/de.lproj/Localizable.strings index 76dbdfc99..f6af55a75 100755 --- a/Application/Resources/Apps/Play SRF/de.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play SRF/de.lproj/Localizable.strings @@ -16,6 +16,9 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (placeholder is the device name) */ "%@ is idle." = "%@ wartet."; +/* Short label displayed on a media expiring soon */ +"%@ left" = "Noch %@"; + /* Label displaying the number of listenings on the player */ "%@ listenings" = "%@"; @@ -34,7 +37,7 @@ /* More than 3 min option */ "> 30 min" = "> 30 min"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "A to Z" = "A bis Z"; /* Button label to add a media to the watch later list, from the media long-press menu @@ -59,6 +62,9 @@ /* Title of the first cell of a media list on homepage. */ "All content" = "Alle anzeigen"; +/* Title label used to present already available videos, usually badged as web first */ +"Already available" = "Vorab online verfügbar"; + /* No comment provided by engineer. */ "Always send" = "Immer senden"; @@ -78,6 +84,7 @@ "Are you sure you want to delete the selected items?" = "Sind Sie sicher, dass Sie die ausgewählten Elemente löschen wollen?"; /* Audios option + Audios tab title Header for audio search results Title displayed at the top of the audio view */ "Audios" = "Audios"; @@ -92,7 +99,7 @@ /* Title label used to present the soon expiring videos */ "Available for a limited time" = "Nur noch kurze Zeit online"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "By date" = "Nach Datum"; /* Label of the button to close the media long-press menu @@ -165,6 +172,9 @@ /* Short label identifying content which has expired. */ "Expired" = "Abgelaufen"; +/* Show favorite button label */ +"Favorite" = "Favoriten"; + /* Favorite show label when added ,in the show view Label to present Favorites Title label used to present the radio favorite shows @@ -184,12 +194,17 @@ /* Total free space size, display at the bottom of download list */ "Free space: %@" = "Verfügbarer Speicher: %@"; -/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available */ +/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available + Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available */ "Highlights" = "Highlights"; -/* Label to present history */ +/* Label to present history + Profile tab title */ "History" = "Zuletzt gesehen"; +/* Home tab title */ +"Home" = "Home"; + /* Image copyright introductory label */ "Image credit: %@" = "Bild: %@"; @@ -205,7 +220,8 @@ /* Period setting option */ "Last week" = "Letzte Woche"; -/* Label for the button for deciding to opt-in for background video playback at a later time */ +/* Label for the button for deciding to opt-in for background video playback at a later time + Watch or listen later button label in media detail view */ "Later" = "Später"; /* Title label used to present the latest videos @@ -218,6 +234,9 @@ /* Explains that a content has expired, will expire or will be available in less than one hour. Displayed in the media player view. */ "less than 1 hour" = "< 1 Stunde"; +/* Play button label for audio in media detail view */ +"Listen" = "Abspielen"; + /* Suggested invocation phrase to listen to a show Suggested invocation phrase to listen to an audio User activity title when listening to an audio */ @@ -228,6 +247,7 @@ "Live" = "Live"; /* Label to present the Livestreams view + Livestreams tab title Title displayed at the top of the livestream view */ "Livestreams" = "Live"; @@ -248,8 +268,9 @@ /* Button label to open the show episode page from the long-press menu Button label to open the show episode page from the preview window + Button to access more episodes from the media detail view Context menu action to open more episodes associated with a media */ -"More episodes" = "Weitere Sendungen zeigen"; +"More episodes" = "Mehr zur Sendung"; /* Title of the related content player section */ "More on this subject" = "Mehr zum Thema"; @@ -349,15 +370,10 @@ /* Title of the button to proceed to the previous onboarding page */ "Previous" = "Zurück"; -/* Title displayed at the top of the profile view */ +/* Profile tab title + Title displayed at the top of the profile view */ "Profile" = "Profil"; -/* Label to present shows A to Z (radio or TV) */ -"Programmes A-Z" = "Sendungen A bis Z"; - -/* Label to present programmes by date */ -"Programmes by date" = "Sendungen nach Datum"; - /* Settings section header */ "Properties" = "Eigenschaften"; @@ -424,8 +440,12 @@ /* Title of the reset search settings button */ "Reset" = "Zurücksetzen"; +/* Resume playback button label */ +"Resume" = "Weiter abspielen"; + /* Label to present the search view Search placeholder text + Search tab title Title displayed when there is no search criterium entered */ "Search" = "Suche"; @@ -459,9 +479,16 @@ Shows search setting option list view title Title label used to present radio associated shows Title label used to present the radio shows AZ and radio shows by date access buttons - Title label used to present the TV shows AZ and TV shows by date access buttons */ + Title label used to present the TV shows AZ and TV shows by date access buttons + Shows tab title */ "Shows" = "Sendungen"; +/* Label to present shows A to Z (radio or TV) */ +"Shows A to Z" = "Sendungen A bis Z"; + +/* Label to present shows (episodes) by date (radio or TV) */ +"Shows by date" = "Sendungen nach Datum"; + /* Title of the button to skip updating the application */ "Skip" = "Abbrechen"; @@ -505,6 +532,9 @@ Message displayed when song title and artist name have been copied to the pasteboard */ "The content has been copied to the clipboard." = "Kopiert."; +/* Error message returned for invalid data */ +"The data is invalid" = "Fehlerhafte Daten: Aktion konnte nicht ausgeführt werden"; + /* Button label to share the entire episode being played. */ "The entire episode" = "Ganze Sendung"; @@ -546,6 +576,9 @@ /* Message displayed when attempting to play some content not allowed to be played with Google Cast */ "This content is not allowed to be played with Google Cast." = "Aus rechtlichen Gründen kann dieser Inhalt nicht mit Google Cast abgespielt werden."; +/* Related content media list title */ +"This might interest you" = "Das könnte Sie auch interessieren"; + /* Period setting option */ "This week" = "Diese Woche"; @@ -560,7 +593,7 @@ Title label used to present TV topics Title label used to present TV topics while loading. It appears if no network connection is available and no cache is available Topics search setting option list view title */ -"Topics" = "Themen"; +"Topics" = "Kategorien"; /* Total space size, display at the bottom of download list */ "Total space used: %@" = "Benötigter Speicher: %@"; @@ -585,6 +618,9 @@ /* Header for video and audio search results */ "Videos and audios" = "Video und Audio"; +/* Play button label for video in media detail view */ +"Watch" = "Abspielen"; + /* Suggested invocation phrase to watch a show Suggested invocation phrase to watch a video User activity title when watching a video */ @@ -593,8 +629,9 @@ /* Label to present the watch later list */ "Watch later" = "Später schauen"; -/* Web first label on media cells */ -"Web first" = "vorab"; +/* Web first label on media cells + Web first label on media detail page */ +"Web first" = "Vorab"; /* Title displayed at the top of the What's new view */ "What's new" = "Neue Funktionen"; diff --git a/Application/Resources/Apps/Play SWI/ApplicationConfiguration.json b/Application/Resources/Apps/Play SWI/ApplicationConfiguration.json index 0fcf1e7ee..fcef7efe3 100755 --- a/Application/Resources/Apps/Play SWI/ApplicationConfiguration.json +++ b/Application/Resources/Apps/Play SWI/ApplicationConfiguration.json @@ -1,7 +1,8 @@ { "businessUnit": "swi", "container": 10, - "comScoreVirtualSite": "swi-player-ios-v", + "siteName": "swi-player-ios-v", + "tvSiteName": "swi-player-tvos-apple", "netMetrixIdentifier": "srgplayer", "voiceOverLanguageCode": "en", "appStoreProductIdentifier": 920785201, diff --git a/Application/Resources/Apps/Play SWI/en.lproj/Accessibility.strings b/Application/Resources/Apps/Play SWI/en.lproj/Accessibility.strings index d10f0ca3f..6eba6d2f4 100755 --- a/Application/Resources/Apps/Play SWI/en.lproj/Accessibility.strings +++ b/Application/Resources/Apps/Play SWI/en.lproj/Accessibility.strings @@ -14,11 +14,14 @@ /* Label displaying the number of views on the player */ "%@ views" = "%@ views"; +/* Title pronounced in home pages on shows A to Z button. */ +"A to Z shows" = "A to Z topics"; + /* Favorite show label when not in favorites, in the player view Favorite show label when not in favorites, in the show view */ "Add to favorites" = "Add to favorites"; -/* Media watch later creation label */ +/* Media watch later addition label */ "Add to the watch later list" = "Add to the watch later list"; /* Title of the first cell of a media list on homepage. */ @@ -66,9 +69,6 @@ /* Introductory title for information notifications */ "Information" = "Information"; -/* Label on recently played livestreams */ -"Last played" = "Last played"; - /* Accessibility introductory text for the logged in user */ "Logged in user: %@" = "Logged in user: %@"; @@ -123,12 +123,6 @@ /* Song cell hint */ "Plays the music." = "Plays the music."; -/* Title displayed in home page shows section. */ -"Programmes A-Z" = "Programmes A-Z"; - -/* Title displayed in home page shows section. */ -"Programmes by date" = "Programmes by date"; - /* Mini player label */ "Recently played: %@" = "Recently played: %@"; @@ -160,6 +154,9 @@ /* Homepage header action hint */ "Shows all contents." = "Shows all contents."; +/* Title pronounced in home pages on shows by date button. */ +"Shows by date" = "Topics by date"; + /* Stop button label */ "Stop" = "Stop"; diff --git a/Application/Resources/Apps/Play SWI/en.lproj/Localizable.strings b/Application/Resources/Apps/Play SWI/en.lproj/Localizable.strings index a5f1a5656..0f740212e 100755 --- a/Application/Resources/Apps/Play SWI/en.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play SWI/en.lproj/Localizable.strings @@ -16,6 +16,9 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (placeholder is the device name) */ "%@ is idle." = "%@ is idle."; +/* Short label displayed on a media expiring soon */ +"%@ left" = "%@ left"; + /* Label displaying the number of listenings on the player */ "%@ listenings" = "%@ listenings"; @@ -34,7 +37,7 @@ /* More than 3 min option */ "> 30 min" = "> 30 min"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "A to Z" = "A to Z"; /* Button label to add a media to the watch later list, from the media long-press menu @@ -59,6 +62,9 @@ /* Title of the first cell of a media list on homepage. */ "All content" = "All content"; +/* Title label used to present already available videos, usually badged as web first */ +"Already available" = "Already available"; + /* No comment provided by engineer. */ "Always send" = "Always send"; @@ -78,6 +84,7 @@ "Are you sure you want to delete the selected items?" = "Are you sure you want to delete the selected items?"; /* Audios option + Audios tab title Header for audio search results Title displayed at the top of the audio view */ "Audios" = "Audios"; @@ -92,7 +99,7 @@ /* Title label used to present the soon expiring videos */ "Available for a limited time" = "Available for a limited time"; -/* Short title displayed in home page shows section. */ +/* Short title displayed in home pages on a button. */ "By date" = "By date"; /* Label of the button to close the media long-press menu @@ -165,6 +172,9 @@ /* Short label identifying content which has expired. */ "Expired" = "Expired"; +/* Show favorite button label */ +"Favorite" = "Favorite"; + /* Favorite show label when added ,in the show view Label to present Favorites Title label used to present the radio favorite shows @@ -184,12 +194,17 @@ /* Total free space size, display at the bottom of download list */ "Free space: %@" = "Free space: %@"; -/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available */ +/* Title label used to present TV event modules while loading. It appears if no network connection is available and no cache is available + Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available */ "Highlights" = "Highlights"; -/* Label to present history */ +/* Label to present history + Profile tab title */ "History" = "History"; +/* Home tab title */ +"Home" = "Home"; + /* Image copyright introductory label */ "Image credit: %@" = "Image credit: %@"; @@ -205,7 +220,8 @@ /* Period setting option */ "Last week" = "Last week"; -/* Label for the button for deciding to opt-in for background video playback at a later time */ +/* Label for the button for deciding to opt-in for background video playback at a later time + Watch or listen later button label in media detail view */ "Later" = "Later"; /* Title label used to present the latest videos @@ -218,6 +234,9 @@ /* Explains that a content has expired, will expire or will be available in less than one hour. Displayed in the media player view. */ "less than 1 hour" = "less than 1 hour"; +/* Play button label for audio in media detail view */ +"Listen" = "Listen"; + /* Suggested invocation phrase to listen to a show Suggested invocation phrase to listen to an audio User activity title when listening to an audio */ @@ -228,6 +247,7 @@ "Live" = "Live"; /* Label to present the Livestreams view + Livestreams tab title Title displayed at the top of the livestream view */ "Livestreams" = "Live"; @@ -248,6 +268,7 @@ /* Button label to open the show episode page from the long-press menu Button label to open the show episode page from the preview window + Button to access more episodes from the media detail view Context menu action to open more episodes associated with a media */ "More episodes" = "More episodes"; @@ -349,15 +370,10 @@ /* Title of the button to proceed to the previous onboarding page */ "Previous" = "Previous"; -/* Title displayed at the top of the profile view */ +/* Profile tab title + Title displayed at the top of the profile view */ "Profile" = "Profile"; -/* Label to present shows A to Z (radio or TV) */ -"Programmes A-Z" = "Programmes A-Z"; - -/* Label to present programmes by date */ -"Programmes by date" = "Programmes by date"; - /* Settings section header */ "Properties" = "Properties"; @@ -424,8 +440,12 @@ /* Title of the reset search settings button */ "Reset" = "Reset"; +/* Resume playback button label */ +"Resume" = "Resume"; + /* Label to present the search view Search placeholder text + Search tab title Title displayed when there is no search criterium entered */ "Search" = "Search"; @@ -459,8 +479,15 @@ Shows search setting option list view title Title label used to present radio associated shows Title label used to present the radio shows AZ and radio shows by date access buttons - Title label used to present the TV shows AZ and TV shows by date access buttons */ -"Shows" = "Shows"; + Title label used to present the TV shows AZ and TV shows by date access buttons + Shows tab title */ +"Shows" = "Topics"; + +/* Label to present shows A to Z (radio or TV) */ +"Shows A to Z" = "Topics A to Z"; + +/* Label to present shows (episodes) by date (radio or TV) */ +"Shows by date" = "Topics by date"; /* Title of the button to skip updating the application */ "Skip" = "Skip"; @@ -505,6 +532,9 @@ Message displayed when song title and artist name have been copied to the pasteboard */ "The content has been copied to the clipboard." = "The content has been copied to the clipboard."; +/* Error message returned for invalid data */ +"The data is invalid" = "The data is invalid"; + /* Button label to share the entire episode being played. */ "The entire episode" = "The entire video"; @@ -546,6 +576,9 @@ /* Message displayed when attempting to play some content not allowed to be played with Google Cast */ "This content is not allowed to be played with Google Cast." = "This content is not allowed to be played with Google Cast."; +/* Related content media list title */ +"This might interest you" = "This might interest you"; + /* Period setting option */ "This week" = "This week"; @@ -585,6 +618,9 @@ /* Header for video and audio search results */ "Videos and audios" = "Videos and audios"; +/* Play button label for video in media detail view */ +"Watch" = "Watch"; + /* Suggested invocation phrase to watch a show Suggested invocation phrase to watch a video User activity title when watching a video */ @@ -593,7 +629,8 @@ /* Label to present the watch later list */ "Watch later" = "Watch later"; -/* Web first label on media cells */ +/* Web first label on media cells + Web first label on media detail page */ "Web first" = "Web first"; /* Title displayed at the top of the What's new view */ diff --git a/Application/Resources/Images/CommonImages.xcassets/Backgrounds/watch_later-90.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Backgrounds/watch_later-90.imageset/Contents.json index 87b94b4fb..05562c227 100644 --- a/Application/Resources/Images/CommonImages.xcassets/Backgrounds/watch_later-90.imageset/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Backgrounds/watch_later-90.imageset/Contents.json @@ -1,15 +1,15 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "watchitlater_90.pdf" + "filename" : "watchitlater_90.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Icons/Contents.json index da4a164c9..73c00596a 100755 --- a/Application/Resources/Images/CommonImages.xcassets/Icons/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Icons/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/audios-22.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Icons/audios-22.imageset/Contents.json new file mode 100644 index 000000000..112ab95f7 --- /dev/null +++ b/Application/Resources/Images/CommonImages.xcassets/Icons/audios-22.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "audios.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/audios-22.imageset/audios.pdf b/Application/Resources/Images/CommonImages.xcassets/Icons/audios-22.imageset/audios.pdf new file mode 100644 index 000000000..47d4226be Binary files /dev/null and b/Application/Resources/Images/CommonImages.xcassets/Icons/audios-22.imageset/audios.pdf differ diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/Contents.json index 102298ae3..d99bd3915 100644 --- a/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/Contents.json @@ -3,6 +3,10 @@ { "filename" : "episodes-22.pdf", "idiom" : "universal" + }, + { + "filename" : "MoreShow_38.pdf", + "idiom" : "tv" } ], "info" : { diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/MoreShow_38.pdf b/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/MoreShow_38.pdf new file mode 100644 index 000000000..6e977f74f Binary files /dev/null and b/Application/Resources/Images/CommonImages.xcassets/Icons/episodes-22.imageset/MoreShow_38.pdf differ diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Contents.json index 2eeb9a495..32e4e014c 100644 --- a/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Contents.json @@ -1,15 +1,19 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "favorite_22.pdf" + "filename" : "favorite_22.pdf", + "idiom" : "universal" + }, + { + "filename" : "Favorite_38.pdf", + "idiom" : "tv" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Favorite_38.pdf b/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Favorite_38.pdf new file mode 100644 index 000000000..ffce24bc5 Binary files /dev/null and b/Application/Resources/Images/CommonImages.xcassets/Icons/favorite-22.imageset/Favorite_38.pdf differ diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/show_favorite-22.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Icons/show_favorite-22.imageset/Contents.json index 2eeb9a495..99ad8544d 100644 --- a/Application/Resources/Images/CommonImages.xcassets/Icons/show_favorite-22.imageset/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Icons/show_favorite-22.imageset/Contents.json @@ -1,15 +1,15 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "favorite_22.pdf" + "filename" : "favorite_22.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/Contents.json index 362d9ae96..9834bcf64 100644 --- a/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/Contents.json @@ -1,15 +1,19 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "watchitlateroff_22.pdf" + "filename" : "watchitlateroff_22.pdf", + "idiom" : "universal" + }, + { + "filename" : "WatchLater_38.pdf", + "idiom" : "tv" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/WatchLater_38.pdf b/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/WatchLater_38.pdf new file mode 100644 index 000000000..60fcffbd5 Binary files /dev/null and b/Application/Resources/Images/CommonImages.xcassets/Icons/watch_later-22.imageset/WatchLater_38.pdf differ diff --git a/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Contents.json b/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Contents.json index 4c760f04c..e06a06c42 100755 --- a/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Contents.json +++ b/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Contents.json @@ -1,15 +1,19 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "play_50.pdf" + "filename" : "play_50.pdf", + "idiom" : "universal" + }, + { + "filename" : "Play_38.pdf", + "idiom" : "tv" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Play_38.pdf b/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Play_38.pdf new file mode 100644 index 000000000..fd47ad2d7 Binary files /dev/null and b/Application/Resources/Images/CommonImages.xcassets/Player/play-50.imageset/Play_38.pdf differ diff --git a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt index c90b5fee1..360dc7d84 100755 --- a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt +++ b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt @@ -91,322 +91,322 @@ version: 1.8.1 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: Firebase, nameSpecified: body: … -version: 6.34.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseABTesting, nameSpecified: body: … -version: 4.2.0 +version: 7.0.0 name: FirebaseAnalytics, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: FirebaseAnalytics, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: FirebaseAnalytics, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: FirebaseAnalytics, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: FirebaseAnalytics, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCore, nameSpecified: body: … -version: 6.10.4 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseCoreDiagnostics, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseInstallations, nameSpecified: body: … -version: 1.7.0 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FirebaseRemoteConfig, nameSpecified: body: … -version: 4.9.1 +version: 7.0.0 name: FSCalendar, nameSpecified: body: Copyright (c) 2013-2… @@ -450,148 +450,148 @@ version: 4.5.1 name: GoogleAppMeasurement, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: GoogleAppMeasurement, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: GoogleAppMeasurement, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: GoogleAppMeasurement, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: GoogleAppMeasurement, nameSpecified: body: Copyright 2020 Googl… -version: 6.9.0 +version: 7.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleDataTransport, nameSpecified: body: … -version: 7.5.1 +version: 8.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GoogleUtilities, nameSpecified: body: … -version: 6.7.2 +version: 7.0.0 name: GTMSessionFetcher, nameSpecified: body: … -version: 1.4.0 +version: 1.5.0 name: GTMSessionFetcher, nameSpecified: body: … -version: 1.4.0 +version: 1.5.0 name: GTMSessionFetcher, nameSpecified: body: … -version: 1.4.0 +version: 1.5.0 name: GTMSessionFetcher, nameSpecified: body: … -version: 1.4.0 +version: 1.5.0 name: GTMSessionFetcher, nameSpecified: body: … -version: 1.4.0 +version: 1.5.0 name: InAppSettingsKit, nameSpecified: body: Copyright (c) 2009-2… @@ -616,27 +616,27 @@ version: 3.1.4 name: MaterialComponents, nameSpecified: body: … -version: 118.0.0 +version: 118.2.0 name: MaterialComponents, nameSpecified: body: … -version: 118.0.0 +version: 118.2.0 name: MaterialComponents, nameSpecified: body: … -version: 118.0.0 +version: 118.2.0 name: MaterialComponents, nameSpecified: body: … -version: 118.0.0 +version: 118.2.0 name: MaterialComponents, nameSpecified: body: … -version: 118.0.0 +version: 118.2.0 name: MDFInternationalization, nameSpecified: body: @@ -710,43 +710,43 @@ version: 1.6.11 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: nanopb, nameSpecified: body: Copyright (c) 2011 P… -version: 1.30906.0 +version: 2.30906.0 name: PromisesObjC, nameSpecified: body: @@ -822,11 +822,9 @@ name: Aiolos, nameSpecified: Aiolos, owner: IdeasOnCanvas, version: name: ComScore-xcframework-apple, nameSpecified: ComScore, owner: SRGSSR, version: 6.6.0 -name: FetchImage, nameSpecified: FetchImage, owner: kean, version: 0.2.1 - name: FXReachability, nameSpecified: FXReachability, owner: SRGSSR, version: 1.3.2-srg5 -name: ios-library, nameSpecified: Airship, owner: urbanairship, version: 14.1.2 +name: ios-library, nameSpecified: Airship, owner: urbanairship, version: 14.1.3 name: libextobjc, nameSpecified: libextobjc, owner: SRGSSR, version: 0.6.0-srg3 @@ -834,27 +832,25 @@ name: MAKVONotificationCenter, nameSpecified: MAKVONotificationCenter, owner: SR name: Mantle, nameSpecified: Mantle, owner: Mantle, version: 2.1.6 -name: Nuke, nameSpecified: Nuke, owner: kean, version: 9.1.2 - name: paper-onboarding, nameSpecified: PaperOnboarding, owner: Ramotion, version: 6.1.5 -name: srganalytics-apple, nameSpecified: SRGAnalytics, owner: SRGSSR, version: 5.0.0 +name: srganalytics-apple, nameSpecified: SRGAnalytics, owner: SRGSSR, version: 6.1.0 -name: srgappearance-apple, nameSpecified: SRGAppearance, owner: SRGSSR, version: 3.0.0 +name: srgappearance-apple, nameSpecified: SRGAppearance, owner: SRGSSR, version: 3.0.1 -name: srgcontentprotection-apple, nameSpecified: SRGContentProtection, owner: SRGSSR, version: 3.0.0 +name: srgcontentprotection-apple, nameSpecified: SRGContentProtection, owner: SRGSSR, version: 3.0.1 -name: srgdataprovider-apple, nameSpecified: SRGDataProvider, owner: SRGSSR, version: 9.0.0 +name: srgdataprovider-apple, nameSpecified: SRGDataProvider, owner: SRGSSR, version: 9.1.0 name: srgdiagnostics-apple, nameSpecified: SRGDiagnostics, owner: SRGSSR, version: 3.0.0 name: srgidentity-apple, nameSpecified: SRGIdentity, owner: SRGSSR, version: 3.0.0 -name: srgletterbox-apple, nameSpecified: SRGLetterbox, owner: SRGSSR, version: 6.0.0 +name: srgletterbox-apple, nameSpecified: SRGLetterbox, owner: SRGSSR, version: 6.1.1 name: srglogger-apple, nameSpecified: SRGLogger, owner: SRGSSR, version: 3.0.0 -name: srgmediaplayer-apple, nameSpecified: SRGMediaPlayer, owner: SRGSSR, version: 6.0.0 +name: srgmediaplayer-apple, nameSpecified: SRGMediaPlayer, owner: SRGSSR, version: 6.1.0 name: srgnetwork-apple, nameSpecified: SRGNetwork, owner: SRGSSR, version: 3.0.0 diff --git a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist index fe373e6ee..1d5dc994a 100755 --- a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist +++ b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist @@ -50,19 +50,11 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/FetchImage - Title - FetchImage (0.2.1) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/Firebase Title - Firebase (6.34.0) + Firebase (7.0.0) Type PSChildPaneSpecifier @@ -70,7 +62,7 @@ File com.mono0926.LicensePlist/FirebaseABTesting Title - FirebaseABTesting (4.2.0) + FirebaseABTesting (7.0.0) Type PSChildPaneSpecifier @@ -78,7 +70,7 @@ File com.mono0926.LicensePlist/FirebaseAnalytics Title - FirebaseAnalytics (6.9.0) + FirebaseAnalytics (7.0.0) Type PSChildPaneSpecifier @@ -86,7 +78,7 @@ File com.mono0926.LicensePlist/FirebaseCore Title - FirebaseCore (6.10.4) + FirebaseCore (7.0.0) Type PSChildPaneSpecifier @@ -94,7 +86,7 @@ File com.mono0926.LicensePlist/FirebaseCoreDiagnostics Title - FirebaseCoreDiagnostics (1.7.0) + FirebaseCoreDiagnostics (7.0.0) Type PSChildPaneSpecifier @@ -102,7 +94,7 @@ File com.mono0926.LicensePlist/FirebaseInstallations Title - FirebaseInstallations (1.7.0) + FirebaseInstallations (7.0.0) Type PSChildPaneSpecifier @@ -110,7 +102,7 @@ File com.mono0926.LicensePlist/FirebaseRemoteConfig Title - FirebaseRemoteConfig (4.9.1) + FirebaseRemoteConfig (7.0.0) Type PSChildPaneSpecifier @@ -142,7 +134,7 @@ File com.mono0926.LicensePlist/GoogleAppMeasurement Title - GoogleAppMeasurement (6.9.0) + GoogleAppMeasurement (7.0.0) Type PSChildPaneSpecifier @@ -150,7 +142,7 @@ File com.mono0926.LicensePlist/GoogleDataTransport Title - GoogleDataTransport (7.5.1) + GoogleDataTransport (8.0.0) Type PSChildPaneSpecifier @@ -158,7 +150,7 @@ File com.mono0926.LicensePlist/GoogleUtilities Title - GoogleUtilities (6.7.2) + GoogleUtilities (7.0.0) Type PSChildPaneSpecifier @@ -166,7 +158,7 @@ File com.mono0926.LicensePlist/GTMSessionFetcher Title - GTMSessionFetcher (1.4.0) + GTMSessionFetcher (1.5.0) Type PSChildPaneSpecifier @@ -182,7 +174,7 @@ File com.mono0926.LicensePlist/ios-library Title - Airship (14.1.2) + Airship (14.1.3) Type PSChildPaneSpecifier @@ -214,7 +206,7 @@ File com.mono0926.LicensePlist/MaterialComponents Title - MaterialComponents (118.0.0) + MaterialComponents (118.2.0) Type PSChildPaneSpecifier @@ -246,15 +238,7 @@ File com.mono0926.LicensePlist/nanopb Title - nanopb (1.30906.0) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/Nuke - Title - Nuke (9.1.2) + nanopb (2.30906.0) Type PSChildPaneSpecifier @@ -286,7 +270,7 @@ File com.mono0926.LicensePlist/srganalytics-apple Title - SRGAnalytics (5.0.0) + SRGAnalytics (6.1.0) Type PSChildPaneSpecifier @@ -294,7 +278,7 @@ File com.mono0926.LicensePlist/srgappearance-apple Title - SRGAppearance (3.0.0) + SRGAppearance (3.0.1) Type PSChildPaneSpecifier @@ -302,7 +286,7 @@ File com.mono0926.LicensePlist/srgcontentprotection-apple Title - SRGContentProtection (3.0.0) + SRGContentProtection (3.0.1) Type PSChildPaneSpecifier @@ -310,7 +294,7 @@ File com.mono0926.LicensePlist/srgdataprovider-apple Title - SRGDataProvider (9.0.0) + SRGDataProvider (9.1.0) Type PSChildPaneSpecifier @@ -334,7 +318,7 @@ File com.mono0926.LicensePlist/srgletterbox-apple Title - SRGLetterbox (6.0.0) + SRGLetterbox (6.1.1) Type PSChildPaneSpecifier @@ -350,7 +334,7 @@ File com.mono0926.LicensePlist/srgmediaplayer-apple Title - SRGMediaPlayer (6.0.0) + SRGMediaPlayer (6.1.0) Type PSChildPaneSpecifier diff --git a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist/FetchImage.plist b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist/FetchImage.plist deleted file mode 100644 index 2b01e190b..000000000 --- a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist/FetchImage.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2020 Alexander Grebenyuk - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - Type - PSGroupSpecifier - - - - diff --git a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist/Nuke.plist b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist/Nuke.plist deleted file mode 100644 index a33e792a6..000000000 --- a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist/Nuke.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) 2015-2020 Alexander Grebenyuk - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - Type - PSGroupSpecifier - - - - diff --git a/Application/Sources/Application/PlayAppDelegate.m b/Application/Sources/Application/PlayAppDelegate.m index 6bb2a7327..40c42821e 100755 --- a/Application/Sources/Application/PlayAppDelegate.m +++ b/Application/Sources/Application/PlayAppDelegate.m @@ -658,7 +658,7 @@ - (void)setupAnalytics SRGAnalyticsConfiguration *configuration = [[SRGAnalyticsConfiguration alloc] initWithBusinessUnitIdentifier:applicationConfiguration.analyticsBusinessUnitIdentifier container:applicationConfiguration.analyticsContainer - comScoreVirtualSite:applicationConfiguration.comScoreVirtualSite + siteName:applicationConfiguration.siteName netMetrixIdentifier:applicationConfiguration.netMetrixIdentifier]; [SRGAnalyticsTracker.sharedTracker startWithConfiguration:configuration identityService:SRGIdentityService.currentIdentityService]; diff --git a/Application/Sources/Configuration/ApplicationConfiguration.h b/Application/Sources/Configuration/ApplicationConfiguration.h index 26d5a1f07..347274a2d 100755 --- a/Application/Sources/Configuration/ApplicationConfiguration.h +++ b/Application/Sources/Configuration/ApplicationConfiguration.h @@ -31,7 +31,8 @@ OBJC_EXPORT NSString * const ApplicationConfigurationDidChangeNotification; @property (nonatomic, readonly, copy) SRGAnalyticsBusinessUnitIdentifier analyticsBusinessUnitIdentifier; @property (nonatomic, readonly) NSInteger analyticsContainer; -@property (nonatomic, readonly, copy) NSString *comScoreVirtualSite; +@property (nonatomic, readonly, copy) NSString *siteName; +@property (nonatomic, readonly, copy) NSString *tvSiteName; @property (nonatomic, readonly, copy) NSString *netMetrixIdentifier; // Might be nil for "exotic" languages like Rumantsch @@ -65,6 +66,7 @@ OBJC_EXPORT NSString * const ApplicationConfigurationDidChangeNotification; @property (nonatomic, readonly) BOOL tvTrendingEpisodesOnly; @property (nonatomic, readonly, nullable) NSNumber *tvTrendingEditorialLimit; +@property (nonatomic, readonly) BOOL tvTrendingPrefersHeroStage; @property (nonatomic, readonly, getter=isTvFeaturedHomeSectionHeaderHidden) BOOL tvFeaturedHomeSectionHeaderHidden; diff --git a/Application/Sources/Configuration/ApplicationConfiguration.m b/Application/Sources/Configuration/ApplicationConfiguration.m index cf1bf4075..3fc28962f 100755 --- a/Application/Sources/Configuration/ApplicationConfiguration.m +++ b/Application/Sources/Configuration/ApplicationConfiguration.m @@ -74,7 +74,8 @@ @interface ApplicationConfiguration () @property (nonatomic, copy) SRGAnalyticsBusinessUnitIdentifier analyticsBusinessUnitIdentifier; @property (nonatomic) NSInteger analyticsContainer; -@property (nonatomic, copy) NSString *comScoreVirtualSite; +@property (nonatomic, copy) NSString *siteName; +@property (nonatomic, copy) NSString *tvSiteName; @property (nonatomic, copy) NSString *netMetrixIdentifier; @property (nonatomic, copy) NSString *voiceOverLanguageCode; @@ -110,6 +111,7 @@ @interface ApplicationConfiguration () @property (nonatomic) BOOL tvTrendingEpisodesOnly; @property (nonatomic) NSNumber *tvTrendingEditorialLimit; +@property (nonatomic) BOOL tvTrendingPrefersHeroStage; @property (nonatomic, getter=isTvFeaturedHomeSectionHeaderHidden) BOOL tvFeaturedHomeSectionHeaderHidden; @@ -224,8 +226,13 @@ - (BOOL)synchronizeWithFirebaseConfiguration:(FirebaseConfiguration *)firebaseCo return NO; } - NSString *comScoreVirtualSite = [firebaseConfiguration stringForKey:@"comScoreVirtualSite"]; - if (! comScoreVirtualSite) { + NSString *siteName = [firebaseConfiguration stringForKey:@"siteName"]; + if (! siteName) { + return NO; + } + + NSString *tvSiteName = [firebaseConfiguration stringForKey:@"tvSiteName"]; + if (! tvSiteName) { return NO; } @@ -261,7 +268,8 @@ - (BOOL)synchronizeWithFirebaseConfiguration:(FirebaseConfiguration *)firebaseCo self.analyticsBusinessUnitIdentifier = analyticsBusinessUnitIdentifier; self.analyticsContainer = analyticsContainer.integerValue; self.vendor = vendor; - self.comScoreVirtualSite = comScoreVirtualSite; + self.siteName = siteName; + self.tvSiteName = tvSiteName; self.netMetrixIdentifier = netMetrixIdentifier; self.playURL = playURL; @@ -324,6 +332,8 @@ - (BOOL)synchronizeWithFirebaseConfiguration:(FirebaseConfiguration *)firebaseCo NSNumber *tvTrendingEditorialLimit = [firebaseConfiguration numberForKey:@"tvTrendingEditorialLimit"]; self.tvTrendingEditorialLimit = tvTrendingEditorialLimit ? @(MAX(tvTrendingEditorialLimit.integerValue, 0)) : nil; + self.tvTrendingPrefersHeroStage = [firebaseConfiguration boolForKey:@"tvTrendingPrefersHeroStage"]; + self.tvFeaturedHomeSectionHeaderHidden = [firebaseConfiguration boolForKey:@"tvFeaturedHomeSectionHeaderHidden"]; self.topicSections = [firebaseConfiguration topicSectionsForKey:@"topicSections"]; diff --git a/Application/Sources/Configuration/ApplicationSection.m b/Application/Sources/Configuration/ApplicationSection.m index 002e3e8b7..c02ad0317 100644 --- a/Application/Sources/Configuration/ApplicationSection.m +++ b/Application/Sources/Configuration/ApplicationSection.m @@ -16,10 +16,10 @@ @(ApplicationSectionHistory) : NSLocalizedString(@"History", @"Label to present history"), @(ApplicationSectionNotifications) : NSLocalizedString(@"Notifications", @"Label to present the help page"), @(ApplicationSectionSearch) : NSLocalizedString(@"Search", @"Label to present the search view"), - @(ApplicationSectionShowByDate) : NSLocalizedString(@"Programmes by date", @"Label to present programmes by date"), + @(ApplicationSectionShowByDate) : NSLocalizedString(@"Shows by date", @"Label to present shows (episodes) by date (radio or TV)"), @(ApplicationSectionOverview) : NSLocalizedString(@"Overview", @"Label to present the main Videos / Audios views"), @(ApplicationSectionLive) : NSLocalizedString(@"Livestreams", @"Label to present the Livestreams view"), - @(ApplicationSectionShowAZ) : NSLocalizedString(@"Programmes A-Z", @"Label to present shows A to Z (radio or TV)"), + @(ApplicationSectionShowAZ) : NSLocalizedString(@"Shows A to Z", @"Label to present shows A to Z (radio or TV)"), @(ApplicationSectionWatchLater) : NSLocalizedString(@"Watch later", @"Label to present the watch later list") }; }); return s_names[@(applicationSection)]; diff --git a/Application/Sources/Configuration/FirebaseConfiguration.m b/Application/Sources/Configuration/FirebaseConfiguration.m index e7249685f..08fae10ad 100644 --- a/Application/Sources/Configuration/FirebaseConfiguration.m +++ b/Application/Sources/Configuration/FirebaseConfiguration.m @@ -22,6 +22,7 @@ static HomeSection HomeSectionWithString(NSString *string) @"tvTopics" : @(HomeSectionTVTopics), @"tvTopicsAccess" : @(HomeSectionTVTopicsAccess), @"tvLatest" : @(HomeSectionTVLatest), + @"tvWebFirst": @(HomeSectionTVWebFirst), @"tvMostPopular" : @(HomeSectionTVMostPopular), @"tvSoonExpiring" : @(HomeSectionTVSoonExpiring), @"tvScheduledLivestreams" : @(HomeSectionTVScheduledLivestreams), diff --git a/Application/Sources/Configuration/FirebaseStorageFix.m b/Application/Sources/Configuration/FirebaseStorageFix.m deleted file mode 100644 index d858d5742..000000000 --- a/Application/Sources/Configuration/FirebaseStorageFix.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -@import Foundation; - -#if TARGET_OS_TV - -#import - -@interface NSObject (FirebaseRemoteConfigStorageFix) - -+ (NSString *)swizzled_remoteConfigPathForDatabase; - -@end - -@implementation NSObject (FirebaseRemoteConfigStorageFix) - -+ (void)load -{ - // Fix RCN000019 Firebase Remote Configuration issue on an Apple TV device, for which only few directories - // can be written (https://developer.apple.com/library/archive/documentation/General/Conceptual/AppleTV_PG/). - Class clazz = NSClassFromString(@"RCNConfigDBManager"); - NSAssert(clazz != NULL, @"The Firebase SDK implementation changed"); - - Method originalMethod = class_getClassMethod(clazz, NSSelectorFromString(@"remoteConfigPathForDatabase")); - NSAssert(originalMethod != NULL, @"The Firebase SDK implementation changed"); - method_exchangeImplementations(originalMethod, - class_getClassMethod(clazz, @selector(swizzled_remoteConfigPathForDatabase))); -} - -+ (NSString *)swizzled_remoteConfigPathForDatabase -{ - NSString *cachesDirectoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; - return [cachesDirectoryPath stringByAppendingPathComponent:@"Google/RemoteConfig/RemoteConfig.sqlite3"]; -} - -@end - -#endif diff --git a/Application/Sources/Configuration/HomeSection.h b/Application/Sources/Configuration/HomeSection.h index c99205268..6f2d6aa59 100644 --- a/Application/Sources/Configuration/HomeSection.h +++ b/Application/Sources/Configuration/HomeSection.h @@ -17,6 +17,7 @@ typedef NS_ENUM(NSInteger, HomeSection) { HomeSectionTVTopics, HomeSectionTVTopicsAccess, HomeSectionTVLatest, + HomeSectionTVWebFirst, HomeSectionTVMostPopular, HomeSectionTVSoonExpiring, HomeSectionTVShowsAccess, diff --git a/Application/Sources/Configuration/HomeSection.m b/Application/Sources/Configuration/HomeSection.m index 5f32c21c2..c255b5f19 100644 --- a/Application/Sources/Configuration/HomeSection.m +++ b/Application/Sources/Configuration/HomeSection.m @@ -17,6 +17,7 @@ @(HomeSectionTVTopics) : NSLocalizedString(@"Topics", @"Title label used to present TV topics while loading. It appears if no network connection is available and no cache is available"), @(HomeSectionTVTopicsAccess) : NSLocalizedString(@"Topics", @"Title label used to present TV topics"), @(HomeSectionTVLatest) : NSLocalizedString(@"Latest videos", @"Title label used to present the latest videos"), + @(HomeSectionTVWebFirst) : NSLocalizedString(@"Already available", @"Title label used to present already available videos, usually badged as web first"), @(HomeSectionTVMostPopular) : NSLocalizedString(@"Most popular", @"Title label used to present the TV most popular videos"), @(HomeSectionTVSoonExpiring) : NSLocalizedString(@"Available for a limited time", @"Title label used to present the soon expiring videos"), @(HomeSectionTVScheduledLivestreams) : NSLocalizedString(@"Events", @"Title label used to present scheduled livestream medias"), diff --git a/Application/Sources/Configuration/RadioChannel.h b/Application/Sources/Configuration/RadioChannel.h index 74f486c46..4751e89cb 100755 --- a/Application/Sources/Configuration/RadioChannel.h +++ b/Application/Sources/Configuration/RadioChannel.h @@ -28,5 +28,6 @@ NS_ASSUME_NONNULL_BEGIN */ OBJC_EXPORT UIImage *RadioChannelLogo22Image(RadioChannel * _Nullable radioChannel); OBJC_EXPORT UIImage *RadioChannelLogo32Image(RadioChannel * _Nullable radioChannel); +OBJC_EXPORT UIImage * _Nullable RadioChannelLogo60Image(RadioChannel * _Nullable radioChannel); NS_ASSUME_NONNULL_END diff --git a/Application/Sources/Configuration/RadioChannel.m b/Application/Sources/Configuration/RadioChannel.m index 7ca2a7192..eb3249713 100755 --- a/Application/Sources/Configuration/RadioChannel.m +++ b/Application/Sources/Configuration/RadioChannel.m @@ -43,3 +43,8 @@ - (instancetype)initWithDictionary:(NSDictionary *)dictionary { return [UIImage imageNamed:[NSString stringWithFormat:@"logo_%@-32", radioChannel.resourceUid]] ?: [UIImage imageNamed:@"radioset-32"]; } + +UIImage *RadioChannelLogo60Image(RadioChannel *radioChannel) +{ + return [UIImage imageNamed:[NSString stringWithFormat:@"logo_%@-60", radioChannel.resourceUid]]; +} diff --git a/Application/Sources/Configuration/TVChannel.h b/Application/Sources/Configuration/TVChannel.h index 65da9b908..d5ab61cc8 100755 --- a/Application/Sources/Configuration/TVChannel.h +++ b/Application/Sources/Configuration/TVChannel.h @@ -20,5 +20,6 @@ NS_ASSUME_NONNULL_BEGIN */ OBJC_EXPORT UIImage *TVChannelLogo22Image(TVChannel * _Nullable tvChannel); OBJC_EXPORT UIImage *TVChannelLogo32Image(TVChannel * _Nullable tvChannel); +OBJC_EXPORT UIImage * _Nullable TVChannelLogo60Image(TVChannel * _Nullable tvChannel); NS_ASSUME_NONNULL_END diff --git a/Application/Sources/Configuration/TVChannel.m b/Application/Sources/Configuration/TVChannel.m index 084775243..469ff4d30 100755 --- a/Application/Sources/Configuration/TVChannel.m +++ b/Application/Sources/Configuration/TVChannel.m @@ -23,3 +23,8 @@ @implementation TVChannel { return [UIImage imageNamed:[NSString stringWithFormat:@"logo_%@-32", tvChannel.resourceUid]] ?: [UIImage imageNamed:@"tv-32"]; } + +UIImage *TVChannelLogo60Image(TVChannel *tvChannel) +{ + return [UIImage imageNamed:[NSString stringWithFormat:@"logo_%@-60", tvChannel.resourceUid]]; +} diff --git a/Application/Sources/Helpers/AnalyticsConstants.h b/Application/Sources/Helpers/AnalyticsConstants.h index 46aa577c7..ceb7c2b64 100755 --- a/Application/Sources/Helpers/AnalyticsConstants.h +++ b/Application/Sources/Helpers/AnalyticsConstants.h @@ -46,6 +46,7 @@ OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleLatest; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleLatestEpisodes; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleLicense; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleLicenses; +OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleMedia; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleMostPopular; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleNotifications; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitlePlayer; @@ -60,6 +61,7 @@ OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleSports; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleTrending; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleTV; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleWatchLater; +OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleWebFirst; OBJC_EXPORT AnalyticsPageTitle const AnalyticsPageTitleWhatsNew; /** diff --git a/Application/Sources/Helpers/AnalyticsConstants.m b/Application/Sources/Helpers/AnalyticsConstants.m index 8284f6bce..79be4ed30 100755 --- a/Application/Sources/Helpers/AnalyticsConstants.m +++ b/Application/Sources/Helpers/AnalyticsConstants.m @@ -5,7 +5,6 @@ // #import "AnalyticsConstants.h" -#import "NSBundle+PlaySRG.h" // See reference specifications at https://confluence.srg.beecollaboration.com/display/SRGPLAY/Play+SRG+simplified+page+view+analytics @@ -36,6 +35,7 @@ AnalyticsPageTitle const AnalyticsPageTitleLatestEpisodes = @"latest episodes"; AnalyticsPageTitle const AnalyticsPageTitleLicense = @"license"; AnalyticsPageTitle const AnalyticsPageTitleLicenses = @"licenses"; +AnalyticsPageTitle const AnalyticsPageTitleMedia = @"media"; AnalyticsPageTitle const AnalyticsPageTitleMostPopular = @"most popular"; AnalyticsPageTitle const AnalyticsPageTitleNotifications = @"notifications"; AnalyticsPageTitle const AnalyticsPageTitlePlayer = @"player"; @@ -50,6 +50,7 @@ AnalyticsPageTitle const AnalyticsPageTitleTrending = @"trending"; AnalyticsPageTitle const AnalyticsPageTitleTV = @"tv"; AnalyticsPageTitle const AnalyticsPageTitleWatchLater = @"watch later"; +AnalyticsPageTitle const AnalyticsPageTitleWebFirst = @"web first"; AnalyticsPageTitle const AnalyticsPageTitleWhatsNew = @"what is new"; AnalyticsTitle const AnalyticsTitleContinuousPlayback = @"continuous_playback"; @@ -127,6 +128,7 @@ AnalyticsPageTitle AnalyticsPageTitleForHomeSection(HomeSection homeSection) s_titles = @{ @(HomeSectionTVTrending) : AnalyticsPageTitleTrending, @(HomeSectionTVLive) : AnalyticsPageTitleTV, @(HomeSectionTVLatest) : AnalyticsPageTitleLatest, + @(HomeSectionTVWebFirst) : AnalyticsPageTitleWebFirst, @(HomeSectionTVMostPopular) : AnalyticsPageTitleMostPopular, @(HomeSectionTVSoonExpiring) : AnalyticsPageTitleSoonExpiring, @(HomeSectionTVScheduledLivestreams) : AnalyticsPageTitleEvents, diff --git a/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.h b/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.h index 3bb590870..f41521580 100755 --- a/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.h +++ b/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.h @@ -15,6 +15,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (class, nonatomic, readonly) NSDateFormatter *play_timeFormatter; +/** + * Absolute date and time formatting. + */ +@property (class, nonatomic, readonly) NSDateFormatter *play_dateAndTimeFormatter; + /** * Relative date and time formatting, i.e. displays today / yesterday / tomorrow / ... for dates near today. * diff --git a/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.m b/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.m index d027af05d..a92980cb1 100755 --- a/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.m +++ b/Application/Sources/Helpers/Categories/NSDateFormatter+PlaySRG.m @@ -20,6 +20,18 @@ + (NSDateFormatter *)play_timeFormatter return s_dateFormatter; } ++ (NSDateFormatter *)play_dateAndTimeFormatter +{ + static NSDateFormatter *s_dateFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateFormatter = [[NSDateFormatter alloc] init]; + s_dateFormatter.dateStyle = NSDateFormatterShortStyle; + s_dateFormatter.timeStyle = NSDateFormatterShortStyle; + }); + return s_dateFormatter; +} + + (NSDateFormatter *)play_relativeDateAndTimeFormatter { static NSDateFormatter *s_dateFormatter; diff --git a/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.h b/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.h index 0d2664a0d..f2f166814 100755 --- a/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.h +++ b/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.h @@ -10,7 +10,8 @@ NS_ASSUME_NONNULL_BEGIN @interface SRGChannel (PlaySRG) -@property (nonatomic, readonly, nullable) UIImage *play_logo32Image; +@property (nonatomic, readonly) UIImage *play_logo32Image; +@property (nonatomic, readonly) UIImage *play_logo60Image; @end diff --git a/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.m b/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.m index 1702a4c1e..b381e929d 100755 --- a/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.m +++ b/Application/Sources/Helpers/Categories/SRGChannel+PlaySRG.m @@ -26,4 +26,18 @@ - (UIImage *)play_logo32Image } } +- (UIImage *)play_logo60Image +{ + if (self.transmission == SRGTransmissionRadio) { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @keypath(RadioChannel.new, uid), self.uid]; + RadioChannel *radioChannel = [ApplicationConfiguration.sharedApplicationConfiguration.radioChannels filteredArrayUsingPredicate:predicate].firstObject; + return RadioChannelLogo60Image(radioChannel); + } + else { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @keypath(TVChannel.new, uid), self.uid]; + TVChannel *tvChannel = [ApplicationConfiguration.sharedApplicationConfiguration.tvChannels filteredArrayUsingPredicate:predicate].firstObject; + return TVChannelLogo60Image(tvChannel); + } +} + @end diff --git a/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.h b/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.h index caf5ccc7f..423a3784e 100755 --- a/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.h +++ b/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.h @@ -27,6 +27,9 @@ OBJC_EXPORT BOOL PlayIsSwissTXTURN(NSString *URN); @property (nonatomic, readonly, getter=play_isWebFirst) BOOL play_webFirst; +@property (nonatomic, readonly) NSArray *play_subtitleLanguages; +@property (nonatomic, readonly) NSArray *play_audioLanguages; + @end NS_ASSUME_NONNULL_END diff --git a/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.m b/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.m index be0dae16d..10994fbcd 100755 --- a/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.m +++ b/Application/Sources/Helpers/Categories/SRGMedia+PlaySRG.m @@ -33,20 +33,18 @@ - (NSString *)play_fullSummary - (BOOL)play_areSubtitlesAvailable { - return [self subtitleVariantsForSource:self.recommendedSubtitleVariantSource].count != 0; + return self.play_subtitleVariants.count != 0; } - (BOOL)play_isAudioDescriptionAvailable { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @keypath(SRGVariant.new, type), @(SRGVariantTypeAudioDescription)]; - NSArray *audioVariants = [self audioVariantsForSource:self.recommendedAudioVariantSource]; - return [audioVariants filteredArrayUsingPredicate:predicate].count != 0; + return [self.play_audioVariants filteredArrayUsingPredicate:predicate].count != 0; } - (BOOL)play_isMultiAudioAvailable { - NSArray *audioVariants = [self audioVariantsForSource:self.recommendedAudioVariantSource]; - NSArray *locales = [audioVariants valueForKey:@keypath(SRGVariant.new, locale)]; + NSArray *locales = [self.play_audioVariants valueForKey:@keypath(SRGVariant.new, locale)]; return [NSSet setWithArray:locales].count > 1; } @@ -56,6 +54,26 @@ - (BOOL)play_isWebFirst return [self.date compare:date] == NSOrderedDescending && [self timeAvailabilityAtDate:date] == SRGTimeAvailabilityAvailable && self.contentType == SRGContentTypeEpisode; } +- (NSArray *)play_subtitleLanguages +{ + return [self.play_subtitleVariants valueForKeyPath:@keypath(SRGVariant.new, language)]; +} + +- (NSArray *)play_audioLanguages +{ + return [self.play_audioVariants valueForKeyPath:@keypath(SRGVariant.new, language)]; +} + +- (NSArray *)play_subtitleVariants +{ + return [self subtitleVariantsForSource:self.recommendedSubtitleVariantSource]; +} + +- (NSArray *)play_audioVariants +{ + return [self audioVariantsForSource:self.recommendedAudioVariantSource]; +} + @end #pragma mark Functions diff --git a/Application/Sources/Helpers/Categories/UIImage+PlaySRG.m b/Application/Sources/Helpers/Categories/UIImage+PlaySRG.m index 0d129678d..f1beca45e 100755 --- a/Application/Sources/Helpers/Categories/UIImage+PlaySRG.m +++ b/Application/Sources/Helpers/Categories/UIImage+PlaySRG.m @@ -28,8 +28,8 @@ CGSize SizeForImageScale(ImageScale imageScale) } #else s_widths = @{ @(ImageScaleSmall) : @(350.f), - @(ImageScaleMedium) : @(1000.f), - @(ImageScaleLarge) : @(1600.f)}; + @(ImageScaleMedium) : @(800.f), + @(ImageScaleLarge) : @(1000.f)}; #endif }); diff --git a/Application/Sources/Helpers/Categories/UILabel+PlaySRG.m b/Application/Sources/Helpers/Categories/UILabel+PlaySRG.m index 38f0b637c..77a1dcc94 100755 --- a/Application/Sources/Helpers/Categories/UILabel+PlaySRG.m +++ b/Application/Sources/Helpers/Categories/UILabel+PlaySRG.m @@ -67,7 +67,7 @@ - (void)play_displayAvailabilityLabelForMediaMetadata:(id)obje NSTimeInterval timeIntervalAfterEnd = [nowDate timeIntervalSinceDate:endDate]; text = [NSString stringWithFormat:NSLocalizedString(@"Not available since %@", @"Explains that a content has expired (days or hours ago). Displayed in the media player view."), LabelFormattedDuration(timeIntervalAfterEnd)]; } - else if (timeAvailability == SRGTimeAvailabilityAvailable && object.endDate && object.contentType != SRGContentTypeScheduledLivestream && object.contentType != SRGContentTypeLivestream) { + else if (timeAvailability == SRGTimeAvailabilityAvailable && object.endDate && object.contentType != SRGContentTypeScheduledLivestream && object.contentType != SRGContentTypeLivestream && object.contentType != SRGContentTypeTrailer) { NSDateComponents *monthsDateComponents = [NSCalendar.currentCalendar components:NSCalendarUnitDay fromDate:nowDate toDate:object.endDate options:0]; if (monthsDateComponents.day <= 30) { NSTimeInterval timeIntervalBeforeEnd = [object.endDate timeIntervalSinceDate:nowDate]; diff --git a/Application/Sources/Helpers/ChannelService.h b/Application/Sources/Helpers/ChannelService.h index 9a6ff5a99..6431ec1fc 100755 --- a/Application/Sources/Helpers/ChannelService.h +++ b/Application/Sources/Helpers/ChannelService.h @@ -25,9 +25,9 @@ typedef void (^ChannelServiceUpdateBlock)(SRGProgramComposition * _Nullable prog /** * Register an observer to be notified of updates for a given channel. The provided block is called when channel information - * is available. + * is available. An opaque handle to the observer is returned for unregistration purposes. */ -- (id)addObserver:(id)observer forUpdatesWithChannel:(SRGChannel *)channel livestreamUid:(NSString *)livestreamUid block:(ChannelServiceUpdateBlock)block; +- (id)addObserverForUpdatesWithChannel:(SRGChannel *)channel livestreamUid:(NSString *)livestreamUid block:(ChannelServiceUpdateBlock)block; /** * Remove the specified observer. diff --git a/Application/Sources/Helpers/ChannelService.m b/Application/Sources/Helpers/ChannelService.m index 8bcf0cea1..ebce8cfc9 100755 --- a/Application/Sources/Helpers/ChannelService.m +++ b/Application/Sources/Helpers/ChannelService.m @@ -79,7 +79,7 @@ - (void)setUpdateTimer:(ForegroundTimer *)updateTimer #pragma mark Registration -- (id)addObserver:(id)observer forUpdatesWithChannel:(SRGChannel *)channel livestreamUid:(NSString *)livestreamUid block:(ChannelServiceUpdateBlock)block +- (id)addObserverForUpdatesWithChannel:(SRGChannel *)channel livestreamUid:(NSString *)livestreamUid block:(ChannelServiceUpdateBlock)block { ChannelServiceSetup *setup = [[ChannelServiceSetup alloc] initWithChannel:channel livestreamUid:livestreamUid]; NSMutableDictionary *channelRegistrations = self.registrations[setup]; diff --git a/Application/Sources/Helpers/PlayDurationFormatter.h b/Application/Sources/Helpers/PlayDurationFormatter.h index ef28ebe43..6edbbfb79 100755 --- a/Application/Sources/Helpers/PlayDurationFormatter.h +++ b/Application/Sources/Helpers/PlayDurationFormatter.h @@ -8,6 +8,19 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Standard duration formatters for a single unit. + */ +OBJC_EXPORT NSString *PlayFormattedHours(NSTimeInterval duration); +OBJC_EXPORT NSString *PlayFormattedDays(NSTimeInterval duration); + +/** + * Short duration formatters for a single unit (minimum 1 unit). + */ +OBJC_EXPORT NSString *PlayShortFormattedMinutes(NSTimeInterval duration); +OBJC_EXPORT NSString *PlayShortFormattedHours(NSTimeInterval duration); +OBJC_EXPORT NSString *PlayShortFormattedDays(NSTimeInterval duration); + /** * Formats a duration in a standard form, e.g. for use in duration labels. */ @@ -18,4 +31,9 @@ OBJC_EXPORT NSString *PlayFormattedDuration(NSTimeInterval duration); */ OBJC_EXPORT NSString *PlayHumanReadableFormattedDuration(NSTimeInterval duration); +/** + * Formats a remaining time duration in hours or minutes (minimum 1 minute). + */ +OBJC_EXPORT NSString *PlayRemainingTimeFormattedDuration(NSTimeInterval duration); + NS_ASSUME_NONNULL_END diff --git a/Application/Sources/Helpers/PlayDurationFormatter.m b/Application/Sources/Helpers/PlayDurationFormatter.m index 7bccad76c..083b394d1 100755 --- a/Application/Sources/Helpers/PlayDurationFormatter.m +++ b/Application/Sources/Helpers/PlayDurationFormatter.m @@ -6,6 +6,71 @@ #import "PlayDurationFormatter.h" +NSString *PlayFormattedHours(NSTimeInterval duration) +{ + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitHour; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; + s_dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:duration]; +} + +NSString *PlayFormattedDays(NSTimeInterval duration) +{ + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitDay; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; + s_dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:duration]; +} + +NSString *PlayShortFormattedMinutes(NSTimeInterval duration) +{ + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitMinute; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleShort; + s_dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:fmax(60., duration)]; +} + +NSString *PlayShortFormattedHours(NSTimeInterval duration) +{ + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitHour; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleShort; + s_dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:fmax(60. * 60., duration)]; +} + +NSString *PlayShortFormattedDays(NSTimeInterval duration) +{ + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitDay; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleShort; + s_dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:fmax(24 * 60. * 60., duration)]; +} + NSString *PlayFormattedDuration(NSTimeInterval duration) { if (duration <= 60. * 60.) { @@ -55,3 +120,27 @@ return [s_dateComponentsFormatter stringFromTimeInterval:duration]; } } + +NSString *PlayRemainingTimeFormattedDuration(NSTimeInterval duration) +{ + if (duration >= 60. * 60.) { + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitHour; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:duration]; + } + else { + static NSDateComponentsFormatter *s_dateComponentsFormatter; + static dispatch_once_t s_onceToken; + dispatch_once(&s_onceToken, ^{ + s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; + s_dateComponentsFormatter.allowedUnits = NSCalendarUnitMinute; + s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; + }); + return [s_dateComponentsFormatter stringFromTimeInterval:fmax(60., duration)]; + } +} diff --git a/Application/Sources/History/History.h b/Application/Sources/History/History.h index 48e62ddeb..4a200c3c8 100755 --- a/Application/Sources/History/History.h +++ b/Application/Sources/History/History.h @@ -5,10 +5,16 @@ // @import SRGDataProviderModel; +@import SRGLetterbox; @import SRGMediaPlayer; NS_ASSUME_NONNULL_BEGIN +/** + * Update playback progress based on the provided controller. + */ +OBJC_EXPORT void HistoryUpdateLetterboxPlaybackProgress(SRGLetterboxController *letterboxController); + /** * Return the playback progress corresponding to the specified playback position and media duration. This takes * into account end tolerance settings which might be applied. diff --git a/Application/Sources/History/History.m b/Application/Sources/History/History.m index f00d24ba6..e6452e4fb 100755 --- a/Application/Sources/History/History.m +++ b/Application/Sources/History/History.m @@ -7,15 +7,20 @@ #import "History.h" #import "ApplicationConfiguration.h" +#if TARGET_OS_IOS #import "Download.h" +#endif #import "NSTimer+PlaySRG.h" +#if TARGET_OS_IOS @import GoogleCast; -@import SRGLetterbox; +#endif @import SRGUserData; static NSMutableDictionary *s_cachedProgresses; +#if TARGET_OS_IOS static NSTimer *s_trackerTimer; +#endif #pragma mark Helpers @@ -55,73 +60,103 @@ float HistoryPlaybackProgress(NSTimeInterval playbackPosition, double durationIn return [mediaComposition mediaForSubdivision:mediaComposition.mainChapter]; } +#if TARGET_OS_IOS SRGMedia *media = controller.media; if (media && [Download downloadForMedia:media]) { return media; } +#endif return nil; } #pragma mark Player tracker +/** + * Update progress information based on the provided controller. + */ +void HistoryUpdateLetterboxPlaybackProgress(SRGLetterboxController *letterboxController) +{ + if (letterboxController.playbackState != SRGMediaPlayerPlaybackStatePlaying) { + return; + } + + SRGMedia *chapterMedia = HistoryChapterMedia(letterboxController); + if (! chapterMedia || chapterMedia.contentType == SRGContentTypeLivestream) { + return; + } + + CMTime currentTime = letterboxController.currentTime; + CMTime chapterPlaybackTime = (chapterMedia.contentType != SRGContentTypeScheduledLivestream && CMTIME_IS_VALID(currentTime)) ? currentTime : kCMTimeZero; + NSString *deviceUid = UIDevice.currentDevice.name; + + // Save the segment position. + SRGSubdivision *subdivision = letterboxController.subdivision; + if ([subdivision isKindOfClass:SRGSegment.class]) { + SRGSegment *segment = (SRGSegment *)subdivision; + CMTime segmentPlaybackTime = CMTimeMaximum(CMTimeSubtract(chapterPlaybackTime, CMTimeMakeWithSeconds(segment.markIn / 1000., NSEC_PER_SEC)), kCMTimeZero); + [SRGUserData.currentUserData.history saveHistoryEntryWithUid:segment.URN lastPlaybackTime:segmentPlaybackTime deviceUid:deviceUid completionBlock:nil]; + } + + // Save the main full-length position (update after the segment so that full-length entries are always more recent than corresponding + // segment entries) + [SRGUserData.currentUserData.history saveHistoryEntryWithUid:chapterMedia.URN lastPlaybackTime:chapterPlaybackTime deviceUid:deviceUid completionBlock:nil]; +} + +#if TARGET_OS_IOS + +/** + * Return YES if a cast session is active and update the progress if needed. + */ +static BOOL HistoryUpdateGoogleCastPlaybackProgress(void) +{ + GCKSession *session = [GCKCastContext sharedInstance].sessionManager.currentSession; + if (! session) { + return NO; + } + + GCKRemoteMediaClient *remoteMediaClient = session.remoteMediaClient; + GCKMediaStatus *mediaStatus = remoteMediaClient.mediaStatus; + if (mediaStatus.playerState != GCKMediaPlayerStatePlaying) { + return YES; + } + + // Only for on-demand streams + GCKMediaInformation *mediaInformation = mediaStatus.mediaInformation; + if (mediaInformation.streamType != GCKMediaStreamTypeBuffered) { + return YES; + } + + NSString *URN = mediaInformation.contentID; + if (! URN) { + return YES; + } + + // Use approximate value. The value in GCKMediaStatus is updated from time to time. The approximateStreamPosition + // interpolates between known values to get a smoother progress + NSTimeInterval streamPosition = remoteMediaClient.approximateStreamPosition; + NSString *deviceUid = UIDevice.currentDevice.name; + [SRGUserData.currentUserData.history saveHistoryEntryWithUid:URN lastPlaybackTime:CMTimeMakeWithSeconds(streamPosition, NSEC_PER_SEC) deviceUid:deviceUid completionBlock:nil]; + + return YES; +} + +#endif + __attribute__((constructor)) static void HistoryPlayerTrackerInit(void) { s_cachedProgresses = [NSMutableDictionary dictionary]; + +#if TARGET_OS_IOS s_trackerTimer = [NSTimer play_timerWithTimeInterval:1. repeats:YES block:^(NSTimer * _Nonnull timer) { - NSString *deviceUid = UIDevice.currentDevice.name; - - GCKSession *session = [GCKCastContext sharedInstance].sessionManager.currentSession; - if (session) { - GCKRemoteMediaClient *remoteMediaClient = session.remoteMediaClient; - GCKMediaStatus *mediaStatus = remoteMediaClient.mediaStatus; - if (mediaStatus.playerState != GCKMediaPlayerStatePlaying) { - return; - } - - // Only for on-demand streams - GCKMediaInformation *mediaInformation = mediaStatus.mediaInformation; - if (mediaInformation.streamType != GCKMediaStreamTypeBuffered) { - return; - } - - NSString *URN = mediaInformation.contentID; - if (! URN) { - return; - } - - // Use approximate value. The value in GCKMediaStatus is updated from time to time. The approximateStreamPosition - // interpolates between known values to get a smoother progress - NSTimeInterval streamPosition = remoteMediaClient.approximateStreamPosition; - [SRGUserData.currentUserData.history saveHistoryEntryWithUid:URN lastPlaybackTime:CMTimeMakeWithSeconds(streamPosition, NSEC_PER_SEC) deviceUid:deviceUid completionBlock:nil]; - } - else { - SRGLetterboxController *letterboxController = SRGLetterboxService.sharedService.controller; - if (letterboxController.playbackState != SRGMediaPlayerPlaybackStatePlaying) { - return; - } - - SRGMedia *chapterMedia = HistoryChapterMedia(letterboxController); - if (! chapterMedia || chapterMedia.contentType == SRGContentTypeLivestream) { - return; - } - - CMTime currentTime = letterboxController.currentTime; - CMTime chapterPlaybackTime = (chapterMedia.contentType != SRGContentTypeScheduledLivestream && CMTIME_IS_VALID(currentTime)) ? currentTime : kCMTimeZero; - - // Save the segment position. - SRGSubdivision *subdivision = letterboxController.subdivision; - if ([subdivision isKindOfClass:SRGSegment.class]) { - SRGSegment *segment = (SRGSegment *)subdivision; - CMTime segmentPlaybackTime = CMTimeMaximum(CMTimeSubtract(chapterPlaybackTime, CMTimeMakeWithSeconds(segment.markIn / 1000., NSEC_PER_SEC)), kCMTimeZero); - [SRGUserData.currentUserData.history saveHistoryEntryWithUid:segment.URN lastPlaybackTime:segmentPlaybackTime deviceUid:deviceUid completionBlock:nil]; - } - - // Save the main full-length position (update after the segment so that full-length entries are always more recent than corresponding - // segment entries) - [SRGUserData.currentUserData.history saveHistoryEntryWithUid:chapterMedia.URN lastPlaybackTime:chapterPlaybackTime deviceUid:deviceUid completionBlock:nil]; + if (HistoryUpdateGoogleCastPlaybackProgress()) { + return; } + + SRGLetterboxController *letterboxController = SRGLetterboxService.sharedService.controller; + HistoryUpdateLetterboxPlaybackProgress(letterboxController); }]; +#endif } #pragma mark Media metadata functions diff --git a/Application/Sources/Home/HomeLiveMediaCollectionViewCell.m b/Application/Sources/Home/HomeLiveMediaCollectionViewCell.m index 1ac0a141a..a5ab5c86e 100755 --- a/Application/Sources/Home/HomeLiveMediaCollectionViewCell.m +++ b/Application/Sources/Home/HomeLiveMediaCollectionViewCell.m @@ -26,31 +26,6 @@ @import SRGAnalytics; @import SRGAppearance; -static NSString *RemainingTimeFormattedDuration(NSTimeInterval duration) -{ - if (duration >= 60. * 60.) { - static NSDateComponentsFormatter *s_dateComponentsFormatter; - static dispatch_once_t s_onceToken; - dispatch_once(&s_onceToken, ^{ - s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; - s_dateComponentsFormatter.allowedUnits = NSCalendarUnitHour; - s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; - }); - return [s_dateComponentsFormatter stringFromTimeInterval:duration]; - } - else { - static NSDateComponentsFormatter *s_dateComponentsFormatter; - static dispatch_once_t s_onceToken; - dispatch_once(&s_onceToken, ^{ - s_dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init]; - s_dateComponentsFormatter.allowedUnits = NSCalendarUnitMinute; - s_dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; - }); - // Minimum is 1 minute - return [s_dateComponentsFormatter stringFromTimeInterval:fmax(60., duration)]; - } -} - @interface HomeLiveMediaCollectionViewCell () @property (nonatomic) SRGProgramComposition *programComposition; @@ -73,7 +48,7 @@ @interface HomeLiveMediaCollectionViewCell () @property (nonatomic, weak) IBOutlet NSLayoutConstraint *topSpaceConstraint; @property (nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *horizontalSpaceConstraints; -@property (nonatomic, weak) id channelRegistration; +@property (nonatomic, weak) id channelObserver; @end @@ -220,8 +195,8 @@ - (void)registerForChannelUpdatesWithMedia:(SRGMedia *)media return; } - [ChannelService.sharedService removeObserver:self.channelRegistration]; - self.channelRegistration = [ChannelService.sharedService addObserver:self forUpdatesWithChannel:media.channel livestreamUid:media.uid block:^(SRGProgramComposition * _Nullable programComposition) { + [ChannelService.sharedService removeObserver:self.channelObserver]; + self.channelObserver = [ChannelService.sharedService addObserverForUpdatesWithChannel:media.channel livestreamUid:media.uid block:^(SRGProgramComposition * _Nullable programComposition) { self.programComposition = programComposition; [self reloadData]; }]; @@ -229,7 +204,7 @@ - (void)registerForChannelUpdatesWithMedia:(SRGMedia *)media - (void)unregisterChannelUpdates { - [ChannelService.sharedService removeObserver:self.channelRegistration]; + [ChannelService.sharedService removeObserver:self.channelObserver]; } #pragma mark UI @@ -271,7 +246,7 @@ - (void)reloadData self.titleLabel.text = currentProgram.title; NSTimeInterval remainingTimeInterval = [currentProgram.endDate timeIntervalSinceDate:NSDate.date]; - self.subtitleLabel.text = [NSString stringWithFormat:NSLocalizedString(@"%@ remaining", "Text displayed on live cells telling how much time remains for a program currently on air"), RemainingTimeFormattedDuration(remainingTimeInterval)]; + self.subtitleLabel.text = [NSString stringWithFormat:NSLocalizedString(@"%@ remaining", "Text displayed on live cells telling how much time remains for a program currently on air"), PlayRemainingTimeFormattedDuration(remainingTimeInterval)]; float progress = [NSDate.date timeIntervalSinceDate:currentProgram.startDate] / ([currentProgram.endDate timeIntervalSinceDate:currentProgram.startDate]); self.progressView.progress = fmaxf(fminf(progress, 1.f), 0.f); diff --git a/Application/Sources/Home/HomeSectionInfo.m b/Application/Sources/Home/HomeSectionInfo.m index 055be80a3..796597a38 100755 --- a/Application/Sources/Home/HomeSectionInfo.m +++ b/Application/Sources/Home/HomeSectionInfo.m @@ -231,11 +231,20 @@ - (void)refreshWithRequestQueue:(SRGRequestQueue *)requestQueue page:(SRGPage *) switch (self.homeSection) { case HomeSectionTVTrending: { - SRGBaseRequest *request = [SRGDataProvider.currentDataProvider tvTrendingMediasForVendor:vendor withLimit:@(pageSize) editorialLimit:applicationConfiguration.tvTrendingEditorialLimit episodesOnly:applicationConfiguration.tvTrendingEpisodesOnly completionBlock:^(NSArray * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { - [requestQueue reportError:error]; - paginatedItemListCompletionBlock(medias, [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); - }]; - [requestQueue addRequest:request resume:YES]; + if (applicationConfiguration.tvTrendingPrefersHeroStage) { + SRGBaseRequest *request = [SRGDataProvider.currentDataProvider tvHeroStageMediasForVendor:vendor withCompletionBlock:^(NSArray * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { + [requestQueue reportError:error]; + paginatedItemListCompletionBlock(medias, [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); + }]; + [requestQueue addRequest:request resume:YES]; + } + else { + SRGBaseRequest *request = [SRGDataProvider.currentDataProvider tvTrendingMediasForVendor:vendor withLimit:@(pageSize) editorialLimit:applicationConfiguration.tvTrendingEditorialLimit episodesOnly:applicationConfiguration.tvTrendingEpisodesOnly completionBlock:^(NSArray * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { + [requestQueue reportError:error]; + paginatedItemListCompletionBlock(medias, [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); + }]; + [requestQueue addRequest:request resume:YES]; + } break; } @@ -307,6 +316,15 @@ - (void)refreshWithRequestQueue:(SRGRequestQueue *)requestQueue page:(SRGPage *) break; } + case HomeSectionTVWebFirst: { + SRGBaseRequest *request = [[[SRGDataProvider.currentDataProvider tvLatestWebFirstEpisodesForVendor:vendor withCompletionBlock:^(NSArray * _Nullable medias, SRGPage * _Nonnull page, SRGPage * _Nullable nextPage, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { + [requestQueue reportError:error]; + paginatedItemListCompletionBlock(medias, page, nextPage, HTTPResponse, error); + }] requestWithPageSize:pageSize] requestWithPage:page]; + [requestQueue addRequest:request resume:YES]; + break; + } + case HomeSectionTVMostPopular: { SRGBaseRequest *request = [[[SRGDataProvider.currentDataProvider tvMostPopularMediasForVendor:vendor withCompletionBlock:^(NSArray * _Nullable medias, SRGPage * _Nonnull page, SRGPage * _Nullable nextPage, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { [requestQueue reportError:error]; diff --git a/Application/Sources/Home/HomeShowsAccessTableViewCell.m b/Application/Sources/Home/HomeShowsAccessTableViewCell.m index 7e2031615..2b733c928 100755 --- a/Application/Sources/Home/HomeShowsAccessTableViewCell.m +++ b/Application/Sources/Home/HomeShowsAccessTableViewCell.m @@ -65,14 +65,14 @@ - (void)awakeFromNib self.showsAtoZButton.backgroundColor = UIColor.play_cardGrayBackgroundColor; self.showsAtoZButton.layer.cornerRadius = LayoutStandardViewCornerRadius; self.showsAtoZButton.layer.masksToBounds = YES; - [self.showsAtoZButton setTitle:NSLocalizedString(@"A to Z", @"Short title displayed in home page shows section.") forState:UIControlStateNormal]; - self.showsAtoZButton.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Programmes A-Z", @"Title displayed in home page shows section."); + [self.showsAtoZButton setTitle:NSLocalizedString(@"A to Z", @"Short title displayed in home pages on a button.") forState:UIControlStateNormal]; + self.showsAtoZButton.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"A to Z shows", @"Title pronounced in home pages on shows A to Z button."); self.showsByDateButton.backgroundColor = UIColor.play_cardGrayBackgroundColor; self.showsByDateButton.layer.cornerRadius = LayoutStandardViewCornerRadius; self.showsByDateButton.layer.masksToBounds = YES; - [self.showsByDateButton setTitle:NSLocalizedString(@"By date", @"Short title displayed in home page shows section.") forState:UIControlStateNormal]; - self.showsByDateButton.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Programmes by date", @"Title displayed in home page shows section."); + [self.showsByDateButton setTitle:NSLocalizedString(@"By date", @"Short title displayed in home pages on a button.") forState:UIControlStateNormal]; + self.showsByDateButton.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Shows by date", @"Title pronounced in home pages on shows by date button."); } - (void)reloadData diff --git a/Application/Sources/MiniPlayer/PlayMiniPlayerView.m b/Application/Sources/MiniPlayer/PlayMiniPlayerView.m index 74c929aea..1d0e64394 100755 --- a/Application/Sources/MiniPlayer/PlayMiniPlayerView.m +++ b/Application/Sources/MiniPlayer/PlayMiniPlayerView.m @@ -41,7 +41,7 @@ @interface PlayMiniPlayerView () @property (nonatomic) SRGMediaSearchSettings *settings; +@property (nonatomic, weak) UIBarButtonItem *filtersBarButtonItem; + @end @implementation SearchViewController @@ -215,7 +217,7 @@ - (void)prepareSearchResultsRefreshWithRequestQueue:(SRGRequestQueue *)requestQu NSString *query = self.query; ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; - SRGPageRequest *mediaSearchRequest = [[[SRGDataProvider.currentDataProvider mediasForVendor:applicationConfiguration.vendor matchingQuery:query withSettings:self.settings completionBlock:^(NSArray * _Nullable mediaURNs, NSNumber *total, SRGMediaAggregations *aggregations, NSArray * suggestions, SRGPage *page, SRGPage * _Nullable nextPage, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { + SRGPageRequest *mediaSearchRequest = [[[SRGDataProvider.currentDataProvider mediasForVendor:applicationConfiguration.vendor matchingQuery:query withSettings:self.settings completionBlock:^(NSArray * _Nullable mediaURNs, NSNumber * _Nullable total, SRGMediaAggregations * _Nullable aggregations, NSArray * _Nullable suggestions, SRGPage * _Nonnull page, SRGPage * _Nullable nextPage, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { if (error) { completionHandler(nil, page, nil, HTTPResponse, error); return; @@ -321,23 +323,31 @@ - (void)updateSearchSettingsButton { ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; if (! applicationConfiguration.searchSettingsHidden) { - UIButton *filtersButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [filtersButton addTarget:self action:@selector(showSettings:) forControlEvents:UIControlEventTouchUpInside]; - - filtersButton.titleLabel.font = [UIFont srg_regularFontWithSize:16.f]; - [filtersButton setTitle:NSLocalizedString(@"Filters", @"Filters button title") forState:UIControlStateNormal]; - [filtersButton setTitleColor:UIColor.grayColor forState:UIControlStateHighlighted]; + if (! self.filtersBarButtonItem) { + UIButton *filtersButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [filtersButton addTarget:self action:@selector(showSettings:) forControlEvents:UIControlEventTouchUpInside]; + + filtersButton.titleLabel.font = [UIFont srg_regularFontWithSize:16.f]; + [filtersButton setTitle:NSLocalizedString(@"Filters", @"Filters button title") forState:UIControlStateNormal]; + [filtersButton setTitleColor:UIColor.grayColor forState:UIControlStateHighlighted]; + + // See https://stackoverflow.com/a/25559946/760435 + static const CGFloat kInset = 2.f; + filtersButton.imageEdgeInsets = UIEdgeInsetsMake(0.f, -kInset, 0.f, kInset); + filtersButton.titleEdgeInsets = UIEdgeInsetsMake(0.f, kInset, 0.f, -kInset); + filtersButton.contentEdgeInsets = UIEdgeInsetsMake(0.f, kInset, 0.f, kInset); + + UIBarButtonItem *filtersBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:filtersButton]; + self.navigationItem.rightBarButtonItem = filtersBarButtonItem; + self.filtersBarButtonItem = filtersBarButtonItem; + } - // See https://stackoverflow.com/a/25559946/760435 - static const CGFloat kInset = 2.f; - filtersButton.imageEdgeInsets = UIEdgeInsetsMake(0.f, -kInset, 0.f, kInset); - filtersButton.titleEdgeInsets = UIEdgeInsetsMake(0.f, kInset, 0.f, -kInset); - filtersButton.contentEdgeInsets = UIEdgeInsetsMake(0.f, kInset, 0.f, kInset); + id customView = self.filtersBarButtonItem.customView; + NSAssert([customView isKindOfClass:UIButton.class], @"Expect a button by construction"); + UIButton *filtersButton = customView; UIImage *image = [SearchViewController containsAdvancedSettings:self.settings] ? [UIImage imageNamed:@"filter_on-22"] : [UIImage imageNamed:@"filter_off-22"]; [filtersButton setImage:image forState:UIControlStateNormal]; - - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:filtersButton]; } else { self.navigationItem.rightBarButtonItem = nil; @@ -698,9 +708,7 @@ - (void)showSettings:(id)sender UIPopoverPresentationController *popoverPresentationController = navigationController.popoverPresentationController; popoverPresentationController.backgroundColor = UIColor.play_popoverGrayBackgroundColor; popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUp; - - popoverPresentationController.sourceView = sender; - popoverPresentationController.sourceRect = [sender bounds]; + popoverPresentationController.barButtonItem = self.filtersBarButtonItem; [self presentViewController:navigationController animated:YES completion:nil]; } diff --git a/Application/Sources/Search/Settings/SearchSettingsViewController.m b/Application/Sources/Search/Settings/SearchSettingsViewController.m index 42bf055a0..d3ce31a68 100644 --- a/Application/Sources/Search/Settings/SearchSettingsViewController.m +++ b/Application/Sources/Search/Settings/SearchSettingsViewController.m @@ -200,26 +200,6 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations return [super supportedInterfaceOrientations] & UIViewController.play_supportedInterfaceOrientations; } -#pragma mark Responsiveness - -- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator -{ - [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; - - [coordinator animateAlongsideTransition:nil completion:^(id _Nonnull context) { - self.popoverPresentationController.sourceRect = self.popoverPresentationController.sourceView.bounds; - }]; -} - -- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id)coordinator -{ - [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator]; - - [coordinator animateAlongsideTransition:nil completion:^(id _Nonnull context) { - self.popoverPresentationController.sourceRect = self.popoverPresentationController.sourceView.bounds; - }]; -} - #pragma mark Status bar - (UIStatusBarStyle)preferredStatusBarStyle diff --git a/Application/Sources/Shows/ShowViewController.m b/Application/Sources/Shows/ShowViewController.m index 0f45b2f5a..b03f892da 100755 --- a/Application/Sources/Shows/ShowViewController.m +++ b/Application/Sources/Shows/ShowViewController.m @@ -29,6 +29,8 @@ @interface ShowViewController () @property (nonatomic) SRGShow *show; +@property (nonatomic, weak) UIBarButtonItem *shareBarButtonItem; + @property (nonatomic, getter=isFromPushNotification) BOOL fromPushNotification; @end @@ -79,12 +81,13 @@ - (void)viewDidLoad NSURL *sharingURL = [ApplicationConfiguration.sharedApplicationConfiguration sharingURLForShow:self.show]; if (sharingURL) { - UIBarButtonItem *shareButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"share-22"] - style:UIBarButtonItemStylePlain - target:self - action:@selector(shareContent:)]; - shareButtonItem.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Share", @"Share button label on player view"); - self.navigationItem.rightBarButtonItems = @[ shareButtonItem ]; + UIBarButtonItem *shareBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"share-22"] + style:UIBarButtonItemStylePlain + target:self + action:@selector(shareContent:)]; + shareBarButtonItem.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Share", @"Share button label on player view"); + self.navigationItem.rightBarButtonItems = @[ shareBarButtonItem ]; + self.shareBarButtonItem = shareBarButtonItem; } [self updateAppearanceForSize:self.view.frame.size]; @@ -353,11 +356,7 @@ - (IBAction)shareContent:(UIBarButtonItem *)barButtonItem activityViewController.modalPresentationStyle = UIModalPresentationPopover; UIPopoverPresentationController *popoverPresentationController = activityViewController.popoverPresentationController; - UIView *barButtonItemView = [barButtonItem valueForKey:@"view"]; - if (barButtonItemView) { - popoverPresentationController.sourceView = barButtonItemView; - popoverPresentationController.sourceRect = barButtonItemView.bounds; - } + popoverPresentationController.barButtonItem = self.shareBarButtonItem; [self presentViewController:activityViewController animated:YES completion:nil]; } diff --git a/Makefile b/Makefile index 1394322ea..dff9f5c69 100755 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/xcrun make -f CONFIGURATION_FOLDER=Configuration -CONFIGURATION_COMMIT_SHA1=62988977fb16c99c73e07b3efe03001952ba5c2c +CONFIGURATION_COMMIT_SHA1=a02ab73778811e71c82f8095743dca3b4240224f CARTHAGE_FOLDER=Carthage CARTHAGE_RESOLUTION_FLAGS=--new-resolver --no-build @@ -45,7 +45,7 @@ update: .PHONY: setup setup: - @echo "Setting up proprietary project..." + @echo "Setting up the project..." @if [ ! -d $(CONFIGURATION_FOLDER) ]; then \ git clone https://github.com/SRGSSR/playsrg-apple-configuration.git $(CONFIGURATION_FOLDER); \ @@ -62,19 +62,6 @@ setup: @echo "... done.\n" -.PHONY: public.setup -public.setup: - @echo "Setting up public project..." - - @rm -rf $(CONFIGURATION_FOLDER) - @rm -rf .env - @mkdir -p Xcode/Links - @pushd Xcode/Links > /dev/null; ln -fs ../Public/*.xcconfig . - - @pod install - - @echo "... done.\n" - .PHONY: clean clean: @echo "Cleaning up build products..." @@ -84,14 +71,10 @@ clean: .PHONY: help help: - @echo "The following targets must be used for proprietary builds:" + @echo "The following targets are available:" @echo " all Build project dependencies and the project" @echo " bootstrap Build previously resolved dependencies" @echo " update Update and build dependencies" @echo " setup Setup project" - @echo " public.setup Setup project (public settings)" - - @echo "" - @echo "The following targets are widely available:" @echo " help Display this message" @echo " clean Clean the project and its dependencies" diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index a44358c0e..9a84fd792 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -27,6 +27,11 @@ 0804917D22832C5D00E4CEC2 /* HomeShowCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0804917522832C5C00E4CEC2 /* HomeShowCollectionViewCell.m */; }; 0804917E22832C5D00E4CEC2 /* HomeShowCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0804917522832C5C00E4CEC2 /* HomeShowCollectionViewCell.m */; }; 0804917F22832C5D00E4CEC2 /* HomeShowCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0804917522832C5C00E4CEC2 /* HomeShowCollectionViewCell.m */; }; + 0805703E2540E0FC00A59C9D /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0805703D2540E0FC00A59C9D /* Navigation.swift */; }; + 0805703F2540E0FC00A59C9D /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0805703D2540E0FC00A59C9D /* Navigation.swift */; }; + 080570402540E0FC00A59C9D /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0805703D2540E0FC00A59C9D /* Navigation.swift */; }; + 080570412540E0FC00A59C9D /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0805703D2540E0FC00A59C9D /* Navigation.swift */; }; + 080570422540E0FC00A59C9D /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0805703D2540E0FC00A59C9D /* Navigation.swift */; }; 0806E7B81D50D918002ED406 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0806E7B61D50D918002ED406 /* SettingsViewController.m */; }; 0806E7B91D50D918002ED406 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0806E7B61D50D918002ED406 /* SettingsViewController.m */; }; 0806E7BA1D50D919002ED406 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0806E7B61D50D918002ED406 /* SettingsViewController.m */; }; @@ -52,11 +57,11 @@ 081220C31DD0ADAC00BF8326 /* DownloadSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 081220C01DD0ADAC00BF8326 /* DownloadSession.m */; }; 081220C41DD0ADAC00BF8326 /* DownloadSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 081220C01DD0ADAC00BF8326 /* DownloadSession.m */; }; 081220C51DD0ADAC00BF8326 /* DownloadSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 081220C01DD0ADAC00BF8326 /* DownloadSession.m */; }; - 081454BB2546CDD500BB7CA6 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081454BA2546CDD500BB7CA6 /* SwiftMessages */; }; - 081455142546CE4300BB7CA6 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455132546CE4300BB7CA6 /* SwiftMessages */; }; - 081455162546CE4F00BB7CA6 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455152546CE4F00BB7CA6 /* SwiftMessages */; }; - 081455182546CE5700BB7CA6 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455172546CE5700BB7CA6 /* SwiftMessages */; }; - 0814551A2546CE5F00BB7CA6 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455192546CE5F00BB7CA6 /* SwiftMessages */; }; + 081456312547740600BB7CA6 /* Badges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081456302547740600BB7CA6 /* Badges.swift */; }; + 081456322547740600BB7CA6 /* Badges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081456302547740600BB7CA6 /* Badges.swift */; }; + 081456332547740600BB7CA6 /* Badges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081456302547740600BB7CA6 /* Badges.swift */; }; + 081456342547740600BB7CA6 /* Badges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081456302547740600BB7CA6 /* Badges.swift */; }; + 081456352547740600BB7CA6 /* Badges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081456302547740600BB7CA6 /* Badges.swift */; }; 081DAA1F1E099A3A00018623 /* DownloadFooterSectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 081DAA1D1E099A3A00018623 /* DownloadFooterSectionView.m */; }; 081DAA211E099DAE00018623 /* DownloadFooterSectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 081DAA1D1E099A3A00018623 /* DownloadFooterSectionView.m */; }; 081DAA221E099DAF00018623 /* DownloadFooterSectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 081DAA1D1E099A3A00018623 /* DownloadFooterSectionView.m */; }; @@ -72,6 +77,11 @@ 0820930A208F522B00711DE4 /* PushService.m in Sources */ = {isa = PBXBuildFile; fileRef = 08209307208F522A00711DE4 /* PushService.m */; }; 0820930B208F522B00711DE4 /* PushService.m in Sources */ = {isa = PBXBuildFile; fileRef = 08209307208F522A00711DE4 /* PushService.m */; }; 0820930C208F522B00711DE4 /* PushService.m in Sources */ = {isa = PBXBuildFile; fileRef = 08209307208F522A00711DE4 /* PushService.m */; }; + 082099F525780A7F0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 082099F425780A7F0077CF8B /* SRGAnalyticsSwiftUI */; }; + 08209A0B25780AAA0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 08209A0A25780AAA0077CF8B /* SRGAnalyticsSwiftUI */; }; + 08209A0D25780ABE0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 08209A0C25780ABE0077CF8B /* SRGAnalyticsSwiftUI */; }; + 08209A0F25780AC50077CF8B /* SRGAnalyticsSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 08209A0E25780AC50077CF8B /* SRGAnalyticsSwiftUI */; }; + 08209A1125780ACB0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 08209A1025780ACB0077CF8B /* SRGAnalyticsSwiftUI */; }; 0821D53E227F5ED300FC091F /* DeepLinkService.m in Sources */ = {isa = PBXBuildFile; fileRef = 0821D53D227F5ED300FC091F /* DeepLinkService.m */; }; 0821D53F227F5ED300FC091F /* DeepLinkService.m in Sources */ = {isa = PBXBuildFile; fileRef = 0821D53D227F5ED300FC091F /* DeepLinkService.m */; }; 0821D540227F5ED300FC091F /* DeepLinkService.m in Sources */ = {isa = PBXBuildFile; fileRef = 0821D53D227F5ED300FC091F /* DeepLinkService.m */; }; @@ -142,6 +152,11 @@ 0841E70822F0953A009D8304 /* SRGDay+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0841E70522F0953A009D8304 /* SRGDay+PlaySRG.m */; }; 0841E70922F0953A009D8304 /* SRGDay+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0841E70522F0953A009D8304 /* SRGDay+PlaySRG.m */; }; 0841E70A22F0953A009D8304 /* SRGDay+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0841E70522F0953A009D8304 /* SRGDay+PlaySRG.m */; }; + 084F0968254861A0009DEDD2 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081454BA2546CDD500BB7CA6 /* SwiftMessages */; }; + 084F0969254861A0009DEDD2 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455132546CE4300BB7CA6 /* SwiftMessages */; }; + 084F096A254861A0009DEDD2 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455152546CE4F00BB7CA6 /* SwiftMessages */; }; + 084F096B254861A0009DEDD2 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455172546CE5700BB7CA6 /* SwiftMessages */; }; + 084F096C254861A0009DEDD2 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 081455192546CE5F00BB7CA6 /* SwiftMessages */; }; 084F94B022D3B77700D2D3A5 /* SearchSettingMultiSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 084F94AE22D3B77600D2D3A5 /* SearchSettingMultiSelectionViewController.m */; }; 084F94B122D3B77700D2D3A5 /* SearchSettingMultiSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 084F94AE22D3B77600D2D3A5 /* SearchSettingMultiSelectionViewController.m */; }; 084F94B222D3B77700D2D3A5 /* SearchSettingMultiSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 084F94AE22D3B77600D2D3A5 /* SearchSettingMultiSelectionViewController.m */; }; @@ -172,6 +187,11 @@ 0859ADC822D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 0859ADC522D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m */; }; 0859ADC922D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 0859ADC522D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m */; }; 0859ADCA22D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 0859ADC522D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m */; }; + 085C2B09256472F800F57D38 /* ShowsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C2B08256472F700F57D38 /* ShowsModel.swift */; }; + 085C2B0A256472F800F57D38 /* ShowsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C2B08256472F700F57D38 /* ShowsModel.swift */; }; + 085C2B0B256472F800F57D38 /* ShowsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C2B08256472F700F57D38 /* ShowsModel.swift */; }; + 085C2B0C256472F800F57D38 /* ShowsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C2B08256472F700F57D38 /* ShowsModel.swift */; }; + 085C2B0D256472F800F57D38 /* ShowsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C2B08256472F700F57D38 /* ShowsModel.swift */; }; 08602BF72213786B0081D166 /* HomeShowsAccessTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 08602BF42213786A0081D166 /* HomeShowsAccessTableViewCell.m */; }; 08602BF82213786B0081D166 /* HomeShowsAccessTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 08602BF42213786A0081D166 /* HomeShowsAccessTableViewCell.m */; }; 08602BF92213786B0081D166 /* HomeShowsAccessTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 08602BF42213786A0081D166 /* HomeShowsAccessTableViewCell.m */; }; @@ -197,6 +217,11 @@ 086321112258C6D000C719A6 /* WatchLaterTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 086320FA2258C6CF00C719A6 /* WatchLaterTableViewCell.xib */; }; 086321122258C6D000C719A6 /* WatchLaterTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 086320FA2258C6CF00C719A6 /* WatchLaterTableViewCell.xib */; }; 086321132258C6D000C719A6 /* WatchLaterTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 086320FA2258C6CF00C719A6 /* WatchLaterTableViewCell.xib */; }; + 086499B224F69FF20027373E /* MediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086499B124F69FF20027373E /* MediaDetailView.swift */; }; + 086499B324F69FF20027373E /* MediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086499B124F69FF20027373E /* MediaDetailView.swift */; }; + 086499B424F69FF20027373E /* MediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086499B124F69FF20027373E /* MediaDetailView.swift */; }; + 086499B524F69FF20027373E /* MediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086499B124F69FF20027373E /* MediaDetailView.swift */; }; + 086499B624F69FF20027373E /* MediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086499B124F69FF20027373E /* MediaDetailView.swift */; }; 086BDE151EA63D3800965F45 /* PlayMiniPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 086BDE131EA63D3800965F45 /* PlayMiniPlayerView.m */; }; 086BDE161EA63D3800965F45 /* PlayMiniPlayerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 086BDE141EA63D3800965F45 /* PlayMiniPlayerView.xib */; }; 086BDE171EA63D5A00965F45 /* PlayMiniPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 086BDE131EA63D3800965F45 /* PlayMiniPlayerView.m */; }; @@ -227,6 +252,11 @@ 086F8D6B22984B29001BE2F4 /* FavoriteTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 086F8D5322984B29001BE2F4 /* FavoriteTableViewCell.m */; }; 086F8D6C22984B29001BE2F4 /* FavoriteTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 086F8D5322984B29001BE2F4 /* FavoriteTableViewCell.m */; }; 086F8D6D22984B29001BE2F4 /* FavoriteTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 086F8D5322984B29001BE2F4 /* FavoriteTableViewCell.m */; }; + 08742C5925481CC8006E2DCE /* ShowDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08742C5825481CC8006E2DCE /* ShowDetailView.swift */; }; + 08742C5A25481CC8006E2DCE /* ShowDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08742C5825481CC8006E2DCE /* ShowDetailView.swift */; }; + 08742C5B25481CC8006E2DCE /* ShowDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08742C5825481CC8006E2DCE /* ShowDetailView.swift */; }; + 08742C5C25481CC8006E2DCE /* ShowDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08742C5825481CC8006E2DCE /* ShowDetailView.swift */; }; + 08742C5D25481CC8006E2DCE /* ShowDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08742C5825481CC8006E2DCE /* ShowDetailView.swift */; }; 08754F40201BAFD700458F3A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 08754F3D201BAFCC00458F3A /* InfoPlist.strings */; }; 08754F44201BAFF500458F3A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 08754F41201BAFF100458F3A /* InfoPlist.strings */; }; 08754F48201BB00400458F3A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 08754F45201BB00000458F3A /* InfoPlist.strings */; }; @@ -347,6 +377,11 @@ 08B77AF7240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 08B77AEF240A86FC00A3BC3B /* AccessibilityIdentifierConstants.m */; }; 08B77AF8240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 08B77AEF240A86FC00A3BC3B /* AccessibilityIdentifierConstants.m */; }; 08B77AF9240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 08B77AEF240A86FC00A3BC3B /* AccessibilityIdentifierConstants.m */; }; + 08B971CD256EF3FF00195901 /* AnalyticsConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE1B4961DCB84F00094D5BA /* AnalyticsConstants.m */; }; + 08B971E2256EF40000195901 /* AnalyticsConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE1B4961DCB84F00094D5BA /* AnalyticsConstants.m */; }; + 08B971E3256EF40100195901 /* AnalyticsConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE1B4961DCB84F00094D5BA /* AnalyticsConstants.m */; }; + 08B971E4256EF40200195901 /* AnalyticsConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE1B4961DCB84F00094D5BA /* AnalyticsConstants.m */; }; + 08B971E5256EF40300195901 /* AnalyticsConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE1B4961DCB84F00094D5BA /* AnalyticsConstants.m */; }; 08BAABDA1E7613F0007644D7 /* HomeMediasViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08BAABD81E7613F0007644D7 /* HomeMediasViewController.m */; }; 08BAABDC1E76141E007644D7 /* HomeMediasViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08BAABD81E7613F0007644D7 /* HomeMediasViewController.m */; }; 08BAABDE1E76141F007644D7 /* HomeMediasViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08BAABD81E7613F0007644D7 /* HomeMediasViewController.m */; }; @@ -417,6 +452,11 @@ 08DD9D6A1FFE6EFA00115906 /* ModuleHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 08DD9D581FFE6EDA00115906 /* ModuleHeaderView.m */; }; 08DD9D6B1FFE6EFA00115906 /* ModuleHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 08DD9D541FFE6EDA00115906 /* ModuleHeaderView.xib */; }; 08DD9D6C1FFE6EFA00115906 /* ModuleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08DD9D591FFE6EDA00115906 /* ModuleViewController.m */; }; + 08FD789D253E0D3500EF302A /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */; }; + 08FD789E253E0D3500EF302A /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */; }; + 08FD789F253E0D3500EF302A /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */; }; + 08FD78A0253E0D3500EF302A /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */; }; + 08FD78A1253E0D3500EF302A /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */; }; 0B2164E33BB0632C4F3DADED /* libPods-Play SRG-tvOS-Play RTR TV.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 624E718ECC7F02E540440901 /* libPods-Play SRG-tvOS-Play RTR TV.a */; }; 2B917038BA0F995D9AC8DC22 /* libPods-Play SRG-iOS-Play RSI.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780F919C25BC0944528A5FD /* libPods-Play SRG-iOS-Play RSI.a */; }; 2F014EC1298A005CED1AB5D8 /* libPods-Play SRG-tvOS-Play RTS TV.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C7CC3BC704FD48424BB497B2 /* libPods-Play SRG-tvOS-Play RTS TV.a */; }; @@ -463,11 +503,21 @@ 6F0CFB6020C94E8E006B2CE4 /* Play RSI notification service extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6F0CFB5820C94E8E006B2CE4 /* Play RSI notification service extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6F0CFB7320C94EC8006B2CE4 /* Play RTR notification service extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6F0CFB6B20C94EC8006B2CE4 /* Play RTR notification service extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6F0CFB8620C94EE5006B2CE4 /* Play SWI notification service extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6F0CFB7E20C94EE5006B2CE4 /* Play SWI notification service extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 6F0E39CD25558A0F004C3A12 /* ShowDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */; }; + 6F0E39CE25558A0F004C3A12 /* ShowDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */; }; + 6F0E39CF25558A0F004C3A12 /* ShowDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */; }; + 6F0E39D025558A0F004C3A12 /* ShowDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */; }; + 6F0E39D125558A0F004C3A12 /* ShowDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */; }; 6F0E8D981F14F62F002014C3 /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; 6F0E8D991F14F62F002014C3 /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; 6F0E8D9A1F14F62F002014C3 /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; 6F0E8D9B1F14F62F002014C3 /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; 6F0E8D9C1F14F62F002014C3 /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; + 6F0ED544252B00B000ECE97B /* LabeledButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0ED543252B00B000ECE97B /* LabeledButton.swift */; }; + 6F0ED545252B00B000ECE97B /* LabeledButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0ED543252B00B000ECE97B /* LabeledButton.swift */; }; + 6F0ED546252B00B000ECE97B /* LabeledButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0ED543252B00B000ECE97B /* LabeledButton.swift */; }; + 6F0ED547252B00B000ECE97B /* LabeledButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0ED543252B00B000ECE97B /* LabeledButton.swift */; }; + 6F0ED548252B00B000ECE97B /* LabeledButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0ED543252B00B000ECE97B /* LabeledButton.swift */; }; 6F0EDA652448B0D800F0FED2 /* RefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0EDA632448B0D700F0FED2 /* RefreshControl.m */; }; 6F0EDA662448B0D800F0FED2 /* RefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0EDA632448B0D700F0FED2 /* RefreshControl.m */; }; 6F0EDA672448B0D800F0FED2 /* RefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0EDA632448B0D700F0FED2 /* RefreshControl.m */; }; @@ -508,6 +558,16 @@ 6F12E4E922D8676600BC1718 /* SearchHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F12E4E122D8676500BC1718 /* SearchHeaderView.xib */; }; 6F12E4EA22D8676600BC1718 /* SearchHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F12E4E122D8676500BC1718 /* SearchHeaderView.xib */; }; 6F12E4EB22D8676600BC1718 /* SearchHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F12E4E122D8676500BC1718 /* SearchHeaderView.xib */; }; + 6F151E27256BF5CF009082F8 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E26256BF5CF009082F8 /* ProgressBar.swift */; }; + 6F151E28256BF5CF009082F8 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E26256BF5CF009082F8 /* ProgressBar.swift */; }; + 6F151E29256BF5CF009082F8 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E26256BF5CF009082F8 /* ProgressBar.swift */; }; + 6F151E2A256BF5CF009082F8 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E26256BF5CF009082F8 /* ProgressBar.swift */; }; + 6F151E2B256BF5CF009082F8 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E26256BF5CF009082F8 /* ProgressBar.swift */; }; + 6F151E55256BFCEB009082F8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E54256BFCEB009082F8 /* Extensions.swift */; }; + 6F151E56256BFCEB009082F8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E54256BFCEB009082F8 /* Extensions.swift */; }; + 6F151E57256BFCEB009082F8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E54256BFCEB009082F8 /* Extensions.swift */; }; + 6F151E58256BFCEB009082F8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E54256BFCEB009082F8 /* Extensions.swift */; }; + 6F151E59256BFCEB009082F8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F151E54256BFCEB009082F8 /* Extensions.swift */; }; 6F167F4A248197C700B8E7F1 /* HomeTopicListTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F167F49248197C700B8E7F1 /* HomeTopicListTableViewCell.m */; }; 6F167F4B248197C700B8E7F1 /* HomeTopicListTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F167F49248197C700B8E7F1 /* HomeTopicListTableViewCell.m */; }; 6F167F4C248197C700B8E7F1 /* HomeTopicListTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F167F49248197C700B8E7F1 /* HomeTopicListTableViewCell.m */; }; @@ -543,11 +603,6 @@ 6F1F92A022A539D40026BFAC /* SearchShowListCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F1F929D22A539D40026BFAC /* SearchShowListCollectionViewCell.m */; }; 6F1F92A122A539D40026BFAC /* SearchShowListCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F1F929D22A539D40026BFAC /* SearchShowListCollectionViewCell.m */; }; 6F1F92A222A539D40026BFAC /* SearchShowListCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F1F929D22A539D40026BFAC /* SearchShowListCollectionViewCell.m */; }; - 6F24913325248CDB00621C27 /* FirebaseStorageFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */; }; - 6F24913425248CDB00621C27 /* FirebaseStorageFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */; }; - 6F24913525248CDB00621C27 /* FirebaseStorageFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */; }; - 6F24913625248CDB00621C27 /* FirebaseStorageFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */; }; - 6F24913725248CDB00621C27 /* FirebaseStorageFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */; }; 6F2544F52521BAA400F9F4FE /* SRGUserData in Frameworks */ = {isa = PBXBuildFile; productRef = 6F2544F42521BAA400F9F4FE /* SRGUserData */; }; 6F2545B52521C14E00F9F4FE /* SRGLetterbox in Frameworks */ = {isa = PBXBuildFile; productRef = 6F2545B42521C14E00F9F4FE /* SRGLetterbox */; }; 6F2545B72521C15E00F9F4FE /* SRGLetterbox in Frameworks */ = {isa = PBXBuildFile; productRef = 6F2545B62521C15E00F9F4FE /* SRGLetterbox */; }; @@ -578,6 +633,11 @@ 6F2961FE2006186100CAB0E4 /* placeholder_media_list-180.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6F2961FB2006186000CAB0E4 /* placeholder_media_list-180.pdf */; }; 6F2961FF2006186100CAB0E4 /* placeholder_media_list-180.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6F2961FB2006186000CAB0E4 /* placeholder_media_list-180.pdf */; }; 6F2962002006186100CAB0E4 /* placeholder_media_list-180.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6F2961FB2006186000CAB0E4 /* placeholder_media_list-180.pdf */; }; + 6F298860255ADDCD007A8E2B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29885F255ADDCD007A8E2B /* Errors.swift */; }; + 6F298861255ADDCD007A8E2B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29885F255ADDCD007A8E2B /* Errors.swift */; }; + 6F298862255ADDCD007A8E2B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29885F255ADDCD007A8E2B /* Errors.swift */; }; + 6F298863255ADDCD007A8E2B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29885F255ADDCD007A8E2B /* Errors.swift */; }; + 6F298864255ADDCD007A8E2B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29885F255ADDCD007A8E2B /* Errors.swift */; }; 6F2AB1B42487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB1B32487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift */; }; 6F2AB1B52487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB1B32487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift */; }; 6F2AB1B62487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB1B32487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift */; }; @@ -632,6 +692,11 @@ 6F3B0229245AAF6B00C5A8D7 /* ProgramTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F3B0226245AAF6B00C5A8D7 /* ProgramTableViewCell.xib */; }; 6F3B022A245AAF6B00C5A8D7 /* ProgramTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F3B0226245AAF6B00C5A8D7 /* ProgramTableViewCell.xib */; }; 6F3B022B245AAF6B00C5A8D7 /* ProgramTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F3B0226245AAF6B00C5A8D7 /* ProgramTableViewCell.xib */; }; + 6F3D67E1256827DD0030922B /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; + 6F3D67F6256827DE0030922B /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; + 6F3D67F7256827DF0030922B /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; + 6F3D680C256827DF0030922B /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; + 6F3D680D256827DF0030922B /* SRGChannel+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */; }; 6F3E0A0123D055DD009B00C1 /* SettingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E0A0023D055DD009B00C1 /* SettingTableViewCell.m */; }; 6F3E0A0223D055DD009B00C1 /* SettingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E0A0023D055DD009B00C1 /* SettingTableViewCell.m */; }; 6F3E0A0323D055DD009B00C1 /* SettingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E0A0023D055DD009B00C1 /* SettingTableViewCell.m */; }; @@ -647,11 +712,11 @@ 6F3F1AB6250286DC000FF4DD /* UIColor+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606B1EB37D60003021EA /* UIColor+PlaySRG.m */; }; 6F3F1AB7250286DC000FF4DD /* UIColor+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606B1EB37D60003021EA /* UIColor+PlaySRG.m */; }; 6F3F1AB8250286DD000FF4DD /* UIColor+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606B1EB37D60003021EA /* UIColor+PlaySRG.m */; }; - 6F3F1ABA25060496000FF4DD /* MediaVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisual.swift */; }; - 6F3F1ABB25060496000FF4DD /* MediaVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisual.swift */; }; - 6F3F1ABC25060496000FF4DD /* MediaVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisual.swift */; }; - 6F3F1ABD25060496000FF4DD /* MediaVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisual.swift */; }; - 6F3F1ABE25060496000FF4DD /* MediaVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisual.swift */; }; + 6F3F1ABA25060496000FF4DD /* MediaVisualView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */; }; + 6F3F1ABB25060496000FF4DD /* MediaVisualView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */; }; + 6F3F1ABC25060496000FF4DD /* MediaVisualView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */; }; + 6F3F1ABD25060496000FF4DD /* MediaVisualView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */; }; + 6F3F1ABE25060496000FF4DD /* MediaVisualView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */; }; 6F40920022DCF43D005F3850 /* Previewing.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4091FF22DCF43D005F3850 /* Previewing.m */; }; 6F40920122DCF43D005F3850 /* Previewing.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4091FF22DCF43D005F3850 /* Previewing.m */; }; 6F40920222DCF43D005F3850 /* Previewing.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4091FF22DCF43D005F3850 /* Previewing.m */; }; @@ -667,6 +732,16 @@ 6F40920F22DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F40920722DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib */; }; 6F40921022DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F40920722DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib */; }; 6F40921122DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F40920722DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib */; }; + 6F426B142567FC6F00DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA6C0622458064C00518DE6 /* SRGProgramComposition+PlaySRG.m */; }; + 6F426B292567FC7000DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA6C0622458064C00518DE6 /* SRGProgramComposition+PlaySRG.m */; }; + 6F426B2A2567FC7100DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA6C0622458064C00518DE6 /* SRGProgramComposition+PlaySRG.m */; }; + 6F426B2B2567FC7100DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA6C0622458064C00518DE6 /* SRGProgramComposition+PlaySRG.m */; }; + 6F426B2C2567FC7200DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA6C0622458064C00518DE6 /* SRGProgramComposition+PlaySRG.m */; }; + 6F426B4225680A8F00DD22AB /* BlockingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */; }; + 6F426B4325680A8F00DD22AB /* BlockingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */; }; + 6F426B4425680A8F00DD22AB /* BlockingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */; }; + 6F426B4525680A8F00DD22AB /* BlockingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */; }; + 6F426B4625680A8F00DD22AB /* BlockingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */; }; 6F475FB41EB37BC6003021EA /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F475F8D1EB37BC6003021EA /* BaseViewController.m */; }; 6F475FB51EB37BC6003021EA /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F475F8D1EB37BC6003021EA /* BaseViewController.m */; }; 6F475FB61EB37BC6003021EA /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F475F8D1EB37BC6003021EA /* BaseViewController.m */; }; @@ -808,11 +883,6 @@ 6F488ACD22EED363002B1150 /* PlayAccessibilityFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F488ACA22EED363002B1150 /* PlayAccessibilityFormatter.m */; }; 6F488ACE22EED363002B1150 /* PlayAccessibilityFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F488ACA22EED363002B1150 /* PlayAccessibilityFormatter.m */; }; 6F488ACF22EED363002B1150 /* PlayAccessibilityFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F488ACA22EED363002B1150 /* PlayAccessibilityFormatter.m */; }; - 6F4BCBE124EFF7C3001ABA17 /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */; }; - 6F4BCBE224EFF7C3001ABA17 /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */; }; - 6F4BCBE324EFF7C3001ABA17 /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */; }; - 6F4BCBE424EFF7C3001ABA17 /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */; }; - 6F4BCBE524EFF7C3001ABA17 /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */; }; 6F4C0A7520DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4C0A7420DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m */; }; 6F4C0A7620DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4C0A7420DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m */; }; 6F4C0A7720DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4C0A7420DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m */; }; @@ -828,6 +898,11 @@ 6F4EA13A1EE034D500BEC4DA /* Accessibility.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6F4EA1381EE034D500BEC4DA /* Accessibility.strings */; }; 6F4EA13D1EE034E200BEC4DA /* Accessibility.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6F4EA13B1EE034E200BEC4DA /* Accessibility.strings */; }; 6F4EA1401EE034EF00BEC4DA /* Accessibility.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6F4EA13E1EE034EF00BEC4DA /* Accessibility.strings */; }; + 6F4F78AF252AF7670011DBCA /* DurationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */; }; + 6F4F78B0252AF7670011DBCA /* DurationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */; }; + 6F4F78B1252AF7670011DBCA /* DurationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */; }; + 6F4F78B2252AF7670011DBCA /* DurationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */; }; + 6F4F78B3252AF7670011DBCA /* DurationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */; }; 6F4FCAF724D06FF7002675EF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F331CE624D06B8200C096AB /* AppDelegate.swift */; }; 6F4FCAF824D06FF7002675EF /* AudiosView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F331CE824D06B8200C096AB /* AudiosView.swift */; }; 6F4FCAF924D06FF7002675EF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F331CE624D06B8200C096AB /* AppDelegate.swift */; }; @@ -879,6 +954,12 @@ 6F58664C1DD226EE005DAFE4 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 6F5866491DD226EE005DAFE4 /* Settings.bundle */; }; 6F58664D1DD226EE005DAFE4 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 6F5866491DD226EE005DAFE4 /* Settings.bundle */; }; 6F58664E1DD226EE005DAFE4 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 6F5866491DD226EE005DAFE4 /* Settings.bundle */; }; + 6F5A7914256ED7E000E884F2 /* History.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F340F51220DA99B00778CA3 /* History.m */; }; + 6F5A792A256EE61200E884F2 /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A7929256EE61200E884F2 /* HistoryModel.swift */; }; + 6F5A792B256EE61200E884F2 /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A7929256EE61200E884F2 /* HistoryModel.swift */; }; + 6F5A792C256EE61200E884F2 /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A7929256EE61200E884F2 /* HistoryModel.swift */; }; + 6F5A792D256EE61200E884F2 /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A7929256EE61200E884F2 /* HistoryModel.swift */; }; + 6F5A792E256EE61200E884F2 /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A7929256EE61200E884F2 /* HistoryModel.swift */; }; 6F5AA59725024C4800718420 /* UIImage+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606F1EB37D60003021EA /* UIImage+PlaySRG.m */; }; 6F5AA59825024C4900718420 /* UIImage+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606F1EB37D60003021EA /* UIImage+PlaySRG.m */; }; 6F5AA59925024C4900718420 /* UIImage+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606F1EB37D60003021EA /* UIImage+PlaySRG.m */; }; @@ -904,6 +985,15 @@ 6F5F4FCB1DB10CFD0011CCA3 /* RelatedContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F5F4FC31DB10CFD0011CCA3 /* RelatedContentView.xib */; }; 6F5F4FCC1DB10CFD0011CCA3 /* RelatedContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F5F4FC31DB10CFD0011CCA3 /* RelatedContentView.xib */; }; 6F5F4FCD1DB10CFD0011CCA3 /* RelatedContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F5F4FC31DB10CFD0011CCA3 /* RelatedContentView.xib */; }; + 6F61CCAA256E419F00E42E31 /* History.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F340F51220DA99B00778CA3 /* History.m */; }; + 6F61CCAB256E419F00E42E31 /* History.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F340F51220DA99B00778CA3 /* History.m */; }; + 6F61CCAC256E41A000E42E31 /* History.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F340F51220DA99B00778CA3 /* History.m */; }; + 6F61CCAD256E41A000E42E31 /* History.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F340F51220DA99B00778CA3 /* History.m */; }; + 6F61CCC3256E41EE00E42E31 /* SRGUserData in Frameworks */ = {isa = PBXBuildFile; productRef = 6F61CCC2256E41EE00E42E31 /* SRGUserData */; }; + 6F61CCC5256E41F500E42E31 /* SRGUserData in Frameworks */ = {isa = PBXBuildFile; productRef = 6F61CCC4256E41F500E42E31 /* SRGUserData */; }; + 6F61CCC7256E41FB00E42E31 /* SRGUserData in Frameworks */ = {isa = PBXBuildFile; productRef = 6F61CCC6256E41FB00E42E31 /* SRGUserData */; }; + 6F61CCC9256E420200E42E31 /* SRGUserData in Frameworks */ = {isa = PBXBuildFile; productRef = 6F61CCC8256E420200E42E31 /* SRGUserData */; }; + 6F61CCCB256E420800E42E31 /* SRGUserData in Frameworks */ = {isa = PBXBuildFile; productRef = 6F61CCCA256E420800E42E31 /* SRGUserData */; }; 6F6B642E21A70FAA00E207FC /* HomeStatusHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F6B642C21A70FAA00E207FC /* HomeStatusHeaderView.m */; }; 6F6B642F21A70FAA00E207FC /* HomeStatusHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F6B642C21A70FAA00E207FC /* HomeStatusHeaderView.m */; }; 6F6B643021A70FAA00E207FC /* HomeStatusHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F6B642C21A70FAA00E207FC /* HomeStatusHeaderView.m */; }; @@ -914,11 +1004,21 @@ 6F6B643521A70FAA00E207FC /* HomeStatusHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F6B642D21A70FAA00E207FC /* HomeStatusHeaderView.xib */; }; 6F6B643621A70FAA00E207FC /* HomeStatusHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F6B642D21A70FAA00E207FC /* HomeStatusHeaderView.xib */; }; 6F6B643721A70FAA00E207FC /* HomeStatusHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6F6B642D21A70FAA00E207FC /* HomeStatusHeaderView.xib */; }; + 6F6C0FA8257AAF240077322C /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6F6C0FBD257AAF250077322C /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6F6C0FBE257AAF250077322C /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6F6C0FBF257AAF260077322C /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6F6C0FC0257AAF270077322C /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; 6F7218131DBE75AC00575072 /* PreviewingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7218121DBE75AC00575072 /* PreviewingDelegate.m */; }; 6F7218141DBE75AC00575072 /* PreviewingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7218121DBE75AC00575072 /* PreviewingDelegate.m */; }; 6F7218151DBE75AC00575072 /* PreviewingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7218121DBE75AC00575072 /* PreviewingDelegate.m */; }; 6F7218161DBE75AC00575072 /* PreviewingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7218121DBE75AC00575072 /* PreviewingDelegate.m */; }; 6F7218171DBE75AC00575072 /* PreviewingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7218121DBE75AC00575072 /* PreviewingDelegate.m */; }; + 6F79E0AA2541647400A28E79 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F79E0A92541647400A28E79 /* Colors.swift */; }; + 6F79E0AB2541647400A28E79 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F79E0A92541647400A28E79 /* Colors.swift */; }; + 6F79E0AC2541647400A28E79 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F79E0A92541647400A28E79 /* Colors.swift */; }; + 6F79E0AD2541647400A28E79 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F79E0A92541647400A28E79 /* Colors.swift */; }; + 6F79E0AE2541647400A28E79 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F79E0A92541647400A28E79 /* Colors.swift */; }; 6F7C35B523708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7C35B423708E8A00259BE7 /* SRGResource+PlaySRG.m */; }; 6F7C35B623708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7C35B423708E8A00259BE7 /* SRGResource+PlaySRG.m */; }; 6F7C35B723708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7C35B423708E8A00259BE7 /* SRGResource+PlaySRG.m */; }; @@ -969,6 +1069,36 @@ 6F85E4B91EEA8F9B00552256 /* UIView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F85E4B61EEA8F9B00552256 /* UIView+PlaySRG.m */; }; 6F85E4BA1EEA8F9B00552256 /* UIView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F85E4B61EEA8F9B00552256 /* UIView+PlaySRG.m */; }; 6F85E4BB1EEA8F9B00552256 /* UIView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F85E4B61EEA8F9B00552256 /* UIView+PlaySRG.m */; }; + 6F85F7502567ED0D00AC8286 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; + 6F85F7652567ED0E00AC8286 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; + 6F85F7662567ED0F00AC8286 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; + 6F85F7672567ED0F00AC8286 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; + 6F85F7682567ED0F00AC8286 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; + 6F85F77D2567ED1500AC8286 /* ChannelServiceSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F56F9F3247C407000B2387B /* ChannelServiceSetup.m */; }; + 6F85F77E2567ED1500AC8286 /* ChannelServiceSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F56F9F3247C407000B2387B /* ChannelServiceSetup.m */; }; + 6F85F77F2567ED1600AC8286 /* ChannelServiceSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F56F9F3247C407000B2387B /* ChannelServiceSetup.m */; }; + 6F85F7802567ED1600AC8286 /* ChannelServiceSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F56F9F3247C407000B2387B /* ChannelServiceSetup.m */; }; + 6F85F7812567ED1700AC8286 /* ChannelServiceSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F56F9F3247C407000B2387B /* ChannelServiceSetup.m */; }; + 6F85F7822567ED2000AC8286 /* ForegroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD2A35323C7628000456DCB /* ForegroundTimer.m */; }; + 6F85F7972567ED2000AC8286 /* ForegroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD2A35323C7628000456DCB /* ForegroundTimer.m */; }; + 6F85F7AC2567ED2100AC8286 /* ForegroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD2A35323C7628000456DCB /* ForegroundTimer.m */; }; + 6F85F7AD2567ED2100AC8286 /* ForegroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD2A35323C7628000456DCB /* ForegroundTimer.m */; }; + 6F85F7AE2567ED2300AC8286 /* ForegroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD2A35323C7628000456DCB /* ForegroundTimer.m */; }; + 6F85F7AF2567ED2A00AC8286 /* NSTimer+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 089EA010201148180070E11A /* NSTimer+PlaySRG.m */; }; + 6F85F7C42567ED2B00AC8286 /* NSTimer+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 089EA010201148180070E11A /* NSTimer+PlaySRG.m */; }; + 6F85F7C52567ED2C00AC8286 /* NSTimer+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 089EA010201148180070E11A /* NSTimer+PlaySRG.m */; }; + 6F85F7DA2567ED2C00AC8286 /* NSTimer+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 089EA010201148180070E11A /* NSTimer+PlaySRG.m */; }; + 6F85F7DB2567ED2C00AC8286 /* NSTimer+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 089EA010201148180070E11A /* NSTimer+PlaySRG.m */; }; + 6F85F7F02567ED3C00AC8286 /* SRGProgram+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8866A1EC58BD5000BC3FF /* SRGProgram+PlaySRG.m */; }; + 6F85F8052567ED3D00AC8286 /* SRGProgram+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8866A1EC58BD5000BC3FF /* SRGProgram+PlaySRG.m */; }; + 6F85F8062567ED3D00AC8286 /* SRGProgram+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8866A1EC58BD5000BC3FF /* SRGProgram+PlaySRG.m */; }; + 6F85F8072567ED3E00AC8286 /* SRGProgram+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8866A1EC58BD5000BC3FF /* SRGProgram+PlaySRG.m */; }; + 6F85F8082567ED3E00AC8286 /* SRGProgram+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8866A1EC58BD5000BC3FF /* SRGProgram+PlaySRG.m */; }; + 6F85F81E2567F0D100AC8286 /* LiveMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */; }; + 6F85F81F2567F0D100AC8286 /* LiveMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */; }; + 6F85F8202567F0D100AC8286 /* LiveMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */; }; + 6F85F8212567F0D100AC8286 /* LiveMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */; }; + 6F85F8222567F0D100AC8286 /* LiveMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */; }; 6F861AF81DAE65A600C46102 /* SRGMediaPlayerDesignables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F861AF71DAE65A600C46102 /* SRGMediaPlayerDesignables.m */; }; 6F861AF91DAE65A600C46102 /* SRGMediaPlayerDesignables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F861AF71DAE65A600C46102 /* SRGMediaPlayerDesignables.m */; }; 6F861AFA1DAE65A600C46102 /* SRGMediaPlayerDesignables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F861AF71DAE65A600C46102 /* SRGMediaPlayerDesignables.m */; }; @@ -1009,6 +1139,11 @@ 6F93DE1C20AE9BE600B71572 /* OnboardingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93DE1920AE9BE600B71572 /* OnboardingTableViewCell.m */; }; 6F93DE1D20AE9BE600B71572 /* OnboardingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93DE1920AE9BE600B71572 /* OnboardingTableViewCell.m */; }; 6F93DE1E20AE9BE600B71572 /* OnboardingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93DE1920AE9BE600B71572 /* OnboardingTableViewCell.m */; }; + 6F975739256EEEC4004DA73C /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F975738256EEEC4004DA73C /* HistoryView.swift */; }; + 6F97573A256EEEC4004DA73C /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F975738256EEEC4004DA73C /* HistoryView.swift */; }; + 6F97573B256EEEC4004DA73C /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F975738256EEEC4004DA73C /* HistoryView.swift */; }; + 6F97573C256EEEC4004DA73C /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F975738256EEEC4004DA73C /* HistoryView.swift */; }; + 6F97573D256EEEC4004DA73C /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F975738256EEEC4004DA73C /* HistoryView.swift */; }; 6F982B93244F1E1200C0386E /* TableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F982B92244F1E1200C0386E /* TableView.m */; }; 6F982B94244F1E1200C0386E /* TableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F982B92244F1E1200C0386E /* TableView.m */; }; 6F982B95244F1E1200C0386E /* TableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F982B92244F1E1200C0386E /* TableView.m */; }; @@ -1024,6 +1159,11 @@ 6F9D2745203AD99C00FDE899 /* Playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9D2742203AD99C00FDE899 /* Playlist.m */; }; 6F9D2746203AD99C00FDE899 /* Playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9D2742203AD99C00FDE899 /* Playlist.m */; }; 6F9D2747203AD99C00FDE899 /* Playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9D2742203AD99C00FDE899 /* Playlist.m */; }; + 6FA14535254729DA006E8D3B /* FocusableRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA14534254729DA006E8D3B /* FocusableRegion.swift */; }; + 6FA14536254729DA006E8D3B /* FocusableRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA14534254729DA006E8D3B /* FocusableRegion.swift */; }; + 6FA14537254729DA006E8D3B /* FocusableRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA14534254729DA006E8D3B /* FocusableRegion.swift */; }; + 6FA14538254729DA006E8D3B /* FocusableRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA14534254729DA006E8D3B /* FocusableRegion.swift */; }; + 6FA14539254729DA006E8D3B /* FocusableRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA14534254729DA006E8D3B /* FocusableRegion.swift */; }; 6FA5D15D1F2077B10059E4E2 /* NSString+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA5D15C1F2077B10059E4E2 /* NSString+PlaySRG.m */; }; 6FA5D15E1F2077B10059E4E2 /* NSString+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA5D15C1F2077B10059E4E2 /* NSString+PlaySRG.m */; }; 6FA5D15F1F2077B10059E4E2 /* NSString+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA5D15C1F2077B10059E4E2 /* NSString+PlaySRG.m */; }; @@ -1264,15 +1404,10 @@ 6FD88F8C22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD88F8922D4BC41008859EF /* UISearchBar+PlaySRG.m */; }; 6FD88F8D22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD88F8922D4BC41008859EF /* UISearchBar+PlaySRG.m */; }; 6FD88F8E22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD88F8922D4BC41008859EF /* UISearchBar+PlaySRG.m */; }; - 6FDB2AA524E590EE00FF286E /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AA424E590EE00FF286E /* Nuke */; }; 6FDB2AA724E590EE00FF286E /* FetchImage in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AA624E590EE00FF286E /* FetchImage */; }; - 6FDB2AA924E590F400FF286E /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AA824E590F400FF286E /* Nuke */; }; 6FDB2AAB24E590F400FF286E /* FetchImage in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AAA24E590F400FF286E /* FetchImage */; }; - 6FDB2AAD24E590FC00FF286E /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AAC24E590FC00FF286E /* Nuke */; }; 6FDB2AAF24E590FC00FF286E /* FetchImage in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AAE24E590FC00FF286E /* FetchImage */; }; - 6FDB2AB124E5910400FF286E /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AB024E5910400FF286E /* Nuke */; }; 6FDB2AB324E5910400FF286E /* FetchImage in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AB224E5910400FF286E /* FetchImage */; }; - 6FDB2AB524E5910B00FF286E /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AB424E5910B00FF286E /* Nuke */; }; 6FDB2AB724E5910B00FF286E /* FetchImage in Frameworks */ = {isa = PBXBuildFile; productRef = 6FDB2AB624E5910B00FF286E /* FetchImage */; }; 6FDB2ABA24E5916F00FF286E /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB2AB824E5916F00FF286E /* ImageView.swift */; }; 6FDB2ABB24E5916F00FF286E /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB2AB824E5916F00FF286E /* ImageView.swift */; }; @@ -1349,6 +1484,26 @@ 6FE28775248168A100358CFF /* SongTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6FE28772248168A100358CFF /* SongTableViewCell.xib */; }; 6FE28776248168A100358CFF /* SongTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6FE28772248168A100358CFF /* SongTableViewCell.xib */; }; 6FE28777248168A100358CFF /* SongTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6FE28772248168A100358CFF /* SongTableViewCell.xib */; }; + 6FE4F0CC257A368B00223F22 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */; }; + 6FE4F0CD257A368B00223F22 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */; }; + 6FE4F0CE257A368B00223F22 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */; }; + 6FE4F0CF257A368B00223F22 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */; }; + 6FE4F0D0257A368B00223F22 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */; }; + 6FE4F0F9257A371200223F22 /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6FE4F10E257A371300223F22 /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6FE4F10F257A371400223F22 /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6FE4F124257A371500223F22 /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6FE4F125257A371500223F22 /* NSBundle+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 0827DA1D1F0D36D900A31A42 /* NSBundle+PlaySRG.m */; }; + 6FE4F13A257A372800223F22 /* PlayErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9122BE1DC8708400725EEB /* PlayErrors.m */; }; + 6FE4F13B257A372800223F22 /* PlayErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9122BE1DC8708400725EEB /* PlayErrors.m */; }; + 6FE4F13C257A372800223F22 /* PlayErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9122BE1DC8708400725EEB /* PlayErrors.m */; }; + 6FE4F13D257A372900223F22 /* PlayErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9122BE1DC8708400725EEB /* PlayErrors.m */; }; + 6FE4F13E257A372900223F22 /* PlayErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F9122BE1DC8708400725EEB /* PlayErrors.m */; }; + 6FE4F153257A7D3C00223F22 /* UIImageView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760711EB37D60003021EA /* UIImageView+PlaySRG.m */; }; + 6FE4F154257A7D3C00223F22 /* UIImageView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760711EB37D60003021EA /* UIImageView+PlaySRG.m */; }; + 6FE4F155257A7D3D00223F22 /* UIImageView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760711EB37D60003021EA /* UIImageView+PlaySRG.m */; }; + 6FE4F156257A7D3D00223F22 /* UIImageView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760711EB37D60003021EA /* UIImageView+PlaySRG.m */; }; + 6FE4F157257A7D3D00223F22 /* UIImageView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760711EB37D60003021EA /* UIImageView+PlaySRG.m */; }; 6FE686E11EB9D57400067D40 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; 6FE686E21EB9D57400067D40 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; 6FE686E31EB9D57400067D40 /* ChannelService.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE686E01EB9D57400067D40 /* ChannelService.m */; }; @@ -1364,6 +1519,36 @@ 6FEC91AB21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FEC91A821A6B39A00AA50C8 /* TableLoadMoreFooterView.m */; }; 6FEC91AC21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FEC91A821A6B39A00AA50C8 /* TableLoadMoreFooterView.m */; }; 6FEC91AD21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FEC91A821A6B39A00AA50C8 /* TableLoadMoreFooterView.m */; }; + 6FF129C4256C0A9D0042F446 /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; + 6FF129C5256C0A9E0042F446 /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; + 6FF129C6256C0A9E0042F446 /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; + 6FF129C7256C0A9E0042F446 /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; + 6FF129C8256C0A9F0042F446 /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; + 6FF129DD256C0D370042F446 /* PlayDurationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601A1EB37BD1003021EA /* PlayDurationFormatter.m */; }; + 6FF129F2256C0D370042F446 /* PlayDurationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601A1EB37BD1003021EA /* PlayDurationFormatter.m */; }; + 6FF129F3256C0D380042F446 /* PlayDurationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601A1EB37BD1003021EA /* PlayDurationFormatter.m */; }; + 6FF12A08256C0D390042F446 /* PlayDurationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601A1EB37BD1003021EA /* PlayDurationFormatter.m */; }; + 6FF12A09256C0D390042F446 /* PlayDurationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601A1EB37BD1003021EA /* PlayDurationFormatter.m */; }; + 6FF12A48256C58A70042F446 /* SRGLoggerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6FF12A47256C58A70042F446 /* SRGLoggerSwift */; }; + 6FF12A5E256C58B20042F446 /* SRGLoggerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6FF12A5D256C58B20042F446 /* SRGLoggerSwift */; }; + 6FF12A74256C58BB0042F446 /* SRGLoggerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6FF12A73256C58BB0042F446 /* SRGLoggerSwift */; }; + 6FF12A76256C58C40042F446 /* SRGLoggerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6FF12A75256C58C40042F446 /* SRGLoggerSwift */; }; + 6FF12A8C256C58CC0042F446 /* SRGLoggerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6FF12A8B256C58CC0042F446 /* SRGLoggerSwift */; }; + 6FF12AA2256C58FE0042F446 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF12AA1256C58FE0042F446 /* Logger.swift */; }; + 6FF12AA3256C58FE0042F446 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF12AA1256C58FE0042F446 /* Logger.swift */; }; + 6FF12AA4256C58FE0042F446 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF12AA1256C58FE0042F446 /* Logger.swift */; }; + 6FF12AA5256C58FE0042F446 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF12AA1256C58FE0042F446 /* Logger.swift */; }; + 6FF12AA6256C58FE0042F446 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF12AA1256C58FE0042F446 /* Logger.swift */; }; + 6FF1EA46256569FC005FFEEA /* TopicDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */; }; + 6FF1EA47256569FC005FFEEA /* TopicDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */; }; + 6FF1EA48256569FC005FFEEA /* TopicDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */; }; + 6FF1EA49256569FC005FFEEA /* TopicDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */; }; + 6FF1EA4A256569FC005FFEEA /* TopicDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */; }; + 6FF1EA7425656BE3005FFEEA /* TopicDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */; }; + 6FF1EA7525656BE3005FFEEA /* TopicDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */; }; + 6FF1EA7625656BE3005FFEEA /* TopicDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */; }; + 6FF1EA7725656BE3005FFEEA /* TopicDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */; }; + 6FF1EA7825656BE3005FFEEA /* TopicDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */; }; 6FF4D4E023D5B07E008B981A /* UIVisualEffectView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FF4D4DF23D5B07E008B981A /* UIVisualEffectView+PlaySRG.m */; }; 6FF4D4E123D5B07E008B981A /* UIVisualEffectView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FF4D4DF23D5B07E008B981A /* UIVisualEffectView+PlaySRG.m */; }; 6FF4D4E223D5B07E008B981A /* UIVisualEffectView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FF4D4DF23D5B07E008B981A /* UIVisualEffectView+PlaySRG.m */; }; @@ -1419,11 +1604,21 @@ 6FFCC5EB24EFC1F700805B0F /* ApplicationConfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 6F1938311EFBFE9E0017B1D1 /* ApplicationConfiguration.json */; }; 6FFCC5EC24EFC1FA00805B0F /* ApplicationConfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 6F1938351EFBFEBC0017B1D1 /* ApplicationConfiguration.json */; }; 6FFCC5ED24EFC1FD00805B0F /* ApplicationConfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 6F1938371EFBFEC80017B1D1 /* ApplicationConfiguration.json */; }; - 6FFF1632250A20EF0053CDA6 /* FocusDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */; }; - 6FFF1633250A20EF0053CDA6 /* FocusDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */; }; - 6FFF1634250A20EF0053CDA6 /* FocusDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */; }; - 6FFF1635250A20EF0053CDA6 /* FocusDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */; }; - 6FFF1636250A20EF0053CDA6 /* FocusDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */; }; + 6FFF1632250A20EF0053CDA6 /* FocusTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */; }; + 6FFF1633250A20EF0053CDA6 /* FocusTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */; }; + 6FFF1634250A20EF0053CDA6 /* FocusTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */; }; + 6FFF1635250A20EF0053CDA6 /* FocusTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */; }; + 6FFF1636250A20EF0053CDA6 /* FocusTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */; }; + 6FFFB99D252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */; }; + 6FFFB99E252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */; }; + 6FFFB99F252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */; }; + 6FFFB9A0252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */; }; + 6FFFB9A1252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */; }; + 6FFFB9B7252CA310004E40AE /* MediaDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */; }; + 6FFFB9B8252CA310004E40AE /* MediaDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */; }; + 6FFFB9B9252CA310004E40AE /* MediaDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */; }; + 6FFFB9BA252CA310004E40AE /* MediaDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */; }; + 6FFFB9BB252CA310004E40AE /* MediaDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */; }; 70D8747D1FCCB2C6B309BC0B /* libPods-Play SRG-iOS-Play RTS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AB348106F5307D86C18CA361 /* libPods-Play SRG-iOS-Play RTS.a */; }; 8C3643978953564D3546A15C /* libPods-Play SRG-tvOS-Play SRF TV.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D81BAFCE19BF2C2B612FE3A2 /* libPods-Play SRG-tvOS-Play SRF TV.a */; }; D8A1811ABB77991A9249F28D /* libPods-Play SRG-iOS-Play RTR.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A39FE8CAE184E875FB2D868 /* libPods-Play SRG-iOS-Play RTR.a */; }; @@ -1652,6 +1847,7 @@ 0804917322832C5C00E4CEC2 /* HomeShowCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeShowCollectionViewCell.h; sourceTree = ""; }; 0804917422832C5C00E4CEC2 /* HomeShowCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HomeShowCollectionViewCell.xib; sourceTree = ""; }; 0804917522832C5C00E4CEC2 /* HomeShowCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeShowCollectionViewCell.m; sourceTree = ""; }; + 0805703D2540E0FC00A59C9D /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = ""; }; 0806E7B51D50D918002ED406 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; 0806E7B61D50D918002ED406 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; 081220A41DD079BB00BF8326 /* DownloadsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadsViewController.h; sourceTree = ""; }; @@ -1661,6 +1857,7 @@ 081220A91DD079BB00BF8326 /* DownloadTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DownloadTableViewCell.xib; sourceTree = ""; }; 081220BF1DD0ADAC00BF8326 /* DownloadSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadSession.h; sourceTree = ""; }; 081220C01DD0ADAC00BF8326 /* DownloadSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadSession.m; sourceTree = ""; }; + 081456302547740600BB7CA6 /* Badges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badges.swift; sourceTree = ""; }; 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play iOS.xcconfig"; sourceTree = ""; }; 0814CE0A24D85E0B0013D7A9 /* Play tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play tvOS.xcconfig"; sourceTree = ""; }; 081DAA1C1E099A3A00018623 /* DownloadFooterSectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadFooterSectionView.h; sourceTree = ""; }; @@ -1702,6 +1899,7 @@ 08564B221D41112800381549 /* HomeSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HomeSectionHeaderView.xib; sourceTree = ""; }; 0859ADC422D72F4F00511F7F /* SearchSettingsMultiSelectionItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SearchSettingsMultiSelectionItem.h; sourceTree = ""; }; 0859ADC522D72F4F00511F7F /* SearchSettingsMultiSelectionItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SearchSettingsMultiSelectionItem.m; sourceTree = ""; }; + 085C2B08256472F700F57D38 /* ShowsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowsModel.swift; sourceTree = ""; }; 08602BF42213786A0081D166 /* HomeShowsAccessTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeShowsAccessTableViewCell.m; sourceTree = ""; }; 08602BF52213786B0081D166 /* HomeShowsAccessTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HomeShowsAccessTableViewCell.xib; sourceTree = ""; }; 08602BF62213786B0081D166 /* HomeShowsAccessTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeShowsAccessTableViewCell.h; sourceTree = ""; }; @@ -1710,6 +1908,7 @@ 086320F82258C6CF00C719A6 /* WatchLaterViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WatchLaterViewController.h; sourceTree = ""; }; 086320F92258C6CF00C719A6 /* WatchLaterTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WatchLaterTableViewCell.h; sourceTree = ""; }; 086320FA2258C6CF00C719A6 /* WatchLaterTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WatchLaterTableViewCell.xib; sourceTree = ""; }; + 086499B124F69FF20027373E /* MediaDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaDetailView.swift; sourceTree = ""; }; 086BDE121EA63D3800965F45 /* PlayMiniPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayMiniPlayerView.h; sourceTree = ""; }; 086BDE131EA63D3800965F45 /* PlayMiniPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayMiniPlayerView.m; sourceTree = ""; }; 086BDE141EA63D3800965F45 /* PlayMiniPlayerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PlayMiniPlayerView.xib; sourceTree = ""; }; @@ -1720,6 +1919,7 @@ 086F8D5122984B29001BE2F4 /* Favorites.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Favorites.h; sourceTree = ""; }; 086F8D5322984B29001BE2F4 /* FavoriteTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FavoriteTableViewCell.m; sourceTree = ""; }; 086F8D5422984B29001BE2F4 /* FavoritesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FavoritesViewController.h; sourceTree = ""; }; + 08742C5825481CC8006E2DCE /* ShowDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowDetailView.swift; sourceTree = ""; }; 08754F3E201BAFCC00458F3A /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; 08754F42201BAFF100458F3A /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/InfoPlist.strings; sourceTree = ""; }; 08754F46201BB00000458F3A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -1772,7 +1972,6 @@ 08B77AEE240A86FC00A3BC3B /* AccessibilityIdentifierConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilityIdentifierConstants.h; sourceTree = ""; }; 08B77AEF240A86FC00A3BC3B /* AccessibilityIdentifierConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AccessibilityIdentifierConstants.m; sourceTree = ""; }; 08B77AFB240A8D0A00A3BC3B /* Screenshots-ObjectiveC-Bridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Screenshots-ObjectiveC-Bridge.h"; sourceTree = ""; }; - 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Screenshots.xcconfig; sourceTree = ""; }; 08BAABD71E7613F0007644D7 /* HomeMediasViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeMediasViewController.h; sourceTree = ""; }; 08BAABD81E7613F0007644D7 /* HomeMediasViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeMediasViewController.m; sourceTree = ""; }; 08BDB5B1228D520000335898 /* PushService+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PushService+Private.h"; sourceTree = ""; }; @@ -1816,6 +2015,24 @@ 08DD9D561FFE6EDA00115906 /* ModuleHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModuleHeaderView.h; sourceTree = ""; }; 08DD9D581FFE6EDA00115906 /* ModuleHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModuleHeaderView.m; sourceTree = ""; }; 08DD9D591FFE6EDA00115906 /* ModuleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModuleViewController.m; sourceTree = ""; }; + 08E7881C255B4D5B00E17515 /* Play RTR.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RTR.xcconfig"; sourceTree = ""; }; + 08E7881E255B4D5C00E17515 /* Play SWI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play SWI.xcconfig"; sourceTree = ""; }; + 08E78820255B4D5D00E17515 /* Play SRF.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play SRF.xcconfig"; sourceTree = ""; }; + 08E78822255B4D5D00E17515 /* Play RSI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RSI.xcconfig"; sourceTree = ""; }; + 08E78823255B4D5E00E17515 /* Play RTS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RTS.xcconfig"; sourceTree = ""; }; + 08E78909255B59D700E17515 /* Play SWI Notification Service Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play SWI Notification Service Extension.xcconfig"; sourceTree = ""; }; + 08E7890A255B59D800E17515 /* Play iOS Notification Service Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play iOS Notification Service Extension.xcconfig"; sourceTree = ""; }; + 08E7890B255B59D800E17515 /* Play SRF Notification Service Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play SRF Notification Service Extension.xcconfig"; sourceTree = ""; }; + 08E7890C255B59D800E17515 /* Play RTR Notification Service Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RTR Notification Service Extension.xcconfig"; sourceTree = ""; }; + 08E7890D255B59D800E17515 /* Play RSI Notification Service Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RSI Notification Service Extension.xcconfig"; sourceTree = ""; }; + 08E7890E255B59D800E17515 /* Play RTS Notification Service Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RTS Notification Service Extension.xcconfig"; sourceTree = ""; }; + 08E7899B255B605E00E17515 /* Play RSI Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RSI Screenshots.xcconfig"; sourceTree = ""; }; + 08E7899C255B605E00E17515 /* Play RTR Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RTR Screenshots.xcconfig"; sourceTree = ""; }; + 08E7899D255B605E00E17515 /* Play iOS Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play iOS Screenshots.xcconfig"; sourceTree = ""; }; + 08E7899E255B605E00E17515 /* Play RTS Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play RTS Screenshots.xcconfig"; sourceTree = ""; }; + 08E7899F255B605F00E17515 /* Play SRF Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play SRF Screenshots.xcconfig"; sourceTree = ""; }; + 08E789A0255B605F00E17515 /* Play SWI Screenshots.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Play SWI Screenshots.xcconfig"; sourceTree = ""; }; + 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = ""; }; 096E5AC5C979FBF1F072A888 /* Pods-Play SRG-iOS-Play RSI.nightly.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Play SRG-iOS-Play RSI.nightly.xcconfig"; path = "Pods/Target Support Files/Pods-Play SRG-iOS-Play RSI/Pods-Play SRG-iOS-Play RSI.nightly.xcconfig"; sourceTree = ""; }; 0A4B38B692D4F377D70590D1 /* Pods-Play SRG-iOS-Play SWI.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Play SRG-iOS-Play SWI.beta.xcconfig"; path = "Pods/Target Support Files/Pods-Play SRG-iOS-Play SWI/Pods-Play SRG-iOS-Play SWI.beta.xcconfig"; sourceTree = ""; }; 0AD1DB5E0679F09CC8A63D91 /* libPods-Play SRG-tvOS-Play SWI TV.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Play SRG-tvOS-Play SWI TV.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1853,8 +2070,10 @@ 6F0CFB5820C94E8E006B2CE4 /* Play RSI notification service extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Play RSI notification service extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6F0CFB6B20C94EC8006B2CE4 /* Play RTR notification service extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Play RTR notification service extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6F0CFB7E20C94EE5006B2CE4 /* Play SWI notification service extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Play SWI notification service extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowDetailModel.swift; sourceTree = ""; }; 6F0E8D961F14F62F002014C3 /* SRGChannel+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SRGChannel+PlaySRG.h"; sourceTree = ""; }; 6F0E8D971F14F62F002014C3 /* SRGChannel+PlaySRG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SRGChannel+PlaySRG.m"; sourceTree = ""; }; + 6F0ED543252B00B000ECE97B /* LabeledButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabeledButton.swift; sourceTree = ""; }; 6F0EDA632448B0D700F0FED2 /* RefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RefreshControl.m; sourceTree = ""; }; 6F0EDA642448B0D800F0FED2 /* RefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefreshControl.h; sourceTree = ""; }; 6F12DB4021A3EFBF0054879D /* HistoryTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryTableViewCell.h; sourceTree = ""; }; @@ -1868,6 +2087,8 @@ 6F12E4DF22D8676300BC1718 /* SearchHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchHeaderView.m; sourceTree = ""; }; 6F12E4E022D8676300BC1718 /* SearchHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchHeaderView.h; sourceTree = ""; }; 6F12E4E122D8676500BC1718 /* SearchHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SearchHeaderView.xib; sourceTree = ""; }; + 6F151E26256BF5CF009082F8 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; + 6F151E54256BFCEB009082F8 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 6F167F48248197C700B8E7F1 /* HomeTopicListTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeTopicListTableViewCell.h; sourceTree = ""; }; 6F167F49248197C700B8E7F1 /* HomeTopicListTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeTopicListTableViewCell.m; sourceTree = ""; }; 6F167F4F248198DC00B8E7F1 /* HomeTopicCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeTopicCollectionViewCell.h; sourceTree = ""; }; @@ -1902,8 +2123,8 @@ 6F1F80652508ECA800FD8FE7 /* ShowsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowsView.swift; sourceTree = ""; }; 6F1F929C22A539D40026BFAC /* SearchShowListCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SearchShowListCollectionViewCell.h; sourceTree = ""; }; 6F1F929D22A539D40026BFAC /* SearchShowListCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SearchShowListCollectionViewCell.m; sourceTree = ""; }; - 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FirebaseStorageFix.m; sourceTree = ""; }; 6F2961FB2006186000CAB0E4 /* placeholder_media_list-180.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "placeholder_media_list-180.pdf"; sourceTree = ""; }; + 6F29885F255ADDCD007A8E2B /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 6F2AB1B32487C31C009D9FBC /* MediaPlayerViewController+SongPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MediaPlayerViewController+SongPanel.swift"; sourceTree = ""; }; 6F331CE424D06B8200C096AB /* Play SRF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Play SRF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 6F331CE624D06B8200C096AB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -1933,11 +2154,12 @@ 6F3E09FF23D055DD009B00C1 /* SettingTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingTableViewCell.h; sourceTree = ""; }; 6F3E0A0023D055DD009B00C1 /* SettingTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingTableViewCell.m; sourceTree = ""; }; 6F3E6A9224978FC8004B26D6 /* MediaPlayerViewController+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MediaPlayerViewController+Private.h"; sourceTree = ""; }; - 6F3F1AB925060496000FF4DD /* MediaVisual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaVisual.swift; sourceTree = ""; }; + 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaVisualView.swift; sourceTree = ""; }; 6F4091FF22DCF43D005F3850 /* Previewing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Previewing.m; sourceTree = ""; }; 6F40920522DCFE2A005F3850 /* MostSearchedShowCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MostSearchedShowCollectionViewCell.h; sourceTree = ""; }; 6F40920622DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MostSearchedShowCollectionViewCell.m; sourceTree = ""; }; 6F40920722DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MostSearchedShowCollectionViewCell.xib; sourceTree = ""; }; + 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingOverlay.swift; sourceTree = ""; }; 6F475F8C1EB37BC6003021EA /* BaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseViewController.h; sourceTree = ""; }; 6F475F8D1EB37BC6003021EA /* BaseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseViewController.m; sourceTree = ""; }; 6F475F8E1EB37BC6003021EA /* CollectionRequestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionRequestViewController.h; sourceTree = ""; }; @@ -1993,7 +2215,6 @@ 6F4855A81E3A38D600B0141C /* SRGLetterboxDesignables.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRGLetterboxDesignables.m; sourceTree = ""; }; 6F488AC922EED363002B1150 /* PlayAccessibilityFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlayAccessibilityFormatter.h; sourceTree = ""; }; 6F488ACA22EED363002B1150 /* PlayAccessibilityFormatter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlayAccessibilityFormatter.m; sourceTree = ""; }; - 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formatters.swift; sourceTree = ""; }; 6F4C0A7320DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeShowVerticalListTableViewCell.h; sourceTree = ""; }; 6F4C0A7420DCBC9D00069A85 /* HomeShowVerticalListTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeShowVerticalListTableViewCell.m; sourceTree = ""; }; 6F4C673B1EA6115C006A0A3B /* SearchViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchViewController.h; sourceTree = ""; }; @@ -2004,6 +2225,7 @@ 6F4EA13C1EE034E200BEC4DA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Accessibility.strings; sourceTree = ""; }; 6F4EA13F1EE034EF00BEC4DA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Accessibility.strings; sourceTree = ""; }; 6F4EA1411EE0351B00BEC4DA /* NSBundle+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+PlaySRG.h"; sourceTree = ""; }; + 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationLabel.swift; sourceTree = ""; }; 6F556B361DDC419800B5DEA2 /* SettingsBaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsBaseViewController.h; sourceTree = ""; }; 6F556B371DDC419800B5DEA2 /* SettingsBaseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsBaseViewController.m; sourceTree = ""; }; 6F556DB020D1174100C3573A /* NotificationService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NotificationService-Info.plist"; sourceTree = ""; }; @@ -2018,6 +2240,7 @@ 6F56F9F2247C407000B2387B /* ChannelServiceSetup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChannelServiceSetup.h; sourceTree = ""; }; 6F56F9F3247C407000B2387B /* ChannelServiceSetup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChannelServiceSetup.m; sourceTree = ""; }; 6F5866491DD226EE005DAFE4 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + 6F5A7929256EE61200E884F2 /* HistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryModel.swift; sourceTree = ""; }; 6F5CAC86245FF04C0058F9B0 /* ProgramHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgramHeaderView.h; sourceTree = ""; }; 6F5E63A4250CB32E00056160 /* Modifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modifiers.swift; sourceTree = ""; }; 6F5F4FC11DB10CFD0011CCA3 /* RelatedContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RelatedContentView.h; sourceTree = ""; }; @@ -2029,6 +2252,7 @@ 6F6B642D21A70FAA00E207FC /* HomeStatusHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeStatusHeaderView.xib; sourceTree = ""; }; 6F7218111DBE75AC00575072 /* PreviewingDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreviewingDelegate.h; sourceTree = ""; }; 6F7218121DBE75AC00575072 /* PreviewingDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreviewingDelegate.m; sourceTree = ""; }; + 6F79E0A92541647400A28E79 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; 6F7C35B323708E8A00259BE7 /* SRGResource+PlaySRG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SRGResource+PlaySRG.h"; sourceTree = ""; }; 6F7C35B423708E8A00259BE7 /* SRGResource+PlaySRG.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SRGResource+PlaySRG.m"; sourceTree = ""; }; 6F7C89CE20AAFC6100255A54 /* Onboardings.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Onboardings.xcassets; sourceTree = ""; }; @@ -2048,6 +2272,7 @@ 6F82430B250637BE00E931A0 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = ""; }; 6F85E4B51EEA8F9B00552256 /* UIView+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+PlaySRG.h"; sourceTree = ""; }; 6F85E4B61EEA8F9B00552256 /* UIView+PlaySRG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+PlaySRG.m"; sourceTree = ""; }; + 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveMediaCell.swift; sourceTree = ""; }; 6F861AF71DAE65A600C46102 /* SRGMediaPlayerDesignables.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRGMediaPlayerDesignables.m; sourceTree = ""; }; 6F88139323D85DD600A00826 /* GoogleCastBarButtonItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoogleCastBarButtonItem.h; sourceTree = ""; }; 6F88139423D85DD600A00826 /* GoogleCastBarButtonItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleCastBarButtonItem.m; sourceTree = ""; }; @@ -2083,12 +2308,14 @@ 6F93DE1220AE9A9900B71572 /* OnboardingsViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = OnboardingsViewController.storyboard; sourceTree = ""; }; 6F93DE1820AE9BE600B71572 /* OnboardingTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OnboardingTableViewCell.h; sourceTree = ""; }; 6F93DE1920AE9BE600B71572 /* OnboardingTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OnboardingTableViewCell.m; sourceTree = ""; }; + 6F975738256EEEC4004DA73C /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = ""; }; 6F982B91244F1E1200C0386E /* TableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableView.h; sourceTree = ""; }; 6F982B92244F1E1200C0386E /* TableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TableView.m; sourceTree = ""; }; 6F9897CB2412582400B390A2 /* Layout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Layout.h; sourceTree = ""; }; 6F9897CC2412582400B390A2 /* Layout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Layout.m; sourceTree = ""; }; 6F9D2741203AD99C00FDE899 /* Playlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Playlist.h; sourceTree = ""; }; 6F9D2742203AD99C00FDE899 /* Playlist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Playlist.m; sourceTree = ""; }; + 6FA14534254729DA006E8D3B /* FocusableRegion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusableRegion.swift; sourceTree = ""; }; 6FA5D15B1F2077B10059E4E2 /* NSString+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+PlaySRG.h"; sourceTree = ""; }; 6FA5D15C1F2077B10059E4E2 /* NSString+PlaySRG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+PlaySRG.m"; sourceTree = ""; }; 6FA6C0612458064C00518DE6 /* SRGProgramComposition+PlaySRG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SRGProgramComposition+PlaySRG.h"; sourceTree = ""; }; @@ -2183,12 +2410,16 @@ 6FE2876B2481687A00358CFF /* SongTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SongTableViewCell.h; sourceTree = ""; }; 6FE2876C2481687A00358CFF /* SongTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SongTableViewCell.m; sourceTree = ""; }; 6FE28772248168A100358CFF /* SongTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SongTableViewCell.xib; sourceTree = ""; }; + 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; 6FE686DF1EB9D57400067D40 /* ChannelService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChannelService.h; sourceTree = ""; }; 6FE686E01EB9D57400067D40 /* ChannelService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChannelService.m; sourceTree = ""; }; 6FE9B54224B84748001D95E5 /* HomeLiveMediaVerticalListTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeLiveMediaVerticalListTableViewCell.m; sourceTree = ""; }; 6FE9B54324B84748001D95E5 /* HomeLiveMediaVerticalListTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeLiveMediaVerticalListTableViewCell.h; sourceTree = ""; }; 6FEC91A721A6B39A00AA50C8 /* TableLoadMoreFooterView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableLoadMoreFooterView.h; sourceTree = ""; }; 6FEC91A821A6B39A00AA50C8 /* TableLoadMoreFooterView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TableLoadMoreFooterView.m; sourceTree = ""; }; + 6FF12AA1256C58FE0042F446 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; + 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopicDetailModel.swift; sourceTree = ""; }; + 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopicDetailView.swift; sourceTree = ""; }; 6FF4D4DE23D5B07E008B981A /* UIVisualEffectView+PlaySRG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIVisualEffectView+PlaySRG.h"; sourceTree = ""; }; 6FF4D4DF23D5B07E008B981A /* UIVisualEffectView+PlaySRG.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIVisualEffectView+PlaySRG.m"; sourceTree = ""; }; 6FF60CC824F2FA8D006013C2 /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = ""; }; @@ -2205,7 +2436,9 @@ 6FFCB62224504C6C00D16466 /* SwimlaneCollectionViewLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwimlaneCollectionViewLayout.h; sourceTree = ""; }; 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SwimlaneCollectionViewLayout.m; sourceTree = ""; }; 6FFCC5E324EFBF2100805B0F /* RadioChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioChannel.swift; sourceTree = ""; }; - 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusDetector.swift; sourceTree = ""; }; + 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusTracker.swift; sourceTree = ""; }; + 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+Extensions.swift"; sourceTree = ""; }; + 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaDetailModel.swift; sourceTree = ""; }; 7D4D90DEC67A54E9E386CAD4 /* Pods-Play SRG-iOS-Play RSI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Play SRG-iOS-Play RSI.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Play SRG-iOS-Play RSI/Pods-Play SRG-iOS-Play RSI.debug.xcconfig"; sourceTree = ""; }; 87A982BD56B5181A6E802E03 /* Pods-Play SRG-tvOS-Play RTS TV.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Play SRG-tvOS-Play RTS TV.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTS TV/Pods-Play SRG-tvOS-Play RTS TV.debug.xcconfig"; sourceTree = ""; }; 881AC8571743B65FBCF1192E /* Pods-Play SRG-iOS-Play RTR.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Play SRG-iOS-Play RTR.appstore.xcconfig"; path = "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTR/Pods-Play SRG-iOS-Play RTR.appstore.xcconfig"; sourceTree = ""; }; @@ -2302,7 +2535,7 @@ files = ( 6FD4095A25221A1000E621EF /* AirshipCore in Frameworks */, 6FD40A4825221C2D00E621EF /* PaperOnboarding in Frameworks */, - 081454BB2546CDD500BB7CA6 /* SwiftMessages in Frameworks */, + 084F0968254861A0009DEDD2 /* SwiftMessages in Frameworks */, 6F2545B52521C14E00F9F4FE /* SRGLetterbox in Frameworks */, 6F2546C12521CFD400F9F4FE /* SRGAnalyticsIdentity in Frameworks */, 6F25463B2521C34800F9F4FE /* Aiolos in Frameworks */, @@ -2317,7 +2550,7 @@ files = ( 6FD4095C25221A1A00E621EF /* AirshipCore in Frameworks */, 6FD40A5E25221C3A00E621EF /* PaperOnboarding in Frameworks */, - 081455142546CE4300BB7CA6 /* SwiftMessages in Frameworks */, + 084F0969254861A0009DEDD2 /* SwiftMessages in Frameworks */, 6F2545B92521C15E00F9F4FE /* SRGUserData in Frameworks */, 6F2546C32521CFDC00F9F4FE /* SRGAnalyticsIdentity in Frameworks */, 6F2546512521C35300F9F4FE /* Aiolos in Frameworks */, @@ -2332,7 +2565,7 @@ files = ( 6FD4095E25221A2300E621EF /* AirshipCore in Frameworks */, 6FD40A7425221C4300E621EF /* PaperOnboarding in Frameworks */, - 081455162546CE4F00BB7CA6 /* SwiftMessages in Frameworks */, + 084F096A254861A0009DEDD2 /* SwiftMessages in Frameworks */, 6F2545BD2521C16800F9F4FE /* SRGUserData in Frameworks */, 6F2546C52521CFEA00F9F4FE /* SRGAnalyticsIdentity in Frameworks */, 6F2546532521C35A00F9F4FE /* Aiolos in Frameworks */, @@ -2347,7 +2580,7 @@ files = ( 6FD4096025221A2B00E621EF /* AirshipCore in Frameworks */, 6FD40A7625221C4900E621EF /* PaperOnboarding in Frameworks */, - 081455182546CE5700BB7CA6 /* SwiftMessages in Frameworks */, + 084F096B254861A0009DEDD2 /* SwiftMessages in Frameworks */, 6F2545C12521C17000F9F4FE /* SRGUserData in Frameworks */, 6F2546C72521CFF000F9F4FE /* SRGAnalyticsIdentity in Frameworks */, 6F2546552521C36100F9F4FE /* Aiolos in Frameworks */, @@ -2362,7 +2595,7 @@ files = ( 6FD4096225221A3500E621EF /* AirshipCore in Frameworks */, 6FD40A8C25221C4F00E621EF /* PaperOnboarding in Frameworks */, - 0814551A2546CE5F00BB7CA6 /* SwiftMessages in Frameworks */, + 084F096C254861A0009DEDD2 /* SwiftMessages in Frameworks */, 6F2545C52521C17700F9F4FE /* SRGUserData in Frameworks */, 6F2546C92521CFFA00F9F4FE /* SRGAnalyticsIdentity in Frameworks */, 6F2546572521C36E00F9F4FE /* Aiolos in Frameworks */, @@ -2415,11 +2648,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6FDB2AA524E590EE00FF286E /* Nuke in Frameworks */, + 08209A0B25780AAA0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */, + 6F61CCC3256E41EE00E42E31 /* SRGUserData in Frameworks */, 6FA84A1A24E44B6400B1A56E /* SRGDataProviderCombine in Frameworks */, 6FA84A1824E44B6400B1A56E /* SRGLetterbox in Frameworks */, 6FDB2AA724E590EE00FF286E /* FetchImage in Frameworks */, 8C3643978953564D3546A15C /* libPods-Play SRG-tvOS-Play SRF TV.a in Frameworks */, + 6FF12A48256C58A70042F446 /* SRGLoggerSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2427,11 +2662,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6FDB2AA924E590F400FF286E /* Nuke in Frameworks */, + 082099F525780A7F0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */, + 6F61CCC5256E41F500E42E31 /* SRGUserData in Frameworks */, 6FA84A1E24E44B6D00B1A56E /* SRGDataProviderCombine in Frameworks */, 6FA84A1C24E44B6D00B1A56E /* SRGLetterbox in Frameworks */, 6FDB2AAB24E590F400FF286E /* FetchImage in Frameworks */, 2F014EC1298A005CED1AB5D8 /* libPods-Play SRG-tvOS-Play RTS TV.a in Frameworks */, + 6FF12A5E256C58B20042F446 /* SRGLoggerSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2439,11 +2676,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6FDB2AAD24E590FC00FF286E /* Nuke in Frameworks */, + 08209A0D25780ABE0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */, + 6F61CCC7256E41FB00E42E31 /* SRGUserData in Frameworks */, 6FA84A2224E44B7500B1A56E /* SRGDataProviderCombine in Frameworks */, 6FA84A2024E44B7500B1A56E /* SRGLetterbox in Frameworks */, 6FDB2AAF24E590FC00FF286E /* FetchImage in Frameworks */, FC46A119EB419CEE1F60BD49 /* libPods-Play SRG-tvOS-Play RSI TV.a in Frameworks */, + 6FF12A74256C58BB0042F446 /* SRGLoggerSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2451,11 +2690,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6FDB2AB124E5910400FF286E /* Nuke in Frameworks */, + 08209A0F25780AC50077CF8B /* SRGAnalyticsSwiftUI in Frameworks */, + 6F61CCC9256E420200E42E31 /* SRGUserData in Frameworks */, 6FA84A2624E44B7B00B1A56E /* SRGDataProviderCombine in Frameworks */, 6FA84A2424E44B7B00B1A56E /* SRGLetterbox in Frameworks */, 6FDB2AB324E5910400FF286E /* FetchImage in Frameworks */, 0B2164E33BB0632C4F3DADED /* libPods-Play SRG-tvOS-Play RTR TV.a in Frameworks */, + 6FF12A76256C58C40042F446 /* SRGLoggerSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2463,11 +2704,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6FDB2AB524E5910B00FF286E /* Nuke in Frameworks */, + 08209A1125780ACB0077CF8B /* SRGAnalyticsSwiftUI in Frameworks */, + 6F61CCCB256E420800E42E31 /* SRGUserData in Frameworks */, 6FA84A2A24E44B8300B1A56E /* SRGDataProviderCombine in Frameworks */, 6FA84A2824E44B8300B1A56E /* SRGLetterbox in Frameworks */, 6FDB2AB724E5910B00FF286E /* FetchImage in Frameworks */, 4ADF71B3CD93059BC9C13BFE /* libPods-Play SRG-tvOS-Play SWI TV.a in Frameworks */, + 6FF12A8C256C58CC0042F446 /* SRGLoggerSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2703,7 +2946,6 @@ 6FD6861E2460670600B8018A /* Channel.m */, 6F566E7E24EE95CB0024B4CA /* FirebaseConfiguration.h */, 6F566E7F24EE95CB0024B4CA /* FirebaseConfiguration.m */, - 6F24913225248CDB00621C27 /* FirebaseStorageFix.m */, 6F566E8524EEC2AD0024B4CA /* HomeSection.h */, 6F566E8624EEC2AD0024B4CA /* HomeSection.m */, 0801E0791D4A4A810008021E /* RadioChannel.h */, @@ -3106,7 +3348,11 @@ isa = PBXGroup; children = ( 08B77A402409BDF200A3BC3B /* Play iOS Application.xcconfig */, + 08E7890A255B59D800E17515 /* Play iOS Notification Service Extension.xcconfig */, + 08E7899D255B605E00E17515 /* Play iOS Screenshots.xcconfig */, 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */, + 08E7890D255B59D800E17515 /* Play RSI Notification Service Extension.xcconfig */, + 08E7899B255B605E00E17515 /* Play RSI Screenshots.xcconfig */, 6F88408024EBF92A007463ED /* Play RSI TV.appstore.xcconfig */, 6F88409124EBF930007463ED /* Play RSI TV.beta.xcconfig */, 6F88408924EBF92D007463ED /* Play RSI TV.debug.xcconfig */, @@ -3115,6 +3361,9 @@ 6F1EBE13222EBB7C003E0722 /* Play RSI.beta.xcconfig */, 6F1EBE0F222EBB7C003E0722 /* Play RSI.debug.xcconfig */, 6F1EBE10222EBB7C003E0722 /* Play RSI.nightly.xcconfig */, + 08E78822255B4D5D00E17515 /* Play RSI.xcconfig */, + 08E7890C255B59D800E17515 /* Play RTR Notification Service Extension.xcconfig */, + 08E7899C255B605E00E17515 /* Play RTR Screenshots.xcconfig */, 6F88408824EBF92C007463ED /* Play RTR TV.appstore.xcconfig */, 6F88409024EBF930007463ED /* Play RTR TV.beta.xcconfig */, 6F88408B24EBF92D007463ED /* Play RTR TV.debug.xcconfig */, @@ -3123,6 +3372,9 @@ 6F1EBE15222EBB7D003E0722 /* Play RTR.beta.xcconfig */, 6F1EBE0E222EBB7C003E0722 /* Play RTR.debug.xcconfig */, 6F1EBE1D222EBB7E003E0722 /* Play RTR.nightly.xcconfig */, + 08E7881C255B4D5B00E17515 /* Play RTR.xcconfig */, + 08E7890E255B59D800E17515 /* Play RTS Notification Service Extension.xcconfig */, + 08E7899E255B605E00E17515 /* Play RTS Screenshots.xcconfig */, 6F88408124EBF92A007463ED /* Play RTS TV.appstore.xcconfig */, 6F88408C24EBF92E007463ED /* Play RTS TV.beta.xcconfig */, 6F88408424EBF92B007463ED /* Play RTS TV.debug.xcconfig */, @@ -3131,6 +3383,9 @@ 6F1EBE0D222EBB7C003E0722 /* Play RTS.beta.xcconfig */, 6F1EBE0B222EBB7B003E0722 /* Play RTS.debug.xcconfig */, 6F1EBE11222EBB7C003E0722 /* Play RTS.nightly.xcconfig */, + 08E78823255B4D5E00E17515 /* Play RTS.xcconfig */, + 08E7890B255B59D800E17515 /* Play SRF Notification Service Extension.xcconfig */, + 08E7899F255B605F00E17515 /* Play SRF Screenshots.xcconfig */, 6F88408A24EBF92D007463ED /* Play SRF TV.appstore.xcconfig */, 6F88408724EBF92C007463ED /* Play SRF TV.beta.xcconfig */, 6F88409324EBF931007463ED /* Play SRF TV.debug.xcconfig */, @@ -3139,6 +3394,9 @@ 6F1EBE1B222EBB7D003E0722 /* Play SRF.beta.xcconfig */, 6F1EBE1C222EBB7E003E0722 /* Play SRF.debug.xcconfig */, 6F1EBE18222EBB7D003E0722 /* Play SRF.nightly.xcconfig */, + 08E78820255B4D5D00E17515 /* Play SRF.xcconfig */, + 08E78909255B59D700E17515 /* Play SWI Notification Service Extension.xcconfig */, + 08E789A0255B605F00E17515 /* Play SWI Screenshots.xcconfig */, 6F88408D24EBF92E007463ED /* Play SWI TV.appstore.xcconfig */, 6F88408E24EBF92F007463ED /* Play SWI TV.beta.xcconfig */, 6F88408224EBF92A007463ED /* Play SWI TV.debug.xcconfig */, @@ -3147,9 +3405,9 @@ 6F1EBE12222EBB7C003E0722 /* Play SWI.beta.xcconfig */, 6F1EBE14222EBB7D003E0722 /* Play SWI.debug.xcconfig */, 6F1EBE16222EBB7D003E0722 /* Play SWI.nightly.xcconfig */, + 08E7881E255B4D5C00E17515 /* Play SWI.xcconfig */, 6F88409424EC05E4007463ED /* Play tvOS Application.xcconfig */, 0814CE0A24D85E0B0013D7A9 /* Play tvOS.xcconfig */, - 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */, ); path = Xcode; sourceTree = ""; @@ -3324,30 +3582,53 @@ 6F4FCB1124D07069002675EF /* Sources */ = { isa = PBXGroup; children = ( + 6FE4F0CB257A368B00223F22 /* ActivityIndicator.swift */, 6F331CE624D06B8200C096AB /* AppDelegate.swift */, 6F1D31B224E5369A005C778B /* ApplicationConfiguration.swift */, 6F331CE824D06B8200C096AB /* AudiosView.swift */, + 081456302547740600BB7CA6 /* Badges.swift */, + 6F426B4125680A8F00DD22AB /* BlockingOverlay.swift */, 6FF60CC824F2FA8D006013C2 /* CollectionView.swift */, - 6FFF1631250A20EF0053CDA6 /* FocusDetector.swift */, + 6F79E0A92541647400A28E79 /* Colors.swift */, + 6F4F78AE252AF7670011DBCA /* DurationLabel.swift */, + 6F29885F255ADDCD007A8E2B /* Errors.swift */, + 6F151E54256BFCEB009082F8 /* Extensions.swift */, + 6FA14534254729DA006E8D3B /* FocusableRegion.swift */, + 6FFF1631250A20EF0053CDA6 /* FocusTracker.swift */, 6F82430B250637BE00E931A0 /* Fonts.swift */, - 6F4BCBE024EFF7C3001ABA17 /* Formatters.swift */, + 08FD789C253E0D3500EF302A /* Foundation+Extensions.swift */, 6F355AA224E65CB400F1500E /* HeroMediaCell.swift */, + 6F5A7929256EE61200E884F2 /* HistoryModel.swift */, + 6F975738256EEEC4004DA73C /* HistoryView.swift */, 6FA84A3124E452F700B1A56E /* HomeModel.swift */, 6FCC73B924E6F8EE00ECF73F /* HomeView.swift */, 6FDB2AB824E5916F00FF286E /* ImageView.swift */, + 6F0ED543252B00B000ECE97B /* LabeledButton.swift */, + 6F85F81D2567F0D100AC8286 /* LiveMediaCell.swift */, 6F331D5F24D0769A00C096AB /* LiveView.swift */, + 6FF12AA1256C58FE0042F446 /* Logger.swift */, 6FDB2AC424E5923600FF286E /* MediaCell.swift */, 6FF8F155250609CF009A741F /* MediaDescription.swift */, - 6F3F1AB925060496000FF4DD /* MediaVisual.swift */, + 6FFFB9B6252CA310004E40AE /* MediaDetailModel.swift */, + 086499B124F69FF20027373E /* MediaDetailView.swift */, + 6F3F1AB925060496000FF4DD /* MediaVisualView.swift */, 6F5E63A4250CB32E00056160 /* Modifiers.swift */, + 0805703D2540E0FC00A59C9D /* Navigation.swift */, 6FC4140124EF11F400FDF806 /* Play-TV-ObjectiveC-Bridge.h */, 6F331D6B24D076D500C096AB /* ProfileView.swift */, + 6F151E26256BF5CF009082F8 /* ProgressBar.swift */, 6FFCC5E324EFBF2100805B0F /* RadioChannel.swift */, 6FDB2AB924E5916F00FF286E /* RedactingView.swift */, 6F331D6524D076C800C096AB /* SearchView.swift */, 6FB1ADF924EFEF2C00E80C1E /* ShowCell.swift */, + 6F0E39CC25558A0F004C3A12 /* ShowDetailModel.swift */, + 08742C5825481CC8006E2DCE /* ShowDetailView.swift */, + 085C2B08256472F700F57D38 /* ShowsModel.swift */, 6F1F80652508ECA800FD8FE7 /* ShowsView.swift */, 6FDFD88B24E6BC9200F20382 /* TopicCell.swift */, + 6FF1EA45256569FC005FFEEA /* TopicDetailModel.swift */, + 6FF1EA7325656BE3005FFEEA /* TopicDetailView.swift */, + 6FFFB99C252C9EAC004E40AE /* UIKit+Extensions.swift */, 6F331D5924D0766500C096AB /* VideosView.swift */, ); path = Sources; @@ -4091,9 +4372,12 @@ buildConfigurationList = 6F331CF724D06B8400C096AB /* Build configuration list for PBXNativeTarget "Play SRF TV" */; buildPhases = ( 3F06504E4BC663E2337102FC /* [CP] Check Pods Manifest.lock */, + 08C13555256B2746002F67B8 /* Restore original icons if needed */, 6F331CE024D06B8200C096AB /* Sources */, 6F331CE124D06B8200C096AB /* Frameworks */, + 08C13557256B277B002F67B8 /* Make custom icons */, 6F331CE224D06B8200C096AB /* Resources */, + 08C13558256B27AC002F67B8 /* Restore original icons */, 6FFCC5EE24EFC47300805B0F /* Copy Google Service Configuration File */, 6F3FA4C425246DF700ABEE9A /* Extract Signing Identity */, ); @@ -4105,8 +4389,10 @@ packageProductDependencies = ( 6FA84A1724E44B6400B1A56E /* SRGLetterbox */, 6FA84A1924E44B6400B1A56E /* SRGDataProviderCombine */, - 6FDB2AA424E590EE00FF286E /* Nuke */, 6FDB2AA624E590EE00FF286E /* FetchImage */, + 6FF12A47256C58A70042F446 /* SRGLoggerSwift */, + 6F61CCC2256E41EE00E42E31 /* SRGUserData */, + 08209A0A25780AAA0077CF8B /* SRGAnalyticsSwiftUI */, ); productName = "Play SRF TV"; productReference = 6F331CE424D06B8200C096AB /* Play SRF.app */; @@ -4117,9 +4403,12 @@ buildConfigurationList = 6F331D0B24D06BA300C096AB /* Build configuration list for PBXNativeTarget "Play RTS TV" */; buildPhases = ( FAE3B1C7F74BDAE7DC78737D /* [CP] Check Pods Manifest.lock */, + 08C1356D256B28C0002F67B8 /* Restore original icons if needed */, 6F331CF824D06BA200C096AB /* Sources */, 6F331CF924D06BA200C096AB /* Frameworks */, + 08C13571256B291D002F67B8 /* Make custom icons */, 6F331CFA24D06BA200C096AB /* Resources */, + 08C13575256B2977002F67B8 /* Restore original icons */, 6FFCC5EF24EFC47A00805B0F /* Copy Google Service Configuration File */, 6F3FA4D925246E0000ABEE9A /* Extract Signing Identity */, ); @@ -4131,8 +4420,10 @@ packageProductDependencies = ( 6FA84A1B24E44B6D00B1A56E /* SRGLetterbox */, 6FA84A1D24E44B6D00B1A56E /* SRGDataProviderCombine */, - 6FDB2AA824E590F400FF286E /* Nuke */, 6FDB2AAA24E590F400FF286E /* FetchImage */, + 6FF12A5D256C58B20042F446 /* SRGLoggerSwift */, + 6F61CCC4256E41F500E42E31 /* SRGUserData */, + 082099F425780A7F0077CF8B /* SRGAnalyticsSwiftUI */, ); productName = "Play RTS TV"; productReference = 6F331CFC24D06BA200C096AB /* Play RTS.app */; @@ -4143,9 +4434,12 @@ buildConfigurationList = 6F331D2324D06BB800C096AB /* Build configuration list for PBXNativeTarget "Play RSI TV" */; buildPhases = ( E7AC0E2F6A0FB261ACE34DB4 /* [CP] Check Pods Manifest.lock */, + 08C1356E256B28C7002F67B8 /* Restore original icons if needed */, 6F331D1024D06BB800C096AB /* Sources */, 6F331D1124D06BB800C096AB /* Frameworks */, + 08C13572256B2922002F67B8 /* Make custom icons */, 6F331D1224D06BB800C096AB /* Resources */, + 08C13576256B297E002F67B8 /* Restore original icons */, 6FFCC5F024EFC48000805B0F /* Copy Google Service Configuration File */, 6F3FA4DA25246E0500ABEE9A /* Extract Signing Identity */, ); @@ -4157,8 +4451,10 @@ packageProductDependencies = ( 6FA84A1F24E44B7500B1A56E /* SRGLetterbox */, 6FA84A2124E44B7500B1A56E /* SRGDataProviderCombine */, - 6FDB2AAC24E590FC00FF286E /* Nuke */, 6FDB2AAE24E590FC00FF286E /* FetchImage */, + 6FF12A73256C58BB0042F446 /* SRGLoggerSwift */, + 6F61CCC6256E41FB00E42E31 /* SRGUserData */, + 08209A0C25780ABE0077CF8B /* SRGAnalyticsSwiftUI */, ); productName = "Play RSI TV"; productReference = 6F331D1424D06BB800C096AB /* Play RSI.app */; @@ -4169,9 +4465,12 @@ buildConfigurationList = 6F331D3B24D06BC700C096AB /* Build configuration list for PBXNativeTarget "Play RTR TV" */; buildPhases = ( 1FF1158BC96D38AD87F2F9E1 /* [CP] Check Pods Manifest.lock */, + 08C1356F256B28CF002F67B8 /* Restore original icons if needed */, 6F331D2824D06BC600C096AB /* Sources */, 6F331D2924D06BC600C096AB /* Frameworks */, + 08C13573256B2927002F67B8 /* Make custom icons */, 6F331D2A24D06BC600C096AB /* Resources */, + 08C13577256B2983002F67B8 /* Restore original icons */, 6FFCC5F124EFC48500805B0F /* Copy Google Service Configuration File */, 6F3FA4EF25246E1100ABEE9A /* Extract Signing Identity */, ); @@ -4183,8 +4482,10 @@ packageProductDependencies = ( 6FA84A2324E44B7B00B1A56E /* SRGLetterbox */, 6FA84A2524E44B7B00B1A56E /* SRGDataProviderCombine */, - 6FDB2AB024E5910400FF286E /* Nuke */, 6FDB2AB224E5910400FF286E /* FetchImage */, + 6FF12A75256C58C40042F446 /* SRGLoggerSwift */, + 6F61CCC8256E420200E42E31 /* SRGUserData */, + 08209A0E25780AC50077CF8B /* SRGAnalyticsSwiftUI */, ); productName = "Play RTR TV"; productReference = 6F331D2C24D06BC600C096AB /* Play RTR.app */; @@ -4195,9 +4496,12 @@ buildConfigurationList = 6F331D5324D06C2700C096AB /* Build configuration list for PBXNativeTarget "Play SWI TV" */; buildPhases = ( 69679F3DF79A5A7DF70B2B79 /* [CP] Check Pods Manifest.lock */, + 08C13570256B28D7002F67B8 /* Restore original icons if needed */, 6F331D4024D06C2600C096AB /* Sources */, 6F331D4124D06C2600C096AB /* Frameworks */, + 08C13574256B292C002F67B8 /* Make custom icons */, 6F331D4224D06C2600C096AB /* Resources */, + 08C13578256B2989002F67B8 /* Restore original icons */, 6FFCC5F224EFC48A00805B0F /* Copy Google Service Configuration File */, 6F3FA50425246E1A00ABEE9A /* Extract Signing Identity */, ); @@ -4209,8 +4513,10 @@ packageProductDependencies = ( 6FA84A2724E44B8300B1A56E /* SRGLetterbox */, 6FA84A2924E44B8300B1A56E /* SRGDataProviderCombine */, - 6FDB2AB424E5910B00FF286E /* Nuke */, 6FDB2AB624E5910B00FF286E /* FetchImage */, + 6FF12A8B256C58CC0042F446 /* SRGLoggerSwift */, + 6F61CCCA256E420800E42E31 /* SRGUserData */, + 08209A1025780ACB0077CF8B /* SRGAnalyticsSwiftUI */, ); productName = "Play SWI TV"; productReference = 6F331D4424D06C2600C096AB /* Play SWI.app */; @@ -4419,7 +4725,6 @@ packageReferences = ( 6F6DD3A624E449BA003F9437 /* XCRemoteSwiftPackageReference "srgletterbox-apple" */, 6F6DD3A924E449D7003F9437 /* XCRemoteSwiftPackageReference "srgdataprovider-apple" */, - 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */, 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */, 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */, 6F2546392521C34800F9F4FE /* XCRemoteSwiftPackageReference "Aiolos" */, @@ -4427,6 +4732,7 @@ 6FFED3F025221564002D471B /* XCRemoteSwiftPackageReference "ios-library" */, 6FD40A4625221C2D00E621EF /* XCRemoteSwiftPackageReference "paper-onboarding" */, 081454B92546CDD500BB7CA6 /* XCRemoteSwiftPackageReference "SwiftMessages" */, + 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */, ); productRefGroup = 08C68D501D38D49600BB8AAA /* Products */; projectDirPath = ""; @@ -5141,7 +5447,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\""; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; }; 08B836961FBDC08400D849D8 /* Restore original icons if needed */ = { isa = PBXShellScriptBuildPhase; @@ -5169,7 +5475,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\""; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; }; 08B836981FBDC0A100D849D8 /* Restore original icons if needed */ = { isa = PBXShellScriptBuildPhase; @@ -5185,6 +5491,276 @@ shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\""; }; + 08C13555256B2746002F67B8 /* Restore original icons if needed */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons if needed"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C13557256B277B002F67B8 /* Make custom icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Make custom icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons.sh\"\n"; + }; + 08C13558256B27AC002F67B8 /* Restore original icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C1356D256B28C0002F67B8 /* Restore original icons if needed */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons if needed"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C1356E256B28C7002F67B8 /* Restore original icons if needed */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons if needed"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C1356F256B28CF002F67B8 /* Restore original icons if needed */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons if needed"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C13570256B28D7002F67B8 /* Restore original icons if needed */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons if needed"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C13571256B291D002F67B8 /* Make custom icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Make custom icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons.sh\"\n"; + }; + 08C13572256B2922002F67B8 /* Make custom icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Make custom icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons.sh\"\n"; + }; + 08C13573256B2927002F67B8 /* Make custom icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Make custom icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons.sh\"\n"; + }; + 08C13574256B292C002F67B8 /* Make custom icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Make custom icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons.sh\"\n"; + }; + 08C13575256B2977002F67B8 /* Restore original icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C13576256B297E002F67B8 /* Restore original icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C13577256B2983002F67B8 /* Restore original icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; + 08C13578256B2989002F67B8 /* Restore original icons */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Restore original icons"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Scripts/generate-icons-restore.sh\"\n"; + }; 08C3ADF9222B018700BB7AF7 /* Update Play MMF service URL */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -5513,7 +6089,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6F19381E1EFA9D4C0017B1D1 /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5527,7 +6103,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6F1938201EFA9D590017B1D1 /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5541,7 +6117,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6F1938211EFA9D5D0017B1D1 /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5555,7 +6131,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6F1938221EFA9D600017B1D1 /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5569,7 +6145,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6F3FA4C425246DF700ABEE9A /* Extract Signing Identity */ = { isa = PBXShellScriptBuildPhase; @@ -5931,7 +6507,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6FFCC5EF24EFC47A00805B0F /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5949,7 +6525,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6FFCC5F024EFC48000805B0F /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5967,7 +6543,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6FFCC5F124EFC48500805B0F /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -5985,7 +6561,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 6FFCC5F224EFC48A00805B0F /* Copy Google Service Configuration File */ = { isa = PBXShellScriptBuildPhase; @@ -6003,7 +6579,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${PRODUCT_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; + shellScript = "GOOGLE_SERVICE_FILE=\"${SRCROOT}/Configuration/GoogleService/GoogleService-Info-${APP_BUNDLE_IDENTIFIER}.plist\"\nif [ -f \"${GOOGLE_SERVICE_FILE}\" ]; then\n cp \"${GOOGLE_SERVICE_FILE}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\nfi\n"; }; 78466A9B6015AEDBE68B70AE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -7167,43 +7743,80 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 086499B224F69FF20027373E /* MediaDetailView.swift in Sources */, + 6F79E0AA2541647400A28E79 /* Colors.swift in Sources */, 6FC413D524EEEDC900FDF806 /* ApplicationConfiguration.m in Sources */, + 081456312547740600BB7CA6 /* Badges.swift in Sources */, 6F026EED250134FD00BAE8D1 /* UIView+PlaySRG.m in Sources */, + 6F6C0FA8257AAF240077322C /* NSBundle+PlaySRG.m in Sources */, + 6F0E39CD25558A0F004C3A12 /* ShowDetailModel.swift in Sources */, + 6FF1EA46256569FC005FFEEA /* TopicDetailModel.swift in Sources */, + 0805703E2540E0FC00A59C9D /* Navigation.swift in Sources */, + 6F0ED544252B00B000ECE97B /* LabeledButton.swift in Sources */, + 6FE4F0F9257A371200223F22 /* NSBundle+PlaySRG.m in Sources */, + 6FF1EA7425656BE3005FFEEA /* TopicDetailView.swift in Sources */, + 6FE4F153257A7D3C00223F22 /* UIImageView+PlaySRG.m in Sources */, 6FDB2ABA24E5916F00FF286E /* ImageView.swift in Sources */, 6FFCC5E424EFBF2100805B0F /* RadioChannel.swift in Sources */, 6F355AA324E65CB400F1500E /* HeroMediaCell.swift in Sources */, 6FC413D224EEEDC900FDF806 /* RadioChannel.m in Sources */, + 6FFFB99D252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */, 6FF60CC924F2FA8D006013C2 /* CollectionView.swift in Sources */, 6FC413D124EEEDC900FDF806 /* FirebaseConfiguration.m in Sources */, + 6F85F77D2567ED1500AC8286 /* ChannelServiceSetup.m in Sources */, 6F1D31B324E5369A005C778B /* ApplicationConfiguration.swift in Sources */, 6F1F80662508ECA800FD8FE7 /* ShowsView.swift in Sources */, - 6F4BCBE124EFF7C3001ABA17 /* Formatters.swift in Sources */, - 6F24913325248CDB00621C27 /* FirebaseStorageFix.m in Sources */, + 6F5A792A256EE61200E884F2 /* HistoryModel.swift in Sources */, 6FCC73BA24E6F8EE00ECF73F /* HomeView.swift in Sources */, + 6F151E27256BF5CF009082F8 /* ProgressBar.swift in Sources */, 6FB1ADFA24EFEF2C00E80C1E /* ShowCell.swift in Sources */, 6F331D6624D076C800C096AB /* SearchView.swift in Sources */, + 6FE4F13A257A372800223F22 /* PlayErrors.m in Sources */, + 6F426B4225680A8F00DD22AB /* BlockingOverlay.swift in Sources */, 6F331CE924D06B8200C096AB /* AudiosView.swift in Sources */, 6F331CE724D06B8200C096AB /* AppDelegate.swift in Sources */, + 08B971CD256EF3FF00195901 /* AnalyticsConstants.m in Sources */, 6F5E63A5250CB32E00056160 /* Modifiers.swift in Sources */, + 085C2B09256472F800F57D38 /* ShowsModel.swift in Sources */, 6F5AA59725024C4800718420 /* UIImage+PlaySRG.m in Sources */, + 6F85F7F02567ED3C00AC8286 /* SRGProgram+PlaySRG.m in Sources */, 6FC413D424EEEDC900FDF806 /* HomeSection.m in Sources */, 6FA84A3224E452F800B1A56E /* HomeModel.swift in Sources */, - 6FFF1632250A20EF0053CDA6 /* FocusDetector.swift in Sources */, + 6F85F7502567ED0D00AC8286 /* ChannelService.m in Sources */, + 6F3D67E1256827DD0030922B /* SRGChannel+PlaySRG.m in Sources */, + 6FF12AA2256C58FE0042F446 /* Logger.swift in Sources */, + 6FFF1632250A20EF0053CDA6 /* FocusTracker.swift in Sources */, + 6F426B142567FC6F00DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */, + 6F61CCAA256E419F00E42E31 /* History.m in Sources */, + 6FA14535254729DA006E8D3B /* FocusableRegion.swift in Sources */, 6FC413D324EEEDC900FDF806 /* TopicSection.m in Sources */, 6FC413F724EF0DEA00FDF806 /* SRGMedia+PlaySRG.m in Sources */, + 6F85F7AF2567ED2A00AC8286 /* NSTimer+PlaySRG.m in Sources */, 6F82430C250637BE00E931A0 /* Fonts.swift in Sources */, + 6FE4F0CC257A368B00223F22 /* ActivityIndicator.swift in Sources */, 6F331D6024D0769A00C096AB /* LiveView.swift in Sources */, 6F3F1AB3250286DA000FF4DD /* UIColor+PlaySRG.m in Sources */, 6FDB2ABF24E5916F00FF286E /* RedactingView.swift in Sources */, + 6FFFB9B7252CA310004E40AE /* MediaDetailModel.swift in Sources */, + 6F975739256EEEC4004DA73C /* HistoryView.swift in Sources */, + 6FF129DD256C0D370042F446 /* PlayDurationFormatter.m in Sources */, + 08742C5925481CC8006E2DCE /* ShowDetailView.swift in Sources */, 6F331D6C24D076D500C096AB /* ProfileView.swift in Sources */, + 6F85F7822567ED2000AC8286 /* ForegroundTimer.m in Sources */, + 6F85F81E2567F0D100AC8286 /* LiveMediaCell.swift in Sources */, 6FDB2AC524E5923600FF286E /* MediaCell.swift in Sources */, + 6FF129C4256C0A9D0042F446 /* NSDateFormatter+PlaySRG.m in Sources */, 6FDFD88C24E6BC9200F20382 /* TopicCell.swift in Sources */, 6FC413D024EEEDC900FDF806 /* ApplicationSection.m in Sources */, 6FF8F156250609CF009A741F /* MediaDescription.swift in Sources */, + 6F151E55256BFCEB009082F8 /* Extensions.swift in Sources */, 6F331D5A24D0766500C096AB /* VideosView.swift in Sources */, 6FC413D624EEEDC900FDF806 /* Channel.m in Sources */, + 08FD789D253E0D3500EF302A /* Foundation+Extensions.swift in Sources */, 6FC413CF24EEEDC900FDF806 /* TVChannel.m in Sources */, - 6F3F1ABA25060496000FF4DD /* MediaVisual.swift in Sources */, + 6F3F1ABA25060496000FF4DD /* MediaVisualView.swift in Sources */, + 6F4F78AF252AF7670011DBCA /* DurationLabel.swift in Sources */, + 6F298860255ADDCD007A8E2B /* Errors.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7211,43 +7824,80 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 086499B324F69FF20027373E /* MediaDetailView.swift in Sources */, + 6F79E0AB2541647400A28E79 /* Colors.swift in Sources */, 6FC413DD24EEEDC900FDF806 /* ApplicationConfiguration.m in Sources */, + 081456322547740600BB7CA6 /* Badges.swift in Sources */, 6F026EEE250134FE00BAE8D1 /* UIView+PlaySRG.m in Sources */, + 6F6C0FBD257AAF250077322C /* NSBundle+PlaySRG.m in Sources */, + 6F0E39CE25558A0F004C3A12 /* ShowDetailModel.swift in Sources */, + 6FF1EA47256569FC005FFEEA /* TopicDetailModel.swift in Sources */, + 0805703F2540E0FC00A59C9D /* Navigation.swift in Sources */, + 6F0ED545252B00B000ECE97B /* LabeledButton.swift in Sources */, + 6FE4F10E257A371300223F22 /* NSBundle+PlaySRG.m in Sources */, + 6FF1EA7525656BE3005FFEEA /* TopicDetailView.swift in Sources */, + 6FE4F154257A7D3C00223F22 /* UIImageView+PlaySRG.m in Sources */, 6FDB2ABB24E5916F00FF286E /* ImageView.swift in Sources */, 6FFCC5E524EFBF2100805B0F /* RadioChannel.swift in Sources */, 6F355AA424E65CB400F1500E /* HeroMediaCell.swift in Sources */, 6FC413DA24EEEDC900FDF806 /* RadioChannel.m in Sources */, + 6FFFB99E252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */, 6FF60CCA24F2FA8D006013C2 /* CollectionView.swift in Sources */, 6FC413D924EEEDC900FDF806 /* FirebaseConfiguration.m in Sources */, + 6F85F77E2567ED1500AC8286 /* ChannelServiceSetup.m in Sources */, 6F1D31B424E5369A005C778B /* ApplicationConfiguration.swift in Sources */, 6F1F80672508ECA800FD8FE7 /* ShowsView.swift in Sources */, - 6F4BCBE224EFF7C3001ABA17 /* Formatters.swift in Sources */, - 6F24913425248CDB00621C27 /* FirebaseStorageFix.m in Sources */, + 6F5A792B256EE61200E884F2 /* HistoryModel.swift in Sources */, 6FCC73BB24E6F8EE00ECF73F /* HomeView.swift in Sources */, + 6F151E28256BF5CF009082F8 /* ProgressBar.swift in Sources */, 6FB1ADFB24EFEF2C00E80C1E /* ShowCell.swift in Sources */, 6F331D6724D076C800C096AB /* SearchView.swift in Sources */, + 6FE4F13B257A372800223F22 /* PlayErrors.m in Sources */, + 6F426B4325680A8F00DD22AB /* BlockingOverlay.swift in Sources */, 6F4FCAF824D06FF7002675EF /* AudiosView.swift in Sources */, 6F4FCAF724D06FF7002675EF /* AppDelegate.swift in Sources */, + 08B971E2256EF40000195901 /* AnalyticsConstants.m in Sources */, 6F5E63A6250CB32E00056160 /* Modifiers.swift in Sources */, + 085C2B0A256472F800F57D38 /* ShowsModel.swift in Sources */, 6F5AA59825024C4900718420 /* UIImage+PlaySRG.m in Sources */, + 6F85F8052567ED3D00AC8286 /* SRGProgram+PlaySRG.m in Sources */, 6FC413DC24EEEDC900FDF806 /* HomeSection.m in Sources */, 6FA84A3324E452F800B1A56E /* HomeModel.swift in Sources */, - 6FFF1633250A20EF0053CDA6 /* FocusDetector.swift in Sources */, + 6F85F7652567ED0E00AC8286 /* ChannelService.m in Sources */, + 6F3D67F6256827DE0030922B /* SRGChannel+PlaySRG.m in Sources */, + 6FF12AA3256C58FE0042F446 /* Logger.swift in Sources */, + 6FFF1633250A20EF0053CDA6 /* FocusTracker.swift in Sources */, + 6F426B292567FC7000DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */, + 6F61CCAB256E419F00E42E31 /* History.m in Sources */, + 6FA14536254729DA006E8D3B /* FocusableRegion.swift in Sources */, 6FC413DB24EEEDC900FDF806 /* TopicSection.m in Sources */, 6FC413F824EF0DEB00FDF806 /* SRGMedia+PlaySRG.m in Sources */, + 6F85F7C42567ED2B00AC8286 /* NSTimer+PlaySRG.m in Sources */, 6F82430D250637BE00E931A0 /* Fonts.swift in Sources */, + 6FE4F0CD257A368B00223F22 /* ActivityIndicator.swift in Sources */, 6F331D6124D0769A00C096AB /* LiveView.swift in Sources */, 6F3F1AB6250286DC000FF4DD /* UIColor+PlaySRG.m in Sources */, 6FDB2AC024E5916F00FF286E /* RedactingView.swift in Sources */, + 6FFFB9B8252CA310004E40AE /* MediaDetailModel.swift in Sources */, + 6F97573A256EEEC4004DA73C /* HistoryView.swift in Sources */, + 6FF129F2256C0D370042F446 /* PlayDurationFormatter.m in Sources */, + 08742C5A25481CC8006E2DCE /* ShowDetailView.swift in Sources */, 6F331D6D24D076D500C096AB /* ProfileView.swift in Sources */, + 6F85F7972567ED2000AC8286 /* ForegroundTimer.m in Sources */, + 6F85F81F2567F0D100AC8286 /* LiveMediaCell.swift in Sources */, 6FDB2AC624E5923600FF286E /* MediaCell.swift in Sources */, + 6FF129C5256C0A9E0042F446 /* NSDateFormatter+PlaySRG.m in Sources */, 6FDFD88D24E6BC9200F20382 /* TopicCell.swift in Sources */, 6FC413D824EEEDC900FDF806 /* ApplicationSection.m in Sources */, 6FF8F157250609CF009A741F /* MediaDescription.swift in Sources */, + 6F151E56256BFCEB009082F8 /* Extensions.swift in Sources */, 6F331D5B24D0766500C096AB /* VideosView.swift in Sources */, 6FC413DE24EEEDC900FDF806 /* Channel.m in Sources */, + 08FD789E253E0D3500EF302A /* Foundation+Extensions.swift in Sources */, 6FC413D724EEEDC900FDF806 /* TVChannel.m in Sources */, - 6F3F1ABB25060496000FF4DD /* MediaVisual.swift in Sources */, + 6F3F1ABB25060496000FF4DD /* MediaVisualView.swift in Sources */, + 6F4F78B0252AF7670011DBCA /* DurationLabel.swift in Sources */, + 6F298861255ADDCD007A8E2B /* Errors.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7255,43 +7905,80 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 086499B424F69FF20027373E /* MediaDetailView.swift in Sources */, + 6F79E0AC2541647400A28E79 /* Colors.swift in Sources */, 6FC413E524EEEDCA00FDF806 /* ApplicationConfiguration.m in Sources */, + 081456332547740600BB7CA6 /* Badges.swift in Sources */, 6F026EEF250134FE00BAE8D1 /* UIView+PlaySRG.m in Sources */, + 6F6C0FBE257AAF250077322C /* NSBundle+PlaySRG.m in Sources */, + 6F0E39CF25558A0F004C3A12 /* ShowDetailModel.swift in Sources */, + 6FF1EA48256569FC005FFEEA /* TopicDetailModel.swift in Sources */, + 080570402540E0FC00A59C9D /* Navigation.swift in Sources */, + 6F0ED546252B00B000ECE97B /* LabeledButton.swift in Sources */, + 6FE4F10F257A371400223F22 /* NSBundle+PlaySRG.m in Sources */, + 6FF1EA7625656BE3005FFEEA /* TopicDetailView.swift in Sources */, + 6FE4F155257A7D3D00223F22 /* UIImageView+PlaySRG.m in Sources */, 6FDB2ABC24E5916F00FF286E /* ImageView.swift in Sources */, 6FFCC5E624EFBF2100805B0F /* RadioChannel.swift in Sources */, 6F355AA524E65CB400F1500E /* HeroMediaCell.swift in Sources */, 6FC413E224EEEDCA00FDF806 /* RadioChannel.m in Sources */, + 6FFFB99F252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */, 6FF60CCB24F2FA8D006013C2 /* CollectionView.swift in Sources */, 6FC413E124EEEDCA00FDF806 /* FirebaseConfiguration.m in Sources */, + 6F85F77F2567ED1600AC8286 /* ChannelServiceSetup.m in Sources */, 6F1D31B524E5369A005C778B /* ApplicationConfiguration.swift in Sources */, 6F1F80682508ECA800FD8FE7 /* ShowsView.swift in Sources */, - 6F4BCBE324EFF7C3001ABA17 /* Formatters.swift in Sources */, - 6F24913525248CDB00621C27 /* FirebaseStorageFix.m in Sources */, + 6F5A792C256EE61200E884F2 /* HistoryModel.swift in Sources */, 6FCC73BC24E6F8EE00ECF73F /* HomeView.swift in Sources */, + 6F151E29256BF5CF009082F8 /* ProgressBar.swift in Sources */, 6FB1ADFC24EFEF2C00E80C1E /* ShowCell.swift in Sources */, 6F331D6824D076C800C096AB /* SearchView.swift in Sources */, + 6FE4F13C257A372800223F22 /* PlayErrors.m in Sources */, + 6F426B4425680A8F00DD22AB /* BlockingOverlay.swift in Sources */, 6F4FCAFA24D06FF7002675EF /* AudiosView.swift in Sources */, 6F4FCAF924D06FF7002675EF /* AppDelegate.swift in Sources */, + 08B971E3256EF40100195901 /* AnalyticsConstants.m in Sources */, 6F5E63A7250CB32E00056160 /* Modifiers.swift in Sources */, + 085C2B0B256472F800F57D38 /* ShowsModel.swift in Sources */, 6F5AA59925024C4900718420 /* UIImage+PlaySRG.m in Sources */, + 6F85F8062567ED3D00AC8286 /* SRGProgram+PlaySRG.m in Sources */, 6FC413E424EEEDCA00FDF806 /* HomeSection.m in Sources */, 6FA84A3424E452F800B1A56E /* HomeModel.swift in Sources */, - 6FFF1634250A20EF0053CDA6 /* FocusDetector.swift in Sources */, + 6F85F7662567ED0F00AC8286 /* ChannelService.m in Sources */, + 6F3D67F7256827DF0030922B /* SRGChannel+PlaySRG.m in Sources */, + 6FF12AA4256C58FE0042F446 /* Logger.swift in Sources */, + 6FFF1634250A20EF0053CDA6 /* FocusTracker.swift in Sources */, + 6F426B2A2567FC7100DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */, + 6F61CCAC256E41A000E42E31 /* History.m in Sources */, + 6FA14537254729DA006E8D3B /* FocusableRegion.swift in Sources */, 6FC413E324EEEDCA00FDF806 /* TopicSection.m in Sources */, 6FC413F924EF0DEC00FDF806 /* SRGMedia+PlaySRG.m in Sources */, + 6F85F7C52567ED2C00AC8286 /* NSTimer+PlaySRG.m in Sources */, 6F82430E250637BE00E931A0 /* Fonts.swift in Sources */, + 6FE4F0CE257A368B00223F22 /* ActivityIndicator.swift in Sources */, 6F331D6224D0769A00C096AB /* LiveView.swift in Sources */, 6F3F1AB5250286DB000FF4DD /* UIColor+PlaySRG.m in Sources */, 6FDB2AC124E5916F00FF286E /* RedactingView.swift in Sources */, + 6FFFB9B9252CA310004E40AE /* MediaDetailModel.swift in Sources */, + 6F97573B256EEEC4004DA73C /* HistoryView.swift in Sources */, + 6FF129F3256C0D380042F446 /* PlayDurationFormatter.m in Sources */, + 08742C5B25481CC8006E2DCE /* ShowDetailView.swift in Sources */, 6F331D6E24D076D500C096AB /* ProfileView.swift in Sources */, + 6F85F7AC2567ED2100AC8286 /* ForegroundTimer.m in Sources */, + 6F85F8202567F0D100AC8286 /* LiveMediaCell.swift in Sources */, 6FDB2AC724E5923600FF286E /* MediaCell.swift in Sources */, + 6FF129C6256C0A9E0042F446 /* NSDateFormatter+PlaySRG.m in Sources */, 6FDFD88E24E6BC9200F20382 /* TopicCell.swift in Sources */, 6FC413E024EEEDCA00FDF806 /* ApplicationSection.m in Sources */, 6FF8F158250609CF009A741F /* MediaDescription.swift in Sources */, + 6F151E57256BFCEB009082F8 /* Extensions.swift in Sources */, 6F331D5C24D0766500C096AB /* VideosView.swift in Sources */, 6FC413E624EEEDCA00FDF806 /* Channel.m in Sources */, + 08FD789F253E0D3500EF302A /* Foundation+Extensions.swift in Sources */, 6FC413DF24EEEDCA00FDF806 /* TVChannel.m in Sources */, - 6F3F1ABC25060496000FF4DD /* MediaVisual.swift in Sources */, + 6F3F1ABC25060496000FF4DD /* MediaVisualView.swift in Sources */, + 6F4F78B1252AF7670011DBCA /* DurationLabel.swift in Sources */, + 6F298862255ADDCD007A8E2B /* Errors.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7299,43 +7986,80 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 086499B524F69FF20027373E /* MediaDetailView.swift in Sources */, + 6F79E0AD2541647400A28E79 /* Colors.swift in Sources */, 6FC413ED24EEEDCA00FDF806 /* ApplicationConfiguration.m in Sources */, + 081456342547740600BB7CA6 /* Badges.swift in Sources */, 6F026EF0250134FF00BAE8D1 /* UIView+PlaySRG.m in Sources */, + 6F6C0FBF257AAF260077322C /* NSBundle+PlaySRG.m in Sources */, + 6F0E39D025558A0F004C3A12 /* ShowDetailModel.swift in Sources */, + 6FF1EA49256569FC005FFEEA /* TopicDetailModel.swift in Sources */, + 080570412540E0FC00A59C9D /* Navigation.swift in Sources */, + 6F0ED547252B00B000ECE97B /* LabeledButton.swift in Sources */, + 6FE4F124257A371500223F22 /* NSBundle+PlaySRG.m in Sources */, + 6FF1EA7725656BE3005FFEEA /* TopicDetailView.swift in Sources */, + 6FE4F156257A7D3D00223F22 /* UIImageView+PlaySRG.m in Sources */, 6FDB2ABD24E5916F00FF286E /* ImageView.swift in Sources */, 6FFCC5E724EFBF2100805B0F /* RadioChannel.swift in Sources */, 6F355AA624E65CB400F1500E /* HeroMediaCell.swift in Sources */, 6FC413EA24EEEDCA00FDF806 /* RadioChannel.m in Sources */, + 6FFFB9A0252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */, 6FF60CCC24F2FA8D006013C2 /* CollectionView.swift in Sources */, 6FC413E924EEEDCA00FDF806 /* FirebaseConfiguration.m in Sources */, + 6F85F7802567ED1600AC8286 /* ChannelServiceSetup.m in Sources */, 6F1D31B624E5369A005C778B /* ApplicationConfiguration.swift in Sources */, 6F1F80692508ECA800FD8FE7 /* ShowsView.swift in Sources */, - 6F4BCBE424EFF7C3001ABA17 /* Formatters.swift in Sources */, - 6F24913625248CDB00621C27 /* FirebaseStorageFix.m in Sources */, + 6F5A792D256EE61200E884F2 /* HistoryModel.swift in Sources */, 6FCC73BD24E6F8EE00ECF73F /* HomeView.swift in Sources */, + 6F151E2A256BF5CF009082F8 /* ProgressBar.swift in Sources */, 6FB1ADFD24EFEF2C00E80C1E /* ShowCell.swift in Sources */, 6F331D6924D076C800C096AB /* SearchView.swift in Sources */, + 6FE4F13D257A372900223F22 /* PlayErrors.m in Sources */, + 6F426B4525680A8F00DD22AB /* BlockingOverlay.swift in Sources */, 6F4FCAFC24D06FF7002675EF /* AudiosView.swift in Sources */, 6F4FCAFB24D06FF7002675EF /* AppDelegate.swift in Sources */, + 08B971E4256EF40200195901 /* AnalyticsConstants.m in Sources */, 6F5E63A8250CB32E00056160 /* Modifiers.swift in Sources */, + 085C2B0C256472F800F57D38 /* ShowsModel.swift in Sources */, 6F5AA59A25024C4A00718420 /* UIImage+PlaySRG.m in Sources */, + 6F85F8072567ED3E00AC8286 /* SRGProgram+PlaySRG.m in Sources */, 6FC413EC24EEEDCA00FDF806 /* HomeSection.m in Sources */, 6FA84A3524E452F800B1A56E /* HomeModel.swift in Sources */, - 6FFF1635250A20EF0053CDA6 /* FocusDetector.swift in Sources */, + 6F85F7672567ED0F00AC8286 /* ChannelService.m in Sources */, + 6F3D680C256827DF0030922B /* SRGChannel+PlaySRG.m in Sources */, + 6FF12AA5256C58FE0042F446 /* Logger.swift in Sources */, + 6FFF1635250A20EF0053CDA6 /* FocusTracker.swift in Sources */, + 6F426B2B2567FC7100DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */, + 6F61CCAD256E41A000E42E31 /* History.m in Sources */, + 6FA14538254729DA006E8D3B /* FocusableRegion.swift in Sources */, 6FC413EB24EEEDCA00FDF806 /* TopicSection.m in Sources */, 6FC413FA24EF0DED00FDF806 /* SRGMedia+PlaySRG.m in Sources */, + 6F85F7DA2567ED2C00AC8286 /* NSTimer+PlaySRG.m in Sources */, 6F82430F250637BE00E931A0 /* Fonts.swift in Sources */, + 6FE4F0CF257A368B00223F22 /* ActivityIndicator.swift in Sources */, 6F331D6324D0769A00C096AB /* LiveView.swift in Sources */, 6F3F1AB7250286DC000FF4DD /* UIColor+PlaySRG.m in Sources */, 6FDB2AC224E5916F00FF286E /* RedactingView.swift in Sources */, + 6FFFB9BA252CA310004E40AE /* MediaDetailModel.swift in Sources */, + 6F97573C256EEEC4004DA73C /* HistoryView.swift in Sources */, + 6FF12A08256C0D390042F446 /* PlayDurationFormatter.m in Sources */, + 08742C5C25481CC8006E2DCE /* ShowDetailView.swift in Sources */, 6F331D6F24D076D500C096AB /* ProfileView.swift in Sources */, + 6F85F7AD2567ED2100AC8286 /* ForegroundTimer.m in Sources */, + 6F85F8212567F0D100AC8286 /* LiveMediaCell.swift in Sources */, 6FDB2AC824E5923600FF286E /* MediaCell.swift in Sources */, + 6FF129C7256C0A9E0042F446 /* NSDateFormatter+PlaySRG.m in Sources */, 6FDFD88F24E6BC9200F20382 /* TopicCell.swift in Sources */, 6FC413E824EEEDCA00FDF806 /* ApplicationSection.m in Sources */, 6FF8F159250609CF009A741F /* MediaDescription.swift in Sources */, + 6F151E58256BFCEB009082F8 /* Extensions.swift in Sources */, 6F331D5D24D0766500C096AB /* VideosView.swift in Sources */, 6FC413EE24EEEDCA00FDF806 /* Channel.m in Sources */, + 08FD78A0253E0D3500EF302A /* Foundation+Extensions.swift in Sources */, 6FC413E724EEEDCA00FDF806 /* TVChannel.m in Sources */, - 6F3F1ABD25060496000FF4DD /* MediaVisual.swift in Sources */, + 6F3F1ABD25060496000FF4DD /* MediaVisualView.swift in Sources */, + 6F4F78B2252AF7670011DBCA /* DurationLabel.swift in Sources */, + 6F298863255ADDCD007A8E2B /* Errors.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7343,43 +8067,80 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 086499B624F69FF20027373E /* MediaDetailView.swift in Sources */, + 6F79E0AE2541647400A28E79 /* Colors.swift in Sources */, 6FC413F524EEEDCB00FDF806 /* ApplicationConfiguration.m in Sources */, + 081456352547740600BB7CA6 /* Badges.swift in Sources */, 6F026EF1250134FF00BAE8D1 /* UIView+PlaySRG.m in Sources */, + 6F6C0FC0257AAF270077322C /* NSBundle+PlaySRG.m in Sources */, + 6F0E39D125558A0F004C3A12 /* ShowDetailModel.swift in Sources */, + 6FF1EA4A256569FC005FFEEA /* TopicDetailModel.swift in Sources */, + 080570422540E0FC00A59C9D /* Navigation.swift in Sources */, + 6F0ED548252B00B000ECE97B /* LabeledButton.swift in Sources */, + 6FE4F125257A371500223F22 /* NSBundle+PlaySRG.m in Sources */, + 6FF1EA7825656BE3005FFEEA /* TopicDetailView.swift in Sources */, + 6FE4F157257A7D3D00223F22 /* UIImageView+PlaySRG.m in Sources */, 6FDB2ABE24E5916F00FF286E /* ImageView.swift in Sources */, 6FFCC5E824EFBF2100805B0F /* RadioChannel.swift in Sources */, 6F355AA724E65CB400F1500E /* HeroMediaCell.swift in Sources */, 6FC413F224EEEDCB00FDF806 /* RadioChannel.m in Sources */, + 6FFFB9A1252C9EAC004E40AE /* UIKit+Extensions.swift in Sources */, 6FF60CCD24F2FA8D006013C2 /* CollectionView.swift in Sources */, 6FC413F124EEEDCB00FDF806 /* FirebaseConfiguration.m in Sources */, + 6F85F7812567ED1700AC8286 /* ChannelServiceSetup.m in Sources */, 6F1D31B724E5369A005C778B /* ApplicationConfiguration.swift in Sources */, 6F1F806A2508ECA800FD8FE7 /* ShowsView.swift in Sources */, - 6F4BCBE524EFF7C3001ABA17 /* Formatters.swift in Sources */, - 6F24913725248CDB00621C27 /* FirebaseStorageFix.m in Sources */, + 6F5A792E256EE61200E884F2 /* HistoryModel.swift in Sources */, 6FCC73BE24E6F8EE00ECF73F /* HomeView.swift in Sources */, + 6F151E2B256BF5CF009082F8 /* ProgressBar.swift in Sources */, 6FB1ADFE24EFEF2C00E80C1E /* ShowCell.swift in Sources */, 6F331D6A24D076C800C096AB /* SearchView.swift in Sources */, + 6FE4F13E257A372900223F22 /* PlayErrors.m in Sources */, + 6F426B4625680A8F00DD22AB /* BlockingOverlay.swift in Sources */, 6F4FCAFE24D06FF8002675EF /* AudiosView.swift in Sources */, 6F4FCAFD24D06FF8002675EF /* AppDelegate.swift in Sources */, + 08B971E5256EF40300195901 /* AnalyticsConstants.m in Sources */, 6F5E63A9250CB32E00056160 /* Modifiers.swift in Sources */, + 085C2B0D256472F800F57D38 /* ShowsModel.swift in Sources */, 6F5AA59B25024C4B00718420 /* UIImage+PlaySRG.m in Sources */, + 6F85F8082567ED3E00AC8286 /* SRGProgram+PlaySRG.m in Sources */, 6FC413F424EEEDCB00FDF806 /* HomeSection.m in Sources */, 6FA84A3624E452F800B1A56E /* HomeModel.swift in Sources */, - 6FFF1636250A20EF0053CDA6 /* FocusDetector.swift in Sources */, + 6F85F7682567ED0F00AC8286 /* ChannelService.m in Sources */, + 6F3D680D256827DF0030922B /* SRGChannel+PlaySRG.m in Sources */, + 6FF12AA6256C58FE0042F446 /* Logger.swift in Sources */, + 6FFF1636250A20EF0053CDA6 /* FocusTracker.swift in Sources */, + 6F426B2C2567FC7200DD22AB /* SRGProgramComposition+PlaySRG.m in Sources */, + 6F5A7914256ED7E000E884F2 /* History.m in Sources */, + 6FA14539254729DA006E8D3B /* FocusableRegion.swift in Sources */, 6FC413F324EEEDCB00FDF806 /* TopicSection.m in Sources */, 6FC413FB24EF0DEE00FDF806 /* SRGMedia+PlaySRG.m in Sources */, + 6F85F7DB2567ED2C00AC8286 /* NSTimer+PlaySRG.m in Sources */, 6F824310250637BE00E931A0 /* Fonts.swift in Sources */, + 6FE4F0D0257A368B00223F22 /* ActivityIndicator.swift in Sources */, 6F331D6424D0769A00C096AB /* LiveView.swift in Sources */, 6F3F1AB8250286DD000FF4DD /* UIColor+PlaySRG.m in Sources */, 6FDB2AC324E5916F00FF286E /* RedactingView.swift in Sources */, + 6FFFB9BB252CA310004E40AE /* MediaDetailModel.swift in Sources */, + 6F97573D256EEEC4004DA73C /* HistoryView.swift in Sources */, + 6FF12A09256C0D390042F446 /* PlayDurationFormatter.m in Sources */, + 08742C5D25481CC8006E2DCE /* ShowDetailView.swift in Sources */, 6F331D7024D076D500C096AB /* ProfileView.swift in Sources */, + 6F85F7AE2567ED2300AC8286 /* ForegroundTimer.m in Sources */, + 6F85F8222567F0D100AC8286 /* LiveMediaCell.swift in Sources */, 6FDB2AC924E5923600FF286E /* MediaCell.swift in Sources */, + 6FF129C8256C0A9F0042F446 /* NSDateFormatter+PlaySRG.m in Sources */, 6FDFD89024E6BC9200F20382 /* TopicCell.swift in Sources */, 6FC413F024EEEDCB00FDF806 /* ApplicationSection.m in Sources */, 6FF8F15A250609CF009A741F /* MediaDescription.swift in Sources */, + 6F151E59256BFCEB009082F8 /* Extensions.swift in Sources */, 6F331D5E24D0766500C096AB /* VideosView.swift in Sources */, 6FC413F624EEEDCB00FDF806 /* Channel.m in Sources */, + 08FD78A1253E0D3500EF302A /* Foundation+Extensions.swift in Sources */, 6FC413EF24EEEDCB00FDF806 /* TVChannel.m in Sources */, - 6F3F1ABE25060496000FF4DD /* MediaVisual.swift in Sources */, + 6F3F1ABE25060496000FF4DD /* MediaVisualView.swift in Sources */, + 6F4F78B3252AF7670011DBCA /* DurationLabel.swift in Sources */, + 6F298864255ADDCD007A8E2B /* Errors.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7604,7 +8365,7 @@ /* Begin XCBuildConfiguration section */ 08B77A4D2409C06000A3BC3B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899F255B605F00E17515 /* Play SRF Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -7625,8 +8386,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -7636,7 +8395,7 @@ }; 08B77A4E2409C06000A3BC3B /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899F255B605F00E17515 /* Play SRF Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7660,8 +8419,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7671,7 +8428,7 @@ }; 08B77A4F2409C06000A3BC3B /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899F255B605F00E17515 /* Play SRF Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7695,8 +8452,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7706,7 +8461,7 @@ }; 08B77A502409C06000A3BC3B /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899F255B605F00E17515 /* Play SRF Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7730,8 +8485,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7741,7 +8494,7 @@ }; 08B77AAD2409E0A300A3BC3B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899E255B605E00E17515 /* Play RTS Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -7762,8 +8515,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -7773,7 +8524,7 @@ }; 08B77AAE2409E0A300A3BC3B /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899E255B605E00E17515 /* Play RTS Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7797,8 +8548,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7808,7 +8557,7 @@ }; 08B77AAF2409E0A300A3BC3B /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899E255B605E00E17515 /* Play RTS Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7832,8 +8581,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7843,7 +8590,7 @@ }; 08B77AB02409E0A300A3BC3B /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899E255B605E00E17515 /* Play RTS Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7867,8 +8614,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7878,7 +8623,7 @@ }; 08B77ABF240A78F200A3BC3B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899B255B605E00E17515 /* Play RSI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -7899,8 +8644,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -7910,7 +8653,7 @@ }; 08B77AC0240A78F200A3BC3B /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899B255B605E00E17515 /* Play RSI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7934,8 +8677,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7945,7 +8686,7 @@ }; 08B77AC1240A78F200A3BC3B /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899B255B605E00E17515 /* Play RSI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NONNULL = YES; @@ -7969,8 +8710,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -7980,7 +8719,7 @@ }; 08B77AC2240A78F200A3BC3B /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899B255B605E00E17515 /* Play RSI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8004,8 +8743,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8015,7 +8752,7 @@ }; 08B77AD0240A7E3200A3BC3B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899C255B605E00E17515 /* Play RTR Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -8036,8 +8773,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -8047,7 +8782,7 @@ }; 08B77AD1240A7E3200A3BC3B /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899C255B605E00E17515 /* Play RTR Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8071,8 +8806,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8082,7 +8815,7 @@ }; 08B77AD2240A7E3200A3BC3B /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899C255B605E00E17515 /* Play RTR Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8106,8 +8839,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8117,7 +8848,7 @@ }; 08B77AD3240A7E3200A3BC3B /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E7899C255B605E00E17515 /* Play RTR Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8141,8 +8872,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8152,7 +8881,7 @@ }; 08B77ADF240A7E5000A3BC3B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E789A0255B605F00E17515 /* Play SWI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -8173,8 +8902,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -8184,7 +8911,7 @@ }; 08B77AE0240A7E5000A3BC3B /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E789A0255B605F00E17515 /* Play SWI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8208,8 +8935,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8219,7 +8944,7 @@ }; 08B77AE1240A7E5000A3BC3B /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E789A0255B605F00E17515 /* Play SWI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8243,8 +8968,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8254,7 +8977,7 @@ }; 08B77AE2240A7E5000A3BC3B /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08B77AFC240A8DF100A3BC3B /* Screenshots.xcconfig */; + baseConfigurationReference = 08E789A0255B605F00E17515 /* Play SWI Screenshots.xcconfig */; buildSettings = { APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NONNULL = YES; @@ -8278,8 +9001,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).screenshots-uitest"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -8317,6 +9038,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -8386,6 +9108,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -8424,7 +9147,6 @@ baseConfigurationReference = 6F1EBE1C222EBB7E003E0722 /* Play SRF.debug.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8442,11 +9164,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playsrf; }; name = Debug; }; @@ -8455,7 +9174,6 @@ baseConfigurationReference = 6F1EBE1A222EBB7D003E0722 /* Play SRF.appstore.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8473,10 +9191,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playsrf; }; name = AppStore; }; @@ -8485,7 +9200,6 @@ baseConfigurationReference = 6F1EBE0B222EBB7B003E0722 /* Play RTS.debug.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8503,11 +9217,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrts; }; name = Debug; }; @@ -8516,7 +9227,6 @@ baseConfigurationReference = 6F1EBE0C222EBB7C003E0722 /* Play RTS.appstore.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8534,10 +9244,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrts; }; name = AppStore; }; @@ -8546,7 +9253,6 @@ baseConfigurationReference = 6F1EBE0F222EBB7C003E0722 /* Play RSI.debug.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8564,11 +9270,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrsi; }; name = Debug; }; @@ -8577,7 +9280,6 @@ baseConfigurationReference = 6F1EBE19222EBB7D003E0722 /* Play RSI.appstore.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8595,10 +9297,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrsi; }; name = AppStore; }; @@ -8607,7 +9306,6 @@ baseConfigurationReference = 6F1EBE0E222EBB7C003E0722 /* Play RTR.debug.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8625,11 +9323,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrtr; }; name = Debug; }; @@ -8638,7 +9333,6 @@ baseConfigurationReference = 6F1EBE17222EBB7D003E0722 /* Play RTR.appstore.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8656,10 +9350,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrtr; }; name = AppStore; }; @@ -8668,7 +9359,6 @@ baseConfigurationReference = 6F1EBE14222EBB7D003E0722 /* Play SWI.debug.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8685,11 +9375,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playswi; }; name = Debug; }; @@ -8698,7 +9385,6 @@ baseConfigurationReference = 6F1EBE1E222EBB7E003E0722 /* Play SWI.appstore.xcconfig */; buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8715,10 +9401,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playswi; }; name = AppStore; }; @@ -8728,7 +9411,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; APP_BUNDLE_IDENTIFIER = "$(APP_BASE_IDENTIFIER)$(BUNDLE_IDENTIFIER_SUFFIX)"; APP_GROUP_IDENTIFIER = "group.$(APP_BUNDLE_IDENTIFIER)"; - BUILD_NAME = ""; BUNDLE_DISPLAY_NAME = "${PRODUCT_NAME}$(BUNDLE_DISPLAY_NAME_SUFFIX)"; BUNDLE_DISPLAY_NAME_SUFFIX = " 🎯"; BUNDLE_IDENTIFIER_SUFFIX = .beta; @@ -8753,6 +9435,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -8793,7 +9476,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8811,10 +9493,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playsrf; }; name = Beta; }; @@ -8824,7 +9503,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8842,10 +9520,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrts; }; name = Beta; }; @@ -8855,7 +9530,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8873,10 +9547,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrsi; }; name = Beta; }; @@ -8886,7 +9557,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8904,10 +9574,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrtr; }; name = Beta; }; @@ -8917,7 +9584,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -8934,10 +9600,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playswi; }; name = Beta; }; @@ -8971,6 +9634,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -9011,7 +9675,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -9029,12 +9692,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playsrf; }; name = Nightly; }; @@ -9044,7 +9704,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -9062,12 +9721,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrts; }; name = Nightly; }; @@ -9077,7 +9733,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -9095,12 +9750,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrsi; }; name = Nightly; }; @@ -9110,7 +9762,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -9128,12 +9779,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playrtr; }; name = Nightly; }; @@ -9143,7 +9791,6 @@ buildSettings = { ACTIVITY_CONTINUATION_DOMAIN = "activitycontinuation:$(DOMAIN)"; APPCENTER_URL = "https://install.appcenter.ms/orgs/$(APPCENTER_OWNER)/apps/$(APPCENTER_APPNAME)?skip_registration=true"; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; APP_LINKS_DOMAIN = "applinks:$(DOMAIN)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -9160,19 +9807,15 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_VERSION = 5.0; - URL_BASE_SCHEME = playswi; }; name = Nightly; }; 6F0CFB3B20C94DF6006B2CE4 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890B255B59D800E17515 /* Play SRF Notification Service Extension.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -9189,8 +9832,6 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -9198,10 +9839,9 @@ }; 6F0CFB3C20C94DF6006B2CE4 /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890B255B59D800E17515 /* Play SRF Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9252,9 +9892,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9263,10 +9900,9 @@ }; 6F0CFB3D20C94DF6006B2CE4 /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890B255B59D800E17515 /* Play SRF Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9317,9 +9953,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9328,10 +9961,9 @@ }; 6F0CFB3E20C94DF6006B2CE4 /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890B255B59D800E17515 /* Play SRF Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9382,9 +10014,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9393,9 +10022,8 @@ }; 6F0CFB4F20C94E77006B2CE4 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890E255B59D800E17515 /* Play RTS Notification Service Extension.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -9412,8 +10040,6 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -9421,10 +10047,9 @@ }; 6F0CFB5020C94E77006B2CE4 /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890E255B59D800E17515 /* Play RTS Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9475,9 +10100,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9486,10 +10108,9 @@ }; 6F0CFB5120C94E77006B2CE4 /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890E255B59D800E17515 /* Play RTS Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9540,9 +10161,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9551,10 +10169,9 @@ }; 6F0CFB5220C94E77006B2CE4 /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890E255B59D800E17515 /* Play RTS Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9605,9 +10222,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9616,9 +10230,8 @@ }; 6F0CFB6220C94E8E006B2CE4 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890D255B59D800E17515 /* Play RSI Notification Service Extension.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -9635,8 +10248,6 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -9644,10 +10255,9 @@ }; 6F0CFB6320C94E8E006B2CE4 /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890D255B59D800E17515 /* Play RSI Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9698,9 +10308,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9709,10 +10316,9 @@ }; 6F0CFB6420C94E8E006B2CE4 /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890D255B59D800E17515 /* Play RSI Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9763,9 +10369,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9774,10 +10377,9 @@ }; 6F0CFB6520C94E8E006B2CE4 /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890D255B59D800E17515 /* Play RSI Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9828,9 +10430,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9839,9 +10438,8 @@ }; 6F0CFB7520C94EC8006B2CE4 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890C255B59D800E17515 /* Play RTR Notification Service Extension.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -9858,8 +10456,6 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -9867,10 +10463,9 @@ }; 6F0CFB7620C94EC8006B2CE4 /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890C255B59D800E17515 /* Play RTR Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9921,9 +10516,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9932,10 +10524,9 @@ }; 6F0CFB7720C94EC8006B2CE4 /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890C255B59D800E17515 /* Play RTR Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -9986,9 +10577,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -9997,10 +10585,9 @@ }; 6F0CFB7820C94EC8006B2CE4 /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E7890C255B59D800E17515 /* Play RTR Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -10051,9 +10638,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -10062,9 +10646,8 @@ }; 6F0CFB8820C94EE5006B2CE4 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E78909255B59D700E17515 /* Play SWI Notification Service Extension.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -10080,8 +10663,6 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -10089,10 +10670,9 @@ }; 6F0CFB8920C94EE5006B2CE4 /* AppStore */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E78909255B59D700E17515 /* Play SWI Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -10142,9 +10722,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -10153,10 +10730,9 @@ }; 6F0CFB8A20C94EE5006B2CE4 /* Beta */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E78909255B59D700E17515 /* Play SWI Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -10206,9 +10782,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -10217,10 +10790,9 @@ }; 6F0CFB8B20C94EE5006B2CE4 /* Nightly */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0814CE0924D85D8F0013D7A9 /* Play iOS.xcconfig */; + baseConfigurationReference = 08E78909255B59D700E17515 /* Play SWI Notification Service Extension.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -10270,9 +10842,6 @@ "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).notification-service-extension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -10283,7 +10852,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6F88409324EBF931007463ED /* Play SRF TV.debug.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.srf.srfplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Debug$(BUILD_NAME_SUFFIX)"; @@ -10305,9 +10873,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SRF"; - SDKROOT = appletvos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -10320,7 +10885,6 @@ baseConfigurationReference = 6F88408A24EBF92D007463ED /* Play SRF TV.appstore.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = ""; @@ -10376,9 +10940,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SRF"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10392,7 +10953,6 @@ baseConfigurationReference = 6F88408724EBF92C007463ED /* Play SRF TV.beta.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Beta$(BUILD_NAME_SUFFIX)"; @@ -10448,9 +11008,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SRF"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10464,7 +11021,6 @@ baseConfigurationReference = 6F88409224EBF930007463ED /* Play SRF TV.nightly.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.srf.srfplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Nightly$(BUILD_NAME_SUFFIX)"; @@ -10520,9 +11076,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SRF"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10535,7 +11088,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6F88408424EBF92B007463ED /* Play RTS TV.debug.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Debug$(BUILD_NAME_SUFFIX)"; @@ -10557,9 +11109,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTS"; - SDKROOT = appletvos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -10572,7 +11121,6 @@ baseConfigurationReference = 6F88408124EBF92A007463ED /* Play RTS TV.appstore.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = ""; @@ -10628,9 +11176,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTS"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10644,7 +11189,6 @@ baseConfigurationReference = 6F88408C24EBF92E007463ED /* Play RTS TV.beta.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Beta$(BUILD_NAME_SUFFIX)"; @@ -10700,9 +11244,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTS"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10716,7 +11257,6 @@ baseConfigurationReference = 6F88408624EBF92C007463ED /* Play RTS TV.nightly.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rts.rtsplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Nightly$(BUILD_NAME_SUFFIX)"; @@ -10772,9 +11312,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTS"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10787,7 +11324,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6F88408924EBF92D007463ED /* Play RSI TV.debug.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Debug$(BUILD_NAME_SUFFIX)"; @@ -10809,9 +11345,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RSI"; - SDKROOT = appletvos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -10824,7 +11357,6 @@ baseConfigurationReference = 6F88408024EBF92A007463ED /* Play RSI TV.appstore.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = ""; @@ -10880,9 +11412,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RSI"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10896,7 +11425,6 @@ baseConfigurationReference = 6F88409124EBF930007463ED /* Play RSI TV.beta.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Beta$(BUILD_NAME_SUFFIX)"; @@ -10952,9 +11480,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RSI"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -10968,7 +11493,6 @@ baseConfigurationReference = 6F88408F24EBF92F007463ED /* Play RSI TV.nightly.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rsi.rsiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Nighty$(BUILD_NAME_SUFFIX)"; @@ -11024,9 +11548,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RSI"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11039,7 +11560,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6F88408B24EBF92D007463ED /* Play RTR TV.debug.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Debug$(BUILD_NAME_SUFFIX)"; @@ -11061,9 +11581,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTR"; - SDKROOT = appletvos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -11076,7 +11593,6 @@ baseConfigurationReference = 6F88408824EBF92C007463ED /* Play RTR TV.appstore.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = ""; @@ -11132,9 +11648,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTR"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11148,7 +11661,6 @@ baseConfigurationReference = 6F88409024EBF930007463ED /* Play RTR TV.beta.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Beta$(BUILD_NAME_SUFFIX)"; @@ -11204,9 +11716,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTR"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11220,7 +11729,6 @@ baseConfigurationReference = 6F88408324EBF92B007463ED /* Play RTR TV.nightly.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.rtr.rtrplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Nightly$(BUILD_NAME_SUFFIX)"; @@ -11276,9 +11784,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play RTR"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11291,7 +11796,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6F88408224EBF92A007463ED /* Play SWI TV.debug.xcconfig */; buildSettings = { - APP_BASE_IDENTIFIER = ch.swi.swiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Debug$(BUILD_NAME_SUFFIX)"; @@ -11313,9 +11817,6 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SWI"; - SDKROOT = appletvos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -11328,7 +11829,6 @@ baseConfigurationReference = 6F88408D24EBF92E007463ED /* Play SWI TV.appstore.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = ""; @@ -11384,9 +11884,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SWI"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11400,7 +11897,6 @@ baseConfigurationReference = 6F88408E24EBF92F007463ED /* Play SWI TV.beta.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Beta$(BUILD_NAME_SUFFIX)"; @@ -11456,9 +11952,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SWI"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11472,7 +11965,6 @@ baseConfigurationReference = 6F88408524EBF92C007463ED /* Play SWI TV.nightly.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BASE_IDENTIFIER = ch.swi.swiplayer; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_NAME = "Nighty$(BUILD_NAME_SUFFIX)"; @@ -11528,9 +12020,6 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; - PRODUCT_NAME = "Play SWI"; - SDKROOT = appletvos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -11805,7 +12294,7 @@ repositoryURL = "https://github.com/SRGSSR/srganalytics-apple.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.0.0; + minimumVersion = 6.1.0; }; }; 6F6DD3A624E449BA003F9437 /* XCRemoteSwiftPackageReference "srgletterbox-apple" */ = { @@ -11813,15 +12302,15 @@ repositoryURL = "https://github.com/SRGSSR/srgletterbox-apple.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 6.0.0; + minimumVersion = 6.1.1; }; }; 6F6DD3A924E449D7003F9437 /* XCRemoteSwiftPackageReference "srgdataprovider-apple" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SRGSSR/srgdataprovider-apple.git"; requirement = { - kind = upToNextMinorVersion; - minimumVersion = 9.0.0; + kind = upToNextMajorVersion; + minimumVersion = 9.1.0; }; }; 6FD40A4625221C2D00E621EF /* XCRemoteSwiftPackageReference "paper-onboarding" */ = { @@ -11832,20 +12321,20 @@ minimumVersion = 6.1.5; }; }; - 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */ = { + 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kean/Nuke.git"; + repositoryURL = "https://github.com/kean/FetchImage.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 9.1.1; + minimumVersion = 0.2.1; }; }; - 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */ = { + 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kean/FetchImage.git"; + repositoryURL = "https://github.com/SRGSSR/srglogger-apple.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.2.1; + minimumVersion = 3.0.0; }; }; 6FFED3F025221564002D471B /* XCRemoteSwiftPackageReference "ios-library" */ = { @@ -11884,6 +12373,31 @@ package = 081454B92546CDD500BB7CA6 /* XCRemoteSwiftPackageReference "SwiftMessages" */; productName = SwiftMessages; }; + 082099F425780A7F0077CF8B /* SRGAnalyticsSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2546AB2521CFA100F9F4FE /* XCRemoteSwiftPackageReference "srganalytics-apple" */; + productName = SRGAnalyticsSwiftUI; + }; + 08209A0A25780AAA0077CF8B /* SRGAnalyticsSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2546AB2521CFA100F9F4FE /* XCRemoteSwiftPackageReference "srganalytics-apple" */; + productName = SRGAnalyticsSwiftUI; + }; + 08209A0C25780ABE0077CF8B /* SRGAnalyticsSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2546AB2521CFA100F9F4FE /* XCRemoteSwiftPackageReference "srganalytics-apple" */; + productName = SRGAnalyticsSwiftUI; + }; + 08209A0E25780AC50077CF8B /* SRGAnalyticsSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2546AB2521CFA100F9F4FE /* XCRemoteSwiftPackageReference "srganalytics-apple" */; + productName = SRGAnalyticsSwiftUI; + }; + 08209A1025780ACB0077CF8B /* SRGAnalyticsSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2546AB2521CFA100F9F4FE /* XCRemoteSwiftPackageReference "srganalytics-apple" */; + productName = SRGAnalyticsSwiftUI; + }; 6F2544F42521BAA400F9F4FE /* SRGUserData */ = { isa = XCSwiftPackageProductDependency; package = 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */; @@ -12009,6 +12523,31 @@ package = 6F2546AB2521CFA100F9F4FE /* XCRemoteSwiftPackageReference "srganalytics-apple" */; productName = SRGAnalyticsIdentity; }; + 6F61CCC2256E41EE00E42E31 /* SRGUserData */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */; + productName = SRGUserData; + }; + 6F61CCC4256E41F500E42E31 /* SRGUserData */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */; + productName = SRGUserData; + }; + 6F61CCC6256E41FB00E42E31 /* SRGUserData */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */; + productName = SRGUserData; + }; + 6F61CCC8256E420200E42E31 /* SRGUserData */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */; + productName = SRGUserData; + }; + 6F61CCCA256E420800E42E31 /* SRGUserData */ = { + isa = XCSwiftPackageProductDependency; + package = 6F2544F32521BAA400F9F4FE /* XCRemoteSwiftPackageReference "srguserdata-apple" */; + productName = SRGUserData; + }; 6FA84A1724E44B6400B1A56E /* SRGLetterbox */ = { isa = XCSwiftPackageProductDependency; package = 6F6DD3A624E449BA003F9437 /* XCRemoteSwiftPackageReference "srgletterbox-apple" */; @@ -12109,56 +12648,56 @@ package = 6FD40A4625221C2D00E621EF /* XCRemoteSwiftPackageReference "paper-onboarding" */; productName = PaperOnboarding; }; - 6FDB2AA424E590EE00FF286E /* Nuke */ = { - isa = XCSwiftPackageProductDependency; - package = 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */; - productName = Nuke; - }; 6FDB2AA624E590EE00FF286E /* FetchImage */ = { isa = XCSwiftPackageProductDependency; package = 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */; productName = FetchImage; }; - 6FDB2AA824E590F400FF286E /* Nuke */ = { - isa = XCSwiftPackageProductDependency; - package = 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */; - productName = Nuke; - }; 6FDB2AAA24E590F400FF286E /* FetchImage */ = { isa = XCSwiftPackageProductDependency; package = 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */; productName = FetchImage; }; - 6FDB2AAC24E590FC00FF286E /* Nuke */ = { - isa = XCSwiftPackageProductDependency; - package = 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */; - productName = Nuke; - }; 6FDB2AAE24E590FC00FF286E /* FetchImage */ = { isa = XCSwiftPackageProductDependency; package = 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */; productName = FetchImage; }; - 6FDB2AB024E5910400FF286E /* Nuke */ = { - isa = XCSwiftPackageProductDependency; - package = 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */; - productName = Nuke; - }; 6FDB2AB224E5910400FF286E /* FetchImage */ = { isa = XCSwiftPackageProductDependency; package = 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */; productName = FetchImage; }; - 6FDB2AB424E5910B00FF286E /* Nuke */ = { - isa = XCSwiftPackageProductDependency; - package = 6FDB2AA224E590BE00FF286E /* XCRemoteSwiftPackageReference "Nuke" */; - productName = Nuke; - }; 6FDB2AB624E5910B00FF286E /* FetchImage */ = { isa = XCSwiftPackageProductDependency; package = 6FDB2AA324E590DC00FF286E /* XCRemoteSwiftPackageReference "FetchImage" */; productName = FetchImage; }; + 6FF12A47256C58A70042F446 /* SRGLoggerSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */; + productName = SRGLoggerSwift; + }; + 6FF12A5D256C58B20042F446 /* SRGLoggerSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */; + productName = SRGLoggerSwift; + }; + 6FF12A73256C58BB0042F446 /* SRGLoggerSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */; + productName = SRGLoggerSwift; + }; + 6FF12A75256C58C40042F446 /* SRGLoggerSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */; + productName = SRGLoggerSwift; + }; + 6FF12A8B256C58CC0042F446 /* SRGLoggerSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 6FF12A46256C58A60042F446 /* XCRemoteSwiftPackageReference "srglogger-apple" */; + productName = SRGLoggerSwift; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 08C68D471D38D49600BB8AAA /* Project object */; diff --git a/PlaySRG.xcodeproj/xcshareddata/xcschemes/Play RSI TV.xcscheme b/PlaySRG.xcodeproj/xcshareddata/xcschemes/Play RSI TV.xcscheme index 8cfee5d57..f136eecfc 100644 --- a/PlaySRG.xcodeproj/xcshareddata/xcschemes/Play RSI TV.xcscheme +++ b/PlaySRG.xcodeproj/xcshareddata/xcschemes/Play RSI TV.xcscheme @@ -1,6 +1,6 @@ 'https://github.com/dzenbot/DZNEmptyDataSet.git', :commit => '8deb3fe69f75c5022a53a903468b29552dc70e66' + pod 'Firebase/Analytics' pod 'FSCalendar' pod 'google-cast-sdk-no-bluetooth' - pod 'InAppSettingsKit' + pod 'InAppSettingsKit', '3.1.4' # 3.1.5 has a public header exposition issue pod 'MaterialComponents/Tabs' pod 'MGSwipeTableCell' @@ -51,6 +51,8 @@ abstract_target 'Play SRG' do abstract_target 'tvOS' do platform :tvos, '14.0' + pod 'TvOSTextViewer' + target 'Play SRF TV' do end diff --git a/Podfile.lock b/Podfile.lock index 5d34b5be8..9ae63f86c 100755 --- a/Podfile.lock +++ b/Podfile.lock @@ -12,47 +12,47 @@ PODS: - AutoCoding (2.2.3) - BDKCollectionIndexView (2.2.2) - DZNEmptyDataSet (1.8.1) - - Firebase/Analytics (6.34.0): + - Firebase/Analytics (7.0.0): - Firebase/Core - - Firebase/Core (6.34.0): + - Firebase/Core (7.0.0): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.9.0) - - Firebase/CoreOnly (6.34.0): - - FirebaseCore (= 6.10.4) - - Firebase/RemoteConfig (6.34.0): + - FirebaseAnalytics (= 7.0.0) + - Firebase/CoreOnly (7.0.0): + - FirebaseCore (= 7.0.0) + - Firebase/RemoteConfig (7.0.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 4.9.1) - - FirebaseABTesting (4.2.0): - - FirebaseCore (~> 6.10) - - FirebaseAnalytics (6.9.0): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.7) - - GoogleAppMeasurement (= 6.9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/MethodSwizzler (~> 6.7) - - GoogleUtilities/Network (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" - - nanopb (~> 1.30906.0) - - FirebaseCore (6.10.4): - - FirebaseCoreDiagnostics (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - FirebaseCoreDiagnostics (1.7.0): - - GoogleDataTransport (~> 7.4) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - nanopb (~> 1.30906.0) - - FirebaseInstallations (1.7.0): - - FirebaseCore (~> 6.10) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) + - FirebaseRemoteConfig (~> 7.0.0) + - FirebaseABTesting (7.0.0): + - FirebaseCore (~> 7.0) + - FirebaseAnalytics (7.0.0): + - FirebaseCore (~> 7.0) + - FirebaseInstallations (~> 7.0) + - GoogleAppMeasurement (= 7.0.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.0) + - GoogleUtilities/MethodSwizzler (~> 7.0) + - GoogleUtilities/Network (~> 7.0) + - "GoogleUtilities/NSData+zlib (~> 7.0)" + - nanopb (~> 2.30906.0) + - FirebaseCore (7.0.0): + - FirebaseCoreDiagnostics (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/Logger (~> 7.0) + - FirebaseCoreDiagnostics (7.0.0): + - GoogleDataTransport (~> 8.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/Logger (~> 7.0) + - nanopb (~> 2.30906.0) + - FirebaseInstallations (7.0.0): + - FirebaseCore (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/UserDefaults (~> 7.0) - PromisesObjC (~> 1.2) - - FirebaseRemoteConfig (4.9.1): - - FirebaseABTesting (~> 4.2) - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" + - FirebaseRemoteConfig (7.0.0): + - FirebaseABTesting (~> 7.0) + - FirebaseCore (~> 7.0) + - FirebaseInstallations (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - "GoogleUtilities/NSData+zlib (~> 7.0)" - FSCalendar (2.8.1) - google-cast-sdk-no-bluetooth (4.5.1): - google-cast-sdk-no-bluetooth/Core (= 4.5.1) @@ -61,59 +61,59 @@ PODS: - google-cast-sdk-no-bluetooth/Core (4.5.1): - GTMSessionFetcher/Core (~> 1.1) - Protobuf (~> 3.12) - - GoogleAppMeasurement (6.9.0): - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/MethodSwizzler (~> 6.7) - - GoogleUtilities/Network (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" - - nanopb (~> 1.30906.0) - - GoogleDataTransport (7.5.1): - - nanopb (~> 1.30906.0) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): + - GoogleAppMeasurement (7.0.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.0) + - GoogleUtilities/MethodSwizzler (~> 7.0) + - GoogleUtilities/Network (~> 7.0) + - "GoogleUtilities/NSData+zlib (~> 7.0)" + - nanopb (~> 2.30906.0) + - GoogleDataTransport (8.0.0): + - nanopb (~> 2.30906.0) + - GoogleUtilities/AppDelegateSwizzler (7.0.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): + - GoogleUtilities/Environment (7.0.0): - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): + - GoogleUtilities/Logger (7.0.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.7.2): + - GoogleUtilities/MethodSwizzler (7.0.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.7.2): + - GoogleUtilities/Network (7.0.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): + - "GoogleUtilities/NSData+zlib (7.0.0)" + - GoogleUtilities/Reachability (7.0.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.7.2): + - GoogleUtilities/UserDefaults (7.0.0): - GoogleUtilities/Logger - - GTMSessionFetcher/Core (1.4.0) + - GTMSessionFetcher/Core (1.5.0) - InAppSettingsKit (3.1.4) - - MaterialComponents/AnimationTiming (118.0.0) - - MaterialComponents/Availability (118.0.0) - - MaterialComponents/Elevation (118.0.0): + - MaterialComponents/AnimationTiming (118.2.0) + - MaterialComponents/Availability (118.2.0) + - MaterialComponents/Elevation (118.2.0): - MaterialComponents/Availability - MaterialComponents/private/Color - MaterialComponents/private/Math - - MaterialComponents/Ink (118.0.0): + - MaterialComponents/Ink (118.2.0): - MaterialComponents/Availability - MaterialComponents/private/Color - MaterialComponents/private/Math - - MaterialComponents/Palettes (118.0.0) - - MaterialComponents/private/Application (118.0.0) - - MaterialComponents/private/Color (118.0.0): + - MaterialComponents/Palettes (118.2.0) + - MaterialComponents/private/Application (118.2.0) + - MaterialComponents/private/Color (118.2.0): - MaterialComponents/Availability - - MaterialComponents/private/Math (118.0.0) - - MaterialComponents/Ripple (118.0.0): + - MaterialComponents/private/Math (118.2.0) + - MaterialComponents/Ripple (118.2.0): - MaterialComponents/AnimationTiming - MaterialComponents/Availability - MaterialComponents/private/Color - MaterialComponents/private/Math - - MaterialComponents/ShadowElevations (118.0.0) - - MaterialComponents/ShadowLayer (118.0.0): + - MaterialComponents/ShadowElevations (118.2.0) + - MaterialComponents/ShadowLayer (118.2.0): - MaterialComponents/ShadowElevations - - MaterialComponents/Tabs (118.0.0): + - MaterialComponents/Tabs (118.2.0): - MaterialComponents/AnimationTiming - MaterialComponents/Elevation - MaterialComponents/Ink @@ -124,20 +124,21 @@ PODS: - MaterialComponents/ShadowLayer - MaterialComponents/Typography - MDFInternationalization - - MaterialComponents/Typography (118.0.0): + - MaterialComponents/Typography (118.2.0): - MaterialComponents/private/Application - MaterialComponents/private/Math - MDFTextAccessibility - MDFInternationalization (2.0.0) - MDFTextAccessibility (2.0.0) - MGSwipeTableCell (1.6.11) - - nanopb (1.30906.0): - - nanopb/decode (= 1.30906.0) - - nanopb/encode (= 1.30906.0) - - nanopb/decode (1.30906.0) - - nanopb/encode (1.30906.0) + - nanopb (2.30906.0): + - nanopb/decode (= 2.30906.0) + - nanopb/encode (= 2.30906.0) + - nanopb/decode (2.30906.0) + - nanopb/encode (2.30906.0) - PromisesObjC (1.2.11) - Protobuf (3.13.0) + - TvOSTextViewer (1.3.0) DEPENDENCIES: - AppCenter @@ -149,9 +150,10 @@ DEPENDENCIES: - Firebase/RemoteConfig - FSCalendar - google-cast-sdk-no-bluetooth - - InAppSettingsKit + - InAppSettingsKit (= 3.1.4) - MaterialComponents/Tabs - MGSwipeTableCell + - TvOSTextViewer SPEC REPOS: https://github.com/CocoaPods/Specs.git: @@ -179,6 +181,7 @@ SPEC REPOS: - nanopb - PromisesObjC - Protobuf + - TvOSTextViewer EXTERNAL SOURCES: DZNEmptyDataSet: @@ -195,28 +198,29 @@ SPEC CHECKSUMS: AutoCoding: 90ca3cbc0d77a37cfff75167d705b44148c5fc51 BDKCollectionIndexView: 489a0c57d27fafe6944ef61e5a4192902148ea84 DZNEmptyDataSet: b94434220f87d9dda46660eb4f07a424778e93b4 - Firebase: c23a36d9e4cdf7877dfcba8dd0c58add66358999 - FirebaseABTesting: 8a9d8df3acc2b43f4a22014ddf9f601bca6af699 - FirebaseAnalytics: 3bb096873ee0d7fa4b6c70f5e9166b6da413cc7f - FirebaseCore: d3a978a3cfa3240bf7e4ba7d137fdf5b22b628ec - FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 - FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 - FirebaseRemoteConfig: 35a729305f254fb15a2e541d4b36f3a379da7fdc + Firebase: 50be68416f50eb4eb2ecb0e78acab9a051ef95df + FirebaseABTesting: b78ae653b7658b8f1c076eaa21029c936d58f758 + FirebaseAnalytics: c1166b7990bae464c6436132510bb718c6680f80 + FirebaseCore: cf3122185fce1cf71cedbbc498ea84d2b3e7cb69 + FirebaseCoreDiagnostics: 5f4aa04fdb04923693cc704c7ef9158bdf41a48b + FirebaseInstallations: c28d4bcbb5c6884d1a39afbc0bd7fc590e31e9b7 + FirebaseRemoteConfig: ff8d3542cbd919c9d3851fd544690b8848fc0402 FSCalendar: 5245140456fcbcac5824c203b98bd8068bdfee1a google-cast-sdk-no-bluetooth: 73ac8e9edbee16f52c2a445e391bd531e116bb31 - GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e - GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - GTMSessionFetcher: 6f5c8abbab8a9bce4bb3f057e317728ec6182b10 + GoogleAppMeasurement: 7790ef975d1d463c8614cd949a847e612edf087a + GoogleDataTransport: 6ce8004a961db1b905740d7be106c61ba7e89c21 + GoogleUtilities: ffb2f4159f2c897c6e8992bd7fbcdef8a300589c + GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 InAppSettingsKit: 01fb9774c74a11fefc521f29cb2338909e1c2b61 - MaterialComponents: 333a09d55de4bc53e0e2e08f178c6f29cfe8645f + MaterialComponents: fc3dba46cf3ae8495728a875778b47d0c6ec7ed5 MDFInternationalization: 010097556d6b09d2c4ea38e0820ea6d37be6a314 MDFTextAccessibility: 85c09a1bd9c321f494348e632a25063bcda35a53 MGSwipeTableCell: b804e4e450dee439c42250be90bd50458bf67fce - nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53 PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748 + TvOSTextViewer: c1a0c64286931e663d131dc680f908bcfa36e214 -PODFILE CHECKSUM: 3e3c433cd6e648a98dbce6ff2f1ef52abe27fae7 +PODFILE CHECKSUM: b647ec4d5c15dac002206c3e1b48e30f0576e253 COCOAPODS: 1.9.3 diff --git a/Scripts/generate-icons-restore.sh b/Scripts/generate-icons-restore.sh index 5e37a07d3..023f25e5a 100755 --- a/Scripts/generate-icons-restore.sh +++ b/Scripts/generate-icons-restore.sh @@ -13,20 +13,49 @@ fi export PATH="${PATH}:/usr/local/bin:/opt/local/bin" BUSINESS_UNIT=`echo ${PRODUCT_NAME} | sed 's/Play //g'` -SOURCE_RESOURCES_PATH="${SRCROOT}/Application/Resources/Apps/Play ${BUSINESS_UNIT}/${BUSINESS_UNIT}Resources.xcassets" -SOURCE_APPICON_PATH="${SOURCE_RESOURCES_PATH}/AppIcon.appiconset" -DUPLICATE_APPICON_PATH="${SOURCE_RESOURCES_PATH}/OriginalAppIcon.appiconset" +SOURCE_IOS_RESOURCES_PATH="${SRCROOT}/Application/Resources/Apps/Play ${BUSINESS_UNIT}/${BUSINESS_UNIT}Resources.xcassets" +SOURCE_IOS_APPICON_PATH="${SOURCE_IOS_RESOURCES_PATH}/AppIcon.appiconset" +DUPLICATE_IOS_APPICON_PATH="${SRCROOT}/OriginalAppIcon.appiconset" +SOURCE_TVOS_RESOURCES_PATH="${SRCROOT}/TV Application/Resources/Play ${BUSINESS_UNIT}/${BUSINESS_UNIT}Assets.xcassets" +SOURCE_TVOS_APPICON_PATH="${SOURCE_TVOS_RESOURCES_PATH}/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Layer 4.imagestacklayer/Content.imageset" +DUPLICATE_TVOS_APPICON_PATH="${SRCROOT}/OriginalTVAppIcon.appiconset" +SOURCE_TVOS_APPSTOREICON_PATH="${SOURCE_TVOS_RESOURCES_PATH}/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Layer 4.imagestacklayer/Content.imageset" +DUPLICATE_TVOS_APPSTOREICON_PATH="${SRCROOT}/OriginalAppStoreTVIcon.appiconset" + +echo $PYTHON_NIGHTLIES_TAG "Restore original iOS app icons..." + +if [ ! -e "${DUPLICATE_IOS_APPICON_PATH}" ]; then + echo $PYTHON_NIGHTLIES_TAG "Original iOS app icons not found." + exit 0 +fi + +rm -fR "${SOURCE_IOS_APPICON_PATH}" +mv "${DUPLICATE_IOS_APPICON_PATH}" "${SOURCE_IOS_APPICON_PATH}" + +echo $PYTHON_NIGHTLIES_TAG "Original iOS app icons Restored." + +echo $PYTHON_NIGHTLIES_TAG "Restore original tvOS app icons..." + +if [ ! -e "${DUPLICATE_TVOS_APPICON_PATH}" ]; then + echo $PYTHON_NIGHTLIES_TAG "Original tvOS app icons not found." + exit 0 +fi + +rm -fR "${SOURCE_TVOS_APPICON_PATH}" +mv "${DUPLICATE_TVOS_APPICON_PATH}" "${SOURCE_TVOS_APPICON_PATH}" + +echo $PYTHON_NIGHTLIES_TAG "Original tvOS app icons Restored." -echo $PYTHON_NIGHTLIES_TAG "Restore original app icons..." +echo $PYTHON_NIGHTLIES_TAG "Restore original tvOS AppStore icons..." -if [ ! -e "${DUPLICATE_APPICON_PATH}" ]; then - echo $PYTHON_NIGHTLIES_TAG "Original app icons not found." +if [ ! -e "${DUPLICATE_TVOS_APPSTOREICON_PATH}" ]; then + echo $PYTHON_NIGHTLIES_TAG "Original tvOS AppStore icons not found." exit 0 fi -rm -fR "${SOURCE_APPICON_PATH}" -mv "${DUPLICATE_APPICON_PATH}" "${SOURCE_APPICON_PATH}" +rm -fR "${SOURCE_TVOS_APPSTOREICON_PATH}" +mv "${DUPLICATE_TVOS_APPSTOREICON_PATH}" "${SOURCE_TVOS_APPSTOREICON_PATH}" -echo $PYTHON_NIGHTLIES_TAG "Original icons Restored." +echo $PYTHON_NIGHTLIES_TAG "Original tvOS AppStore icons Restored." exit 0 diff --git a/Scripts/generate-icons.sh b/Scripts/generate-icons.sh index f8d395afd..2dc4c84cc 100755 --- a/Scripts/generate-icons.sh +++ b/Scripts/generate-icons.sh @@ -13,13 +13,20 @@ fi export PATH="${PATH}:/usr/local/bin:/opt/local/bin" BUSINESS_UNIT=`echo ${PRODUCT_NAME} | sed 's/Play //g'` -SOURCE_RESOURCES_PATH="${SRCROOT}/Application/Resources/Apps/Play ${BUSINESS_UNIT}/${BUSINESS_UNIT}Resources.xcassets" -SOURCE_APPICON_PATH="${SOURCE_RESOURCES_PATH}/AppIcon.appiconset" -DUPLICATE_APPICON_PATH="${SOURCE_RESOURCES_PATH}/OriginalAppIcon.appiconset" +SOURCE_IOS_RESOURCES_PATH="${SRCROOT}/Application/Resources/Apps/Play ${BUSINESS_UNIT}/${BUSINESS_UNIT}Resources.xcassets" +SOURCE_IOS_APPICON_PATH="${SOURCE_IOS_RESOURCES_PATH}/AppIcon.appiconset" +DUPLICATE_IOS_APPICON_PATH="${SRCROOT}/OriginalAppIcon.appiconset" +SOURCE_TVOS_RESOURCES_PATH="${SRCROOT}/TV Application/Resources/Play ${BUSINESS_UNIT}/${BUSINESS_UNIT}Assets.xcassets" +SOURCE_TVOS_APPICON_PATH="${SOURCE_TVOS_RESOURCES_PATH}/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Layer 4.imagestacklayer/Content.imageset" +DUPLICATE_TVOS_APPICON_PATH="${SRCROOT}/OriginalTVAppIcon.appiconset" +SOURCE_TVOS_APPSTOREICON_PATH="${SOURCE_TVOS_RESOURCES_PATH}/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Layer 4.imagestacklayer/Content.imageset" +DUPLICATE_TVOS_APPSTOREICON_PATH="${SRCROOT}/OriginalAppStoreTVIcon.appiconset" echo $PYTHON_NIGHTLIES_TAG "Duplicate original icons..." -cp -fR "${SOURCE_APPICON_PATH}" "${DUPLICATE_APPICON_PATH}" +cp -fR "${SOURCE_IOS_APPICON_PATH}" "${DUPLICATE_IOS_APPICON_PATH}" +cp -fR "${SOURCE_TVOS_APPICON_PATH}" "${DUPLICATE_TVOS_APPICON_PATH}" +cp -fR "${SOURCE_TVOS_APPSTOREICON_PATH}" "${DUPLICATE_TVOS_APPSTOREICON_PATH}" BUNDLE_IDENTIFIER=${PRODUCT_BUNDLE_IDENTIFIER} BUILD_NUMBER=${CURRENT_PROJECT_VERSION} @@ -41,73 +48,82 @@ if [ "$LAST_RUN" == "$CURRENT_RUN" ]; then echo $PYTHON_NIGHTLIES_TAG "Last run had same configuration: $CURRENT_RUN. No need to recreate dev/beta icons, except if not created before." fi -CONTENTS_JSON="${SOURCE_APPICON_PATH}/Contents.json" +CONTENTS_JSON="${SOURCE_IOS_APPICON_PATH}/Contents.json" echo "Processing Icons..." -ICON_COUNT=$(jq -r '.images | length-1' "${CONTENTS_JSON}") -for i in $(jot - 0 ${ICON_COUNT}); +for CONTENTS_JSON in "${SOURCE_IOS_APPICON_PATH}/Contents.json" "${SOURCE_TVOS_APPICON_PATH}/Contents.json" "${SOURCE_TVOS_APPSTOREICON_PATH}/Contents.json"; do - filename=$(jq -r ".images[${i}] | .filename" "${CONTENTS_JSON}") - size=$(jq -r ".images[${i}] | .size" "${CONTENTS_JSON}") - scale=@$(jq -r ".images[${i}] | .scale" "${CONTENTS_JSON}") - idiom=~$(jq -r ".images[${i}] | .idiom" "${CONTENTS_JSON}") - - if [ ${scale} == "@1x" ]; then - scale="" - fi - if [ ${idiom} == "~iphone" ]; then - idiom="" - fi - - if [ ${filename} == "null" ]; then - continue - fi - - SOURCE_ICON_PATH="${SOURCE_APPICON_PATH}/${filename}" - TARGET_ICON_PATH="${SOURCE_ICON_PATH}" - - if [ ! -e "${SOURCE_ICON_PATH}" ]; then - echo $PYTHON_NIGHTLIES_TAG "warning: App icon not found: ${SOURCE_ICON_PATH}" - continue - fi - - SCRIPT_DIR=`dirname $BASH_SOURCE` - CACHE_APPICON_PATH="${SCRIPT_DIR}/generate-icons-caches" - - if [ ! -e "${CACHE_APPICON_PATH}" ]; then - mkdir ${CACHE_APPICON_PATH} - fi - - if [ "${CONFIGURATION}" == "Beta" ]; then - TITLE="Beta" - elif [ "${CONFIGURATION}" == "Nightly" ]; then - TITLE="Nightly" - elif [ "${CONFIGURATION}" == "Debug" ]; then - TITLE="Debug" - else - TITLE="Dev" - fi - - SCRIPT_ICON_PATH="${CACHE_APPICON_PATH}/${TITLE}-${filename}" - - if [ "$LAST_RUN" != "$CURRENT_RUN" ] || [! -e "${SCRIPT_ICON_PATH}"]; then + ICON_COUNT=$(jq -r '.images | length-1' "${CONTENTS_JSON}") + for i in $(jot - 0 ${ICON_COUNT}); + do + filename=$(jq -r ".images[${i}] | .filename" "${CONTENTS_JSON}") + size=$(jq -r ".images[${i}] | .size" "${CONTENTS_JSON}") + scale=@$(jq -r ".images[${i}] | .scale" "${CONTENTS_JSON}") + idiom=$(jq -r ".images[${i}] | .idiom" "${CONTENTS_JSON}") + + if [ ${scale} == "@1x" ]; then + scale="" + fi + if [ ${idiom} == "iphone" ]; then + idiom="" + fi + + if [ ${filename} == "null" ]; then + continue + fi + + SOURCE_ICON_FOLDER="${CONTENTS_JSON//Contents.json/}" + SOURCE_ICON_PATH="${SOURCE_ICON_FOLDER}/${filename}" + TARGET_ICON_PATH="${SOURCE_ICON_PATH}" + + if [ ! -e "${SOURCE_ICON_PATH}" ]; then + echo $PYTHON_NIGHTLIES_TAG "warning: App icon not found: ${SOURCE_ICON_PATH}" + continue + fi + WIDTH=`identify -format %w "${SOURCE_ICON_PATH}"` - HEIGHT=`echo "${WIDTH}/6" | bc` - if [ "${BUILD_NUMBER}" != "" ]; then - CAPTION="${TITLE}-${BUILD_NUMBER}" - else - CAPTION="${TITLE}" + SCRIPT_DIR=`dirname $BASH_SOURCE` + CACHE_APPICON_PATH="${SCRIPT_DIR}/generate-icons-caches/${WIDTH}" + + if [ ! -e "${CACHE_APPICON_PATH}" ]; then + mkdir -p ${CACHE_APPICON_PATH} fi - echo $PYTHON_NIGHTLIES_TAG "Making app icon ${CAPTION} | ${filename}" - convert -background '#0005' -fill white -gravity center -size ${WIDTH}x${HEIGHT} caption:"${CAPTION}" "${SOURCE_ICON_PATH}" +swap -gravity south -composite "${SCRIPT_ICON_PATH}" - fi + if [ "${CONFIGURATION}" == "Beta" ]; then + TITLE="Beta" + elif [ "${CONFIGURATION}" == "Nightly" ]; then + TITLE="Nightly" + elif [ "${CONFIGURATION}" == "Debug" ]; then + TITLE="Debug" + else + TITLE="Dev" + fi + + SCRIPT_ICON_PATH="${CACHE_APPICON_PATH}/${TITLE}-${filename}" + + if [ "$LAST_RUN" != "$CURRENT_RUN" ] || [! -e "${SCRIPT_ICON_PATH}"]; then + if [ ${idiom} == "tv" ]; then + HEIGHT=`echo "${WIDTH}/16" | bc` + else + HEIGHT=`echo "${WIDTH}/6" | bc` + fi + + if [ "${BUILD_NUMBER}" != "" ] && [[ "${CONTENTS_JSON}" != *"App Icon - App Store"* ]]; then + CAPTION="${TITLE}-${BUILD_NUMBER}" + else + CAPTION="${TITLE}" + fi + + echo $PYTHON_NIGHTLIES_TAG "Making app icon ${CAPTION} | ${filename}" + convert -background '#0005' -fill white -gravity center -size ${WIDTH}x${HEIGHT} caption:"${CAPTION}" "${SOURCE_ICON_PATH}" +swap -gravity south -composite "${SCRIPT_ICON_PATH}" + fi - SOURCE_ICON_PATH="${SCRIPT_ICON_PATH}" - echo "Copying icon from '${SOURCE_ICON_PATH}' ... " - echo "... in '${TARGET_ICON_PATH}'" - cp -f "${SOURCE_ICON_PATH}" "${TARGET_ICON_PATH}" + SOURCE_ICON_PATH="${SCRIPT_ICON_PATH}" + echo "Copying icon from '${SOURCE_ICON_PATH}' ... " + echo "... in '${TARGET_ICON_PATH}'" + cp -f "${SOURCE_ICON_PATH}" "${TARGET_ICON_PATH}" + done done if [ -f $LAST_RUN_FILE ]; then @@ -116,6 +132,6 @@ fi echo $CURRENT_RUN > $LAST_RUN_FILE -echo $PYTHON_NIGHTLIES_TAG "After the compilation, execute `generate-icons-restore.sh` to restore original app icons." +echo $PYTHON_NIGHTLIES_TAG "After the compilation, execute 'generate-icons-restore.sh' to restore original app icons." echo $PYTHON_NIGHTLIES_CLOSING_TAG exit 0 diff --git a/TV Application/Preview Content/Preview Assets.xcassets/media-rts-tv.dataset/Contents.json b/TV Application/Preview Content/Preview Assets.xcassets/media-rts-tv.dataset/Contents.json new file mode 100644 index 000000000..a60cbb298 --- /dev/null +++ b/TV Application/Preview Content/Preview Assets.xcassets/media-rts-tv.dataset/Contents.json @@ -0,0 +1,13 @@ +{ + "data" : [ + { + "filename" : "media-rts-tv.json", + "idiom" : "universal", + "universal-type-identifier" : "public.json" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TV Application/Preview Content/Preview Assets.xcassets/media-rts-tv.dataset/media-rts-tv.json b/TV Application/Preview Content/Preview Assets.xcassets/media-rts-tv.dataset/media-rts-tv.json new file mode 100644 index 000000000..47a7e5a91 --- /dev/null +++ b/TV Application/Preview Content/Preview Assets.xcassets/media-rts-tv.dataset/media-rts-tv.json @@ -0,0 +1,43 @@ +{ + "id": "11740064", + "mediaType": "VIDEO", + "vendor": "RTS", + "urn": "urn:rts:video:11740064", + "title": "19h30", + "imageUrl": "https://www.rts.ch/2020/11/09/21/08/11740063.image/16x9", + "imageTitle": "19h30 [RTS]", + "type": "EPISODE", + "date": "2020-11-09T19:30:00+01:00", + "duration": 1903000, + "validFrom": "2020-11-09T20:08:00+01:00", + "playableAbroad": true, + "subtitlesAvailable": false, + "episode": { + "id": "11637143", + "title": "19h30", + "imageUrl": "https://www.rts.ch/2020/11/09/21/08/11740063.image/16x9", + "imageTitle": "19h30 [RTS]" + }, + "show": { + "id": "105932", + "vendor": "RTS", + "transmission": "TV", + "urn": "urn:rts:show:tv:105932", + "title": "19h30", + "imageUrl": "https://ws.srf.ch/asset/image/audio/8b32fd87-459e-40f1-9519-ba0cbb01f34e/NOT_SPECIFIED.jpg", + "posterImageUrl": "https://ws.srf.ch/asset/image/audio/e0322b37-5697-474d-93ac-19a4044a6a24/POSTER.jpg", + "posterImageIsFallbackUrl": true, + "primaryChannelId": "143932a79bb5a123a646b68b1d1188d7ae493e5b", + "primaryChannelUrn": "urn:rts:channel:tv:143932a79bb5a123a646b68b1d1188d7ae493e5b" + }, + "channel": { + "id": "143932a79bb5a123a646b68b1d1188d7ae493e5b", + "vendor": "RTS", + "urn": "urn:rts:channel:tv:143932a79bb5a123a646b68b1d1188d7ae493e5b", + "title": "RTS Un", + "imageUrl": "https://ws.srf.ch/asset/image/audio/8b32fd87-459e-40f1-9519-ba0cbb01f34e/NOT_SPECIFIED.jpg", + "transmission": "TV" + }, + "presentation": "DEFAULT", + "aspectRatio": "16:9" +} \ No newline at end of file diff --git a/TV Application/Preview Content/Preview Assets.xcassets/show-srf-tv.dataset/Contents.json b/TV Application/Preview Content/Preview Assets.xcassets/show-srf-tv.dataset/Contents.json new file mode 100644 index 000000000..3205ad052 --- /dev/null +++ b/TV Application/Preview Content/Preview Assets.xcassets/show-srf-tv.dataset/Contents.json @@ -0,0 +1,13 @@ +{ + "data" : [ + { + "filename" : "show-srf-tv.json", + "idiom" : "universal", + "universal-type-identifier" : "public.json" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TV Application/Preview Content/Preview Assets.xcassets/show-srf-tv.dataset/show-srf-tv.json b/TV Application/Preview Content/Preview Assets.xcassets/show-srf-tv.dataset/show-srf-tv.json new file mode 100644 index 000000000..1fe2b2fe3 --- /dev/null +++ b/TV Application/Preview Content/Preview Assets.xcassets/show-srf-tv.dataset/show-srf-tv.json @@ -0,0 +1,44 @@ +{ + "id": "3b016ffc-afa2-466d-a694-c48b7ffe1783", + "vendor": "SRF", + "transmission": "TV", + "urn": "urn:srf:show:tv:3b016ffc-afa2-466d-a694-c48b7ffe1783", + "title": "DOK", + "lead": "Dokumentarfilme und Reportagen, so einzigartig wie das Leben.", + "description": "Dokumentarfilme zu Themen aus Gesellschaft, Natur, Politik, Sport und Wirtschaft. Es sind Geschichten, so einzigartig wie das Leben.", + "imageUrl": "https://ws.srf.ch/asset/image/audio/e2ea7098-ffbd-4ae5-b71b-814152546ac9/WEBVISUAL/1572427510.jpg", + "imageTitle": "DOK", + "bannerImageUrl": "https://ws.srf.ch/asset/image/audio/e2ea7098-ffbd-4ae5-b71b-814152546ac9/HEADER_SRF_PLAYER/1572427457.jpg", + "posterImageUrl": "https://ws.srf.ch/asset/image/audio/e2ea7098-ffbd-4ae5-b71b-814152546ac9/POSTER/1589379785.jpg", + "posterImageIsFallbackUrl": false, + "timeTableUrl": "https://www.srf.ch/programm/tv/mediagroup/dok", + "links": [ + { + "title": "Mehr zur Sendung", + "link": "https://www.srf.ch/sendungen/dok" + }, + { + "title": "Mit Audiodeskription", + "link": "https://www.srf.ch/play/tv/sendung/dok-mit-audiodeskription?id=525041ef-5e96-4a5f-8333-92a676e5c281" + } + ], + "primaryChannelId": "23FFBE1B-65CE-4188-ADD2-C724186C2C9F", + "primaryChannelUrn": "urn:srf:channel:tv:23FFBE1B-65CE-4188-ADD2-C724186C2C9F", + "numberOfEpisodes": 594, + "topicList": [ + { + "id": "516421f0-ec89-43ba-823b-1b5ceec262f3", + "vendor": "SRF", + "transmission": "TV", + "urn": "urn:srf:topic:tv:516421f0-ec89-43ba-823b-1b5ceec262f3", + "title": "Dokus", + "lead": "Reportagen, Dokumentarfilme, Dok-Serien. SRF bietet dem User Geschichten, so einzigartig wie das Leben.", + "description": "Gesellschaft, Natur, Tiere, Politik, Geschichte, Sport und Wirtschaft. Die Dokus von SRF beleuchten alle Bereiche des Lebens. Feinfühlig, nachfragend, überraschend und unterhaltsam." + } + ], + "broadcastInformation": { + "hintText": "8. Staffel ab 7. Dezember", + "startDate": "2020-11-02T09:44:00+01:00", + "endDate": "2020-12-07T23:30:00+01:00" + } +} \ No newline at end of file diff --git a/TV Application/Sources/ActivityIndicator.swift b/TV Application/Sources/ActivityIndicator.swift new file mode 100644 index 000000000..de2a18722 --- /dev/null +++ b/TV Application/Sources/ActivityIndicator.swift @@ -0,0 +1,31 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +struct ActivityIndicator: View { + var body: some View { + LoadingImageView() + .frame(width: 90, height: 90) + } + + private struct LoadingImageView: UIViewRepresentable { + func makeUIView(context: Context) -> UIImageView { + return UIImageView.play_loadingImageView90(withTintColor: .play_lightGray) + } + + func updateUIView(_ uiView: UIImageView, context: Context) { + // No update logic required + } + } +} + +struct ActivityIndicator_Previews: PreviewProvider { + static var previews: some View { + ActivityIndicator() + .previewLayout(.sizeThatFits) + } +} diff --git a/TV Application/Sources/AppDelegate.swift b/TV Application/Sources/AppDelegate.swift index e6e58808d..d18906ead 100644 --- a/TV Application/Sources/AppDelegate.swift +++ b/TV Application/Sources/AppDelegate.swift @@ -5,8 +5,10 @@ // import Firebase +import SRGAnalytics import SRGAppearance -import SRGDataProvider +import SRGDataProviderCombine +import SRGUserData import SwiftUI import UIKit @@ -14,9 +16,9 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? + private var cancellables = Set() + private static func configuredTabBarController(tabBarController: UITabBarController) { - tabBarController.view.backgroundColor = (UIScreen.main.traitCollection.userInterfaceStyle == .dark) ? .play_black : .play_lightGray - let appearance = UITabBarAppearance() appearance.backgroundColor = .play_cardGrayBackground appearance.selectionIndicatorTintColor = .srg_color(fromHexadecimalString: "#979797") @@ -32,31 +34,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate { tabBarController.tabBar.standardAppearance = appearance } - // MARK: - UIApplicationDelegate protocol - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - if Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist") != nil { - FirebaseApp.configure() - } - - SRGDataProvider.current = SRGDataProvider(serviceURL: SRGIntegrationLayerProductionServiceURL()) - - let window = UIWindow(frame: UIScreen.main.bounds) - window.makeKeyAndVisible() - self.window = window - - let configuration = ApplicationConfiguration.shared + private func applicationRootViewController() -> UIViewController? { var viewControllers = [UIViewController]() let videosViewController = UIHostingController(rootView: VideosView()) - videosViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Videos", comment: "Videos tab title"), image: nil, tag: 0) + videosViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Home", comment: "Home tab title"), image: nil, tag: 0) viewControllers.append(videosViewController) + let configuration = ApplicationConfiguration.shared + + #if DEBUG if !configuration.radioChannels.isEmpty { let audiosViewController = UIHostingController(rootView: AudiosView()) audiosViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Audios", comment: "Audios tab title"), image: nil, tag: 1) viewControllers.append(audiosViewController) } + #endif if !configuration.liveHomeSections.isEmpty { let liveViewController = UIHostingController(rootView: LiveView()) @@ -64,10 +57,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { viewControllers.append(liveViewController) } - let showsViewController = UIHostingController(rootView: ShowsView()) - showsViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Shows", comment: "Shows tab title"), image: nil, tag: 3) - viewControllers.append(showsViewController) + if configuration.videoHomeSections.contains(NSNumber(value: HomeSection.tvShowsAccess.rawValue)) { + let showsViewController = UIHostingController(rootView: ShowsView()) + showsViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Shows", comment: "Shows tab title"), image: nil, tag: 3) + viewControllers.append(showsViewController) + } + #if DEBUG let searchViewController = UIHostingController(rootView: SearchView()) searchViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Search", comment: "Search tab title"), image: nil, tag: 3) viewControllers.append(searchViewController) @@ -76,10 +72,56 @@ class AppDelegate: UIResponder, UIApplicationDelegate { profileViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("Profile", comment: "Profile tab title"), image: nil, tag: 4) viewControllers.append(profileViewController) - let tabBarController = UITabBarController() - Self.configuredTabBarController(tabBarController: tabBarController) - tabBarController.viewControllers = viewControllers - window.rootViewController = tabBarController + let historyViewController = UIHostingController(rootView: HistoryView()) + historyViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("History", comment: "Profile tab title"), image: nil, tag: 4) + viewControllers.append(historyViewController) + #endif + + if viewControllers.count > 1 { + let tabBarController = UITabBarController() + Self.configuredTabBarController(tabBarController: tabBarController) + tabBarController.viewControllers = viewControllers + return tabBarController + } + else { + return viewControllers.first + } + } + + // MARK: - UIApplicationDelegate protocol + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + if Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist") != nil { + FirebaseApp.configure() + } + + try? AVAudioSession.sharedInstance().setCategory(.playback) + + let configuration = ApplicationConfiguration.shared + application.accessibilityLanguage = configuration.voiceOverLanguageCode; + + let cachesDirectoryUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!) + let storeFileUrl = cachesDirectoryUrl.appendingPathComponent("PlayData.sqlite") + SRGUserData.current = SRGUserData(storeFileURL: storeFileUrl, serviceURL: configuration.userDataServiceURL, identityService: nil) + + let analyticsConfiguration = SRGAnalyticsConfiguration(businessUnitIdentifier: configuration.analyticsBusinessUnitIdentifier, + container: configuration.analyticsContainer, + siteName: configuration.tvSiteName, + netMetrixIdentifier: configuration.netMetrixIdentifier) + #if DEBUG || NIGHLTY || BETA + analyticsConfiguration.environmentMode = .preProduction + #endif + SRGAnalyticsTracker.shared.start(with: analyticsConfiguration) + + SRGDataProvider.current = SRGDataProvider(serviceURL: SRGIntegrationLayerProductionServiceURL()) + + let window = UIWindow(frame: UIScreen.main.bounds) + window.makeKeyAndVisible() + self.window = window + + let rootViewController = applicationRootViewController()! + rootViewController.view.backgroundColor = (UIScreen.main.traitCollection.userInterfaceStyle == .dark) ? .play_black : .play_lightGray + window.rootViewController = rootViewController return true } @@ -88,4 +130,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Self.configuredTabBarController(tabBarController: tabBarController) } } + + // See URL_SCHEMES.md + func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + guard let deeplinkAction = url.host else { return false } + + if deeplinkAction == "media" { + let mediaUrn = url.lastPathComponent + SRGDataProvider.current?.media(withUrn: mediaUrn) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { _ in + }, receiveValue: { media, _ in + navigateToMedia(media) + }) + .store(in: &cancellables) + return true + } + else if deeplinkAction == "show" { + let showUrn = url.lastPathComponent + SRGDataProvider.current?.show(withUrn: showUrn) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { _ in + }, receiveValue: { show, _ in + navigateToShow(show) + }) + .store(in: &cancellables) + return true + } + return false + } } diff --git a/TV Application/Sources/ApplicationConfiguration.swift b/TV Application/Sources/ApplicationConfiguration.swift index d8e11c7cd..f65fa3823 100644 --- a/TV Application/Sources/ApplicationConfiguration.swift +++ b/TV Application/Sources/ApplicationConfiguration.swift @@ -7,12 +7,14 @@ import SRGDataProviderModel extension ApplicationConfiguration { - private func videoHomeRowId(from homeSection: HomeSection) -> HomeRowId? { + private func videoHomeRowId(from homeSection: HomeSection) -> HomeModel.RowId? { switch homeSection { case .tvTrending: return .tvTrending(appearance: .hero) case .tvLatest: return .tvLatest + case .tvWebFirst: + return .tvWebFirst case .tvMostPopular: return .tvMostPopular case .tvSoonExpiring: @@ -28,7 +30,7 @@ extension ApplicationConfiguration { } } - private func liveHomeRowId(from homeSection: HomeSection) -> HomeRowId? { + private func liveHomeRowId(from homeSection: HomeSection) -> HomeModel.RowId? { switch homeSection { case .tvLive: return .tvLive @@ -45,8 +47,8 @@ extension ApplicationConfiguration { } } - func videoHomeRowIds() -> [HomeRowId] { - var rowIds = [HomeRowId]() + func videoHomeRowIds() -> [HomeModel.RowId] { + var rowIds = [HomeModel.RowId]() for homeSection in videoHomeSections { if let homeSection = HomeSection(rawValue: homeSection.intValue), let rowId = videoHomeRowId(from: homeSection) { @@ -56,8 +58,8 @@ extension ApplicationConfiguration { return rowIds } - func liveHomeRowIds() -> [HomeRowId] { - var rowIds = [HomeRowId]() + func liveHomeRowIds() -> [HomeModel.RowId] { + var rowIds = [HomeModel.RowId]() for homeSection in liveHomeSections { if let homeSection = HomeSection(rawValue: homeSection.intValue), let rowId = liveHomeRowId(from: homeSection) { diff --git a/TV Application/Sources/AudiosView.swift b/TV Application/Sources/AudiosView.swift index a3523a10f..6e03d975b 100644 --- a/TV Application/Sources/AudiosView.swift +++ b/TV Application/Sources/AudiosView.swift @@ -81,7 +81,7 @@ struct RadioChannelView: View { .onDisappear { model.cancelRefresh() } - .onResume { + .onWake { model.refresh() } } diff --git a/TV Application/Sources/Badges.swift b/TV Application/Sources/Badges.swift new file mode 100644 index 000000000..ed5b3fcbf --- /dev/null +++ b/TV Application/Sources/Badges.swift @@ -0,0 +1,72 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderModel +import SwiftUI + +struct Badge: View { + let text: String + let color: Color + + var body: some View { + Text(text) + .srgFont(.caption) + .foregroundColor(.white) + .padding([.top, .bottom], 5) + .padding([.leading, .trailing], 8) + .background(color) + .cornerRadius(4) + } +} + +struct AvailabilityBadge: View { + let media: SRGMedia + + static func formattedDuration(from: Date, to: Date) -> String? { + guard let days = Calendar.current.dateComponents([.day], from: from, to: to).day else { return nil } + switch days { + case 0: + return PlayShortFormattedHours(to.timeIntervalSince(from)) + case 1...3: + return PlayShortFormattedDays(to.timeIntervalSince(from)) + default: + return nil + } + } + + private func availabilityBadgeProperties() -> (text: String, color: Color)? { + let now = Date() + let availability = media.timeAvailability(at: now) + switch availability { + case .notYetAvailable: + return (NSLocalizedString("Soon", comment: "Short label identifying content which will be available soon."), Color(.play_gray)) + case .notAvailableAnymore: + return (NSLocalizedString("Expired", comment: "Short label identifying content which has expired."), Color(.play_gray)) + case .available: + guard let endDate = media.endDate, media.contentType == .episode else { return nil } + if let remainingTime = Self.formattedDuration(from: now, to: endDate) { + return (String(format: NSLocalizedString("%@ left", comment: "Short label displayed on a media expiring soon"), remainingTime), Color(.play_orange)) + } + else { + return nil + } + default: + return nil + } + } + + var body: some View { + Group { + if media.play_isWebFirst { + Badge(text: NSLocalizedString("Web first", comment: "Web first label on media cells"), color: Color(.srg_blue)) + } + else if let availabilityBadgeProperties = availabilityBadgeProperties() { + Badge(text: availabilityBadgeProperties.text, color: availabilityBadgeProperties.color) + } + } + .padding([.leading, .top], 8) + } +} diff --git a/TV Application/Sources/BlockingOverlay.swift b/TV Application/Sources/BlockingOverlay.swift new file mode 100644 index 000000000..eea917b5f --- /dev/null +++ b/TV Application/Sources/BlockingOverlay.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +struct BlockingOverlay: View { + let media: SRGMedia? + + private var blockingIconImage: UIImage? { + guard let blockingReason = media?.blockingReason(at: Date()) else { return nil } + return UIImage.play_image(for: blockingReason) + } + + var body: some View { + if let blockingIconImage = blockingIconImage { + ZStack { + Rectangle() + .fill(Color(white: 0, opacity: 0.6)) + Image(uiImage: blockingIconImage) + .foregroundColor(.white) + } + } + } +} diff --git a/TV Application/Sources/CollectionView.swift b/TV Application/Sources/CollectionView.swift index 7aa6b3c5b..c1eafc793 100644 --- a/TV Application/Sources/CollectionView.swift +++ b/TV Application/Sources/CollectionView.swift @@ -7,47 +7,13 @@ import SwiftUI extension CollectionView { - func synchronizeParentTabScrolling() -> some View { + func synchronizeParentTabScrolling() -> CollectionView { var collectionView = self collectionView.parentTabScrollingEnabled = true return collectionView } } -// See https://stackoverflow.com/questions/61552497/uitableviewheaderfooterview-with-swiftui-content-getting-automatic-safe-area-ins -extension UIHostingController { - convenience public init(rootView: Content, ignoreSafeArea: Bool) { - self.init(rootView: rootView) - - if ignoreSafeArea { - disableSafeArea() - } - } - - func disableSafeArea() { - guard let viewClass = object_getClass(view) else { return } - - let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea") - if let viewSubclass = NSClassFromString(viewSubclassName) { - object_setClass(view, viewSubclass) - } - else { - guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } - guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return } - - if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) { - let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in - return .zero - } - class_addMethod(viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method)) - } - - objc_registerClassPair(viewSubclass) - object_setClass(view, viewSubclass) - } - } -} - /** * Collection row. */ @@ -113,67 +79,6 @@ struct CollectionView Bool { return focusable } - - func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) { - if let focusGuideBackgroundView = view as? FocusGuideBackgroundView { - focusGuideBackgroundView.section = indexPath.section - focusGuideBackgroundView.collectionView = collectionView - } - } } /// Data displayed by the collection view. @@ -222,6 +120,28 @@ struct CollectionView]) -> [CollectionRow] { + var addedItems = [Item: Bool]() + var cleanedRows = [CollectionRow]() + for row in rows { + let cleanedRow = CollectionRow(section: row.section, items: row.items.filter { + let isNew = addedItems.updateValue(true, forKey: $0) == nil + if !isNew { + PlayLogWarning(category: "collection", message: "A duplicate item has been removed: \($0)") + } + return isNew + }) + cleanedRows.append(cleanedRow) + } + return cleanedRows + } + /** * Create a collection view displaying the specified data with cells delivered by the provided builder. */ @@ -229,7 +149,7 @@ struct CollectionView NSCollectionLayoutSection, @ViewBuilder cell: @escaping (IndexPath, Item) -> Cell, @ViewBuilder supplementaryView: @escaping (String, IndexPath) -> SupplementaryView) { - self.rows = rows + self.rows = Self.removeDuplicates(in: rows) self.sectionLayoutProvider = sectionLayoutProvider self.cell = cell self.supplementaryView = supplementaryView @@ -248,16 +168,9 @@ struct CollectionView UICollectionViewLayout { - let focusGuideIdentifier = "focusGuide" - - let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in - let section = context.coordinator.sectionLayoutProvider!(sectionIndex, layoutEnvironment) - let focusGuide = NSCollectionLayoutDecorationItem.background(elementKind: focusGuideIdentifier) - section.decorationItems.append(focusGuide) - return section + return UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in + return context.coordinator.sectionLayoutProvider!(sectionIndex, layoutEnvironment) } - layout.register(FocusGuideBackgroundView.self, forDecorationViewOfKind: focusGuideIdentifier) - return layout } private func reloadData(in collectionView: UICollectionView, context: Context, animated: Bool = false) { diff --git a/TV Application/Sources/Colors.swift b/TV Application/Sources/Colors.swift new file mode 100644 index 000000000..abd346323 --- /dev/null +++ b/TV Application/Sources/Colors.swift @@ -0,0 +1,20 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +extension Color { + public static let darkGray = Color(.darkGray) +} + +#if DEBUG +extension UIColor { + public static func random(alpha: CGFloat = 1) -> UIColor { + return UIColor(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1), alpha: 1) + } +} +#endif diff --git a/TV Application/Sources/DurationLabel.swift b/TV Application/Sources/DurationLabel.swift new file mode 100644 index 000000000..469191230 --- /dev/null +++ b/TV Application/Sources/DurationLabel.swift @@ -0,0 +1,38 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +struct DurationLabel: View { + let media: SRGMedia? + + private var isLivestream: Bool { + guard let media = media else { return false } + return media.contentType == .livestream || media.contentType == .scheduledLivestream + } + + private var duration: String? { + guard let media = media else { return nil } + if isLivestream { + return NSLocalizedString("Live", comment: "Short label identifying a livestream. Display in uppercase.") + } + else { + return PlayShortFormattedMinutes(media.duration / 1000) + } + } + + var body: some View { + if let duration = duration { + Text(duration) + .srgFont(.caption) + .foregroundColor(.white) + .padding([.top, .bottom], 5) + .padding([.leading, .trailing], 8) + .background(isLivestream ? Color(.play_liveRed) : Color(white: 0, opacity: 0.5)) + .cornerRadius(4) + } + } +} diff --git a/TV Application/Sources/Errors.swift b/TV Application/Sources/Errors.swift new file mode 100644 index 000000000..fb1def5d2 --- /dev/null +++ b/TV Application/Sources/Errors.swift @@ -0,0 +1,22 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderCombine +import SRGNetwork + +func friendlyMessage(for error: Error) -> String { + if let error = error as? SRGDataProviderError { + switch error { + case let .http(statusCode): + return HTTPURLResponse.srg_localizedString(forStatusCode: statusCode) + case .invalidData: + return NSLocalizedString("The data is invalid", comment: "Error message returned for invalid data") + } + } + else { + return error.localizedDescription + } +} diff --git a/TV Application/Sources/Extensions.swift b/TV Application/Sources/Extensions.swift new file mode 100644 index 000000000..6a2ccb93e --- /dev/null +++ b/TV Application/Sources/Extensions.swift @@ -0,0 +1,48 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import Foundation +import SwiftUI + +extension Comparable { + func clamped(to range: ClosedRange) -> Self { + return min(max(self, range.lowerBound), range.upperBound) + } +} + +// See https://stackoverflow.com/questions/61552497/uitableviewheaderfooterview-with-swiftui-content-getting-automatic-safe-area-ins +extension UIHostingController { + convenience public init(rootView: Content, ignoreSafeArea: Bool) { + self.init(rootView: rootView) + + if ignoreSafeArea { + disableSafeArea() + } + } + + func disableSafeArea() { + guard let viewClass = object_getClass(view) else { return } + + let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea") + if let viewSubclass = NSClassFromString(viewSubclassName) { + object_setClass(view, viewSubclass) + } + else { + guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } + guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return } + + if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) { + let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in + return .zero + } + class_addMethod(viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method)) + } + + objc_registerClassPair(viewSubclass) + object_setClass(view, viewSubclass) + } + } +} diff --git a/TV Application/Sources/FocusDetector.swift b/TV Application/Sources/FocusDetector.swift deleted file mode 100644 index b7a783e87..000000000 --- a/TV Application/Sources/FocusDetector.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -import SwiftUI - -fileprivate struct FocusDetector: View { - fileprivate struct FocusedKey: PreferenceKey { - static var defaultValue: Bool = false - - static func reduce(value: inout Bool, nextValue: () -> Bool) {} - } - - @Environment(\.isFocused) private var isFocused: Bool - - var body: some View { - Color.clear - .preference(key: FocusedKey.self, value: isFocused) - } -} - -extension View { - func reportFocusChanges() -> some View { - self.background(FocusDetector()) - } - - func onFocusChange(perform action: @escaping (Bool) -> Void) -> some View { - onPreferenceChange(FocusDetector.FocusedKey.self, perform: action) - } -} diff --git a/TV Application/Sources/FocusTracker.swift b/TV Application/Sources/FocusTracker.swift new file mode 100644 index 000000000..d20ee7820 --- /dev/null +++ b/TV Application/Sources/FocusTracker.swift @@ -0,0 +1,30 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +fileprivate struct FocusTracker: View { + let action: (Bool) -> Void + + @Environment(\.isFocused) private var isFocused: Bool + + var body: some View { + Rectangle() + .fill(Color.clear) + .onChange(of: isFocused) { action($0) } + } +} + +extension View { + /** + * Apply modifier on a view wrapped in a focusable context. The provided action block will be called + * each time a focus change is detected. This is useful to have parent or sibling views apply changes + * due to one of their children getting the focus. + */ + func onFocusChange(perform action: @escaping (Bool) -> Void) -> some View { + self.background(FocusTracker(action: action)) + } +} diff --git a/TV Application/Sources/FocusableRegion.swift b/TV Application/Sources/FocusableRegion.swift new file mode 100644 index 000000000..b3e126b6b --- /dev/null +++ b/TV Application/Sources/FocusableRegion.swift @@ -0,0 +1,41 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +/** + * A region which can capture the focus, no matter the focus comes from. + */ +struct FocusableRegion: UIViewControllerRepresentable { + private let content: () -> Content + + init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + func makeUIViewController(context: Context) -> UIHostingController { + let hostController = UIHostingController(rootView: content(), ignoreSafeArea: true) + + if let hostView = hostController.view { + let focusGuide = UIFocusGuide() + focusGuide.preferredFocusEnvironments = [hostView] + hostView.addLayoutGuide(focusGuide) + + NSLayoutConstraint.activate([ + focusGuide.topAnchor.constraint(equalTo: hostView.topAnchor), + focusGuide.bottomAnchor.constraint(equalTo: hostView.bottomAnchor), + focusGuide.leadingAnchor.constraint(equalTo: hostView.leadingAnchor), + focusGuide.trailingAnchor.constraint(equalTo: hostView.trailingAnchor) + ]) + } + + return hostController + } + + func updateUIViewController(_ uiViewController: UIHostingController, context: Context) { + uiViewController.rootView = content() + } +} diff --git a/TV Application/Sources/Fonts.swift b/TV Application/Sources/Fonts.swift index 1ff0a0c51..86aa5765a 100644 --- a/TV Application/Sources/Fonts.swift +++ b/TV Application/Sources/Fonts.swift @@ -4,6 +4,7 @@ // License information is available from the LICENSE file. // +import UIKit import SwiftUI // FIXME: Should be moved to SRG Appearance, see issues @@ -11,7 +12,7 @@ import SwiftUI // https://github.com/SRGSSR/srgappearance-apple/issues/4 enum SRGFont { - enum Style { + enum Kind { case regular case bold case heavy @@ -49,49 +50,109 @@ enum SRGFont { } } - enum Size { - case caption + enum Style { + case title1 + case title2 + case headline1 + case headline2 case subtitle - case subheadline case body - case headline - case title + case button1 + case button2 + case overline + case caption + case label - fileprivate var properties: (size: CGFloat, textStyle: Font.TextStyle) { + fileprivate var properties: (size: CGFloat, kind: SRGFont.Kind) { switch self { - case .caption: - return (20, .caption) - case .subheadline: - return (24, .subheadline) + case .title1: + return (48, .bold) + case .title2: + return (42, .medium) + case .headline1: + return (32, .regular) + case .headline2: + return (30, .medium) case .subtitle: - return (29, .body) + return (32, .light) case .body: - return (26, .body) - case .headline: - return (31, .headline) - case .title: - return (42, .title) + return (30, .regular) + case .button1: + return (32, .medium) + case .button2: + return (28, .regular) + case .overline: + return (24, .regular) + case .caption: + return (20, .medium) + case .label: + return (18, .medium) } } } - static public func font(_ style: Style, size: Size) -> Font { - let properties = size.properties - return .custom(style.name, size: properties.size, relativeTo: properties.textStyle) + static public func font(_ style: Style, relativeTo textStyle: Font.TextStyle = .body) -> Font { + let properties = style.properties + return .custom(properties.kind.name, size: properties.size, relativeTo: textStyle) + } + + static public func uiFont(_ style: Style, relativeTo textStyle: UIFont.TextStyle = .body) -> UIFont { + let properties = style.properties + return UIFontMetrics(forTextStyle: textStyle).scaledFont(for: uiFont(properties.kind, fixedSize: properties.size)) + } + + static public func font(_ kind: Kind, fixedSize: CGFloat) -> Font { + return .custom(kind.name, fixedSize: fixedSize) } - static public func font(_ style: Style, fixedSize: CGFloat) -> Font { - return .custom(style.name, fixedSize: fixedSize) + static public func uiFont(_ kind: Kind, fixedSize: CGFloat) -> UIFont { + return UIFont(name: kind.name, size: fixedSize)! } } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) extension Text { - func srgFont(_ style: SRGFont.Style, size: SRGFont.Size) -> Text { - return font(SRGFont.font(style, size: size)) + func srgFont(_ style: SRGFont.Style, relativeTo textStyle: Font.TextStyle = .body) -> Text { + return font(SRGFont.font(style, relativeTo: textStyle)) } - func srgFont(_ style: SRGFont.Style, fixedSize: CGFloat) -> Text { - return font(SRGFont.font(style, fixedSize: fixedSize)) + func srgFont(_ kind: SRGFont.Kind, fixedSize: CGFloat) -> Text { + return font(SRGFont.font(kind, fixedSize: fixedSize)) + } +} + +struct Fonts_Previews: PreviewProvider { + private struct TextPreview: View { + let text: String + let style: SRGFont.Style + + var body: some View { + Text(text) + .srgFont(style) + .padding() + } + } + + static var previews: some View { + VStack(alignment: .leading) { + Group { + TextPreview(text: "Title 1", style: .title1) + TextPreview(text: "Title 2", style: .title2) + TextPreview(text: "Headline 1", style: .headline1) + TextPreview(text: "Headline 2", style: .headline2) + TextPreview(text: "Subtitle", style: .subtitle) + } + + Group { + TextPreview(text: "Body", style: .body) + TextPreview(text: "Button 1", style: .button1) + TextPreview(text: "Button 2", style: .button2) + TextPreview(text: "Overline", style: .overline) + TextPreview(text: "Caption", style: .caption) + TextPreview(text: "Label", style: .label) + } + } + .padding() + .previewLayout(.sizeThatFits) } } diff --git a/TV Application/Sources/Formatters.swift b/TV Application/Sources/Formatters.swift deleted file mode 100644 index de96f8f61..000000000 --- a/TV Application/Sources/Formatters.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -import Foundation - -struct DateFormatters { - private static let relativeDateAndTimeFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .short - formatter.doesRelativeDateFormatting = true - return formatter - }() - - static func formattedRelativeDateAndTime(for date: Date) -> String { - return relativeDateAndTimeFormatter.string(from: date) - } - - private static let relativeDateFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .none - formatter.doesRelativeDateFormatting = true - return formatter - }() - - static func formattedRelativeDate(for date: Date) -> String { - return relativeDateFormatter.string(from: date) - } -} - -struct DurationFormatters { - private static let shortMinuteFormatter: DateComponentsFormatter = { - let formatter = DateComponentsFormatter() - formatter.allowedUnits = [.minute] - formatter.unitsStyle = .short - formatter.zeroFormattingBehavior = .pad - return formatter - }() - - static func minutes(for duration: TimeInterval) -> String { - return Self.shortMinuteFormatter.string(from: max(duration, 60))! - } - - private static let shortHourFormatter: DateComponentsFormatter = { - let formatter = DateComponentsFormatter() - formatter.allowedUnits = [.hour] - formatter.unitsStyle = .short - formatter.zeroFormattingBehavior = .pad - return formatter - }() - - static func hours(for duration: TimeInterval) -> String { - return Self.shortHourFormatter.string(from: max(duration, 60 * 60))! - } - - private static let shortDayFormatter: DateComponentsFormatter = { - let formatter = DateComponentsFormatter() - formatter.allowedUnits = [.day] - formatter.unitsStyle = .short - formatter.zeroFormattingBehavior = .pad - return formatter - }() - - static func days(for duration: TimeInterval) -> String { - return Self.shortDayFormatter.string(from: max(duration, 60 * 60 * 24))! - } -} diff --git a/TV Application/Sources/Foundation+Extensions.swift b/TV Application/Sources/Foundation+Extensions.swift new file mode 100644 index 000000000..6eb5d19b4 --- /dev/null +++ b/TV Application/Sources/Foundation+Extensions.swift @@ -0,0 +1,13 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import Foundation + +extension String { + var capitalizedFirstLetter: String { + return prefix(1).capitalized + dropFirst() + } +} diff --git a/TV Application/Sources/HeroMediaCell.swift b/TV Application/Sources/HeroMediaCell.swift index 31ef0a6b0..ec1e7efaf 100644 --- a/TV Application/Sources/HeroMediaCell.swift +++ b/TV Application/Sources/HeroMediaCell.swift @@ -5,24 +5,54 @@ // import SRGAppearance -import SRGLetterbox import SwiftUI struct HeroMediaCell: View { + let media: SRGMedia? + + private var redactionReason: RedactionReasons { + return media == nil ? .placeholder : .init() + } + + var body: some View { + GeometryReader { geometry in + Button(action: { + if let media = media { + navigateToMedia(media) + } + }) { + HStack(spacing: 0) { + MediaVisualView(media: media, scale: .large) + .frame(width: geometry.size.height * 16 / 9, height: geometry.size.height) + DescriptionView(media: media) + .padding() + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .frame(width: geometry.size.width, height: geometry.size.height) + .background(Color(.srg_color(fromHexadecimalString: "#232323")!)) + .redacted(reason: redactionReason) + .accessibilityElement() + .accessibilityLabel(MediaDescription.accessibilityLabel(for: media)) + .accessibility(addTraits: .isButton) + } + .buttonStyle(CardButtonStyle()) + } + } + private struct DescriptionView: View { let media: SRGMedia? var body: some View { VStack { Spacer() - Text(MediaDescription.title(for: media)) - .srgFont(.regular, size: .subheadline) + Text(MediaDescription.title(for: media, style: .show)) + .srgFont(.subtitle) .lineLimit(1) .opacity(0.8) Spacer() .frame(height: 10) - Text(MediaDescription.subtitle(for: media)) - .srgFont(.medium, size: .title) + Text(MediaDescription.subtitle(for: media, style: .show)) + .srgFont(.title2) .lineLimit(2) .multilineTextAlignment(.center) .padding() @@ -30,47 +60,18 @@ struct HeroMediaCell: View { Spacer() .frame(height: 20) Text(summary) - .srgFont(.regular, size: .subtitle) + .srgFont(.body) .lineLimit(4) .multilineTextAlignment(.center) .opacity(0.8) .padding() } + if let media = media { + AvailabilityBadge(media: media) + } Spacer() } .foregroundColor(.white) } } - - let media: SRGMedia? - - private var redactionReason: RedactionReasons { - return media == nil ? .placeholder : .init() - } - - var body: some View { - GeometryReader { geometry in - Button(action: { - // TODO: Could / should be presented with SwiftUI, but presentation flag must be part of topmost state - if let media = media, - let rootViewController = UIApplication.shared.windows.first?.rootViewController { - let letterboxViewController = SRGLetterboxViewController() - letterboxViewController.controller.playMedia(media, at: nil, withPreferredSettings: nil) - rootViewController.present(letterboxViewController, animated: true, completion: nil) - } - }) { - HStack(spacing: 0) { - MediaVisual(media: media, scale: .large) - .frame(width: geometry.size.height * 16 / 9, height: geometry.size.height) - DescriptionView(media: media) - .padding() - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - .frame(width: geometry.size.width, height: geometry.size.height) - .background(Color(.srg_color(fromHexadecimalString: "#232323")!)) - .redacted(reason: redactionReason) - } - .buttonStyle(CardButtonStyle()) - } - } } diff --git a/TV Application/Sources/HistoryModel.swift b/TV Application/Sources/HistoryModel.swift new file mode 100644 index 000000000..9f27ae0d1 --- /dev/null +++ b/TV Application/Sources/HistoryModel.swift @@ -0,0 +1,88 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderCombine +import SRGUserData + +// TODO: Should implement a model protocol. Also should implement better refresh in general, probably +// either deep (erase all cached content and restart from the first page) or smart incremental +// (probably do not restart from the first page but updates the list with single changes received +// from the history, so that they appear inlined into the results without having to reload everything). +class HistoryModel: ObservableObject { + enum State { + case loading + case failed(error: Error) + case loaded(medias: [SRGMedia]) + } + + @Published private(set) var state = State.loaded(medias: []) + + private var cancellables = Set() + private var medias: [SRGMedia] = [] + private var nextPage: SRGDataProvider.Medias.Page? = nil + + func refresh() { + guard medias.isEmpty else { return } + loadNextPage() + } + + func loadNextPage(from media: SRGMedia? = nil) { + guard let publisher = publisher(from: media) else { return } + publisher + .receive(on: DispatchQueue.main) + .handleEvents(receiveRequest: { _ in + if self.medias.isEmpty { + self.state = .loading + } + }) + .sink(receiveCompletion: { completion in + if case let .failure(error) = completion { + self.state = .failed(error: error) + } + }, receiveValue: { result in + self.medias.append(contentsOf: result.medias) + self.state = .loaded(medias: self.medias) + self.nextPage = result.nextPage + }) + .store(in: &cancellables) + } + + func cancelRefresh() { + cancellables = [] + } + + private func historyEntries() -> Future<[SRGHistoryEntry], Error> { + return Future { promise in + // TODO: Compile-checked keypath + let sortDescriptor = NSSortDescriptor(key: "date", ascending: false) + SRGUserData.current!.history.historyEntries(matching: nil, sortedWith: [sortDescriptor]) { historyEntries, error in + if let error = error { + promise(.failure(error)) + } + else { + promise(.success(historyEntries ?? [])) + } + } + } + } + + private func publisher(from media: SRGMedia?) -> AnyPublisher? { + if media != nil { + guard let nextPage = nextPage, media == medias.last else { return nil } + return SRGDataProvider.current!.medias(at: nextPage) + } + else { + return historyEntries() + .map { historyEntries in + historyEntries.compactMap { $0.uid } + } + .flatMap { urns in + return SRGDataProvider.current!.medias(withUrns: urns, pageSize: ApplicationConfiguration.shared.pageSize) + } + .eraseToAnyPublisher() + } + } +} diff --git a/TV Application/Sources/HistoryView.swift b/TV Application/Sources/HistoryView.swift new file mode 100644 index 000000000..6a2e35a02 --- /dev/null +++ b/TV Application/Sources/HistoryView.swift @@ -0,0 +1,138 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +// TODO: We could have a generic media grid view displaying a model (we need a protocol to define what +// this is). Then TopicDetailView and HistoryView will be replaced with this view. +struct HistoryView: View { + @ObservedObject var model = HistoryModel() + + static let headerHeight: CGFloat = 60 + + enum Section: Hashable { + case medias + case information + } + + enum Content: Hashable { + case loading + case message(_ message: String, iconName: String) + case media(_ media: SRGMedia) + } + + typealias Row = CollectionRow + + private var rows: [Row] { + switch model.state { + case .loading: + return [Row(section: .information, items: [.loading])] + case let .failed(error: error): + let item = Content.message(friendlyMessage(for: error), iconName: "error-90") + return [Row(section: .information, items: [item])] + case let .loaded(medias: medias): + if !medias.isEmpty { + return [Row(section: .medias, items: medias.map { .media($0) })] + } + else { + let item = Content.message(NSLocalizedString("No results", comment: "Default text displayed when no results are available"), iconName: "media-90") + return [Row(section: .information, items: [item])] + } + } + } + + private static func header() -> [NSCollectionLayoutBoundarySupplementaryItem] { + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(Self.headerHeight)), + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .topLeading + ) + return [header] + } + + private static func layoutSection(for section: Section, geometry: GeometryProxy) -> NSCollectionLayoutSection { + switch section { + case .medias: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(380)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 4) + group.interItemSpacing = .fixed(40) + + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0) + section.interGroupSpacing = 40 + section.boundarySupplementaryItems = Self.header() + return section + case .information: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let height = geometry.size.height - Self.headerHeight + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(height)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + section.boundarySupplementaryItems = Self.header() + return section + } + } + + var body: some View { + GeometryReader { geometry in + CollectionView(rows: rows) { _, _ in + return Self.layoutSection(for: rows.first!.section, geometry: geometry) + } cell: { _, item in + switch item { + case .loading: + ActivityIndicator() + case let .message(text, iconName): + VStack(spacing: 20) { + Image(iconName) + Text(text) + .srgFont(.body) + .lineLimit(2) + .foregroundColor(.white) + } + .opacity(0.8) + .padding() + case let .media(media): + MediaCell(media: media, style: .show) + .onAppear { + model.loadNextPage(from: media) + } + } + } supplementaryView: { _, _ in + HeaderView(title: "History (WIP)") + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.play_black)) + .edgesIgnoringSafeArea(.all) + .onAppear { + model.refresh() + } + .onDisappear { + model.cancelRefresh() + } + .onWake { + model.refresh() + } + } + } + + private struct HeaderView: View { + let title: String + + var body: some View { + Text(title) + .srgFont(.title2) + .foregroundColor(.white) + .opacity(0.8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) + } + } +} diff --git a/TV Application/Sources/HomeModel.swift b/TV Application/Sources/HomeModel.swift index d34db05d4..6961e78ba 100644 --- a/TV Application/Sources/HomeModel.swift +++ b/TV Application/Sources/HomeModel.swift @@ -5,192 +5,6 @@ // import SRGDataProviderCombine -/** - * The appearance to apply to a home row. - */ -enum HomeRowAppearance: Equatable { - case `default` - case hero -} - -/** - * The row identifier. - */ -enum HomeRowId: Hashable { - case tvTrending(appearance: HomeRowAppearance) - case tvLatest - case tvMostPopular - case tvSoonExpiring - case tvLatestForModule(_ module: SRGModule?, type: SRGModuleType) - case tvLatestForTopic(_ topic: SRGTopic?) - case tvTopicsAccess - - case radioLatestEpisodes(channelUid: String) - case radioMostPopular(channelUid: String) - case radioLatest(channelUid: String) - case radioLatestVideos(channelUid: String) - case radioAllShows(channelUid: String) - - case tvLive - case radioLive - case radioLiveSatellite - - case tvLiveCenter - case tvScheduledLivestreams - - func publisher() -> AnyPublisher<[HomeRowItem], Error>? { - let dataProvider = SRGDataProvider.current! - let configuration = ApplicationConfiguration.shared - - let vendor = configuration.vendor - let pageSize = configuration.pageSize - - switch self { - case .tvTrending: - return dataProvider.tvTrendingMedias(for: vendor, limit: pageSize, editorialLimit: configuration.tvTrendingEditorialLimit?.uintValue, - episodesOnly: configuration.tvTrendingEpisodesOnly) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .tvLatest: - return dataProvider.tvLatestMedias(for: vendor, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .tvMostPopular: - return dataProvider.tvMostPopularMedias(for: vendor, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .tvSoonExpiring: - return dataProvider.tvSoonExpiringMedias(for: vendor, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case let .tvLatestForModule(module, type: _): - guard let urn = module?.urn else { return nil } - return dataProvider.latestMediasForModule(withUrn: urn, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .tvTopicsAccess: - return SRGDataProvider.current!.tvTopics(for: vendor) - .map { $0.topics.map { HomeRowItem(rowId: self, content: .topic($0)) } } - .eraseToAnyPublisher() - case let .tvLatestForTopic(topic): - guard let urn = topic?.urn else { return nil } - return dataProvider.latestMediasForTopic(withUrn: urn, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case let .radioLatestEpisodes(channelUid: channelUid): - return dataProvider.radioLatestEpisodes(for: vendor, channelUid: channelUid, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case let .radioMostPopular(channelUid: channelUid): - return dataProvider.radioMostPopularMedias(for: vendor, channelUid: channelUid, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case let .radioLatest(channelUid: channelUid): - return dataProvider.radioLatestMedias(for: vendor, channelUid: channelUid, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case let .radioLatestVideos(channelUid: channelUid): - return dataProvider.radioLatestVideos(for: vendor, channelUid: channelUid, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case let .radioAllShows(channelUid): - return SRGDataProvider.current!.radioShows(for: vendor, channelUid: channelUid, pageSize: SRGDataProviderUnlimitedPageSize) - .map { $0.shows.map { HomeRowItem(rowId: self, content: .show($0)) } } - .eraseToAnyPublisher() - case .tvLive: - return dataProvider.tvLivestreams(for: vendor) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .radioLive: - return dataProvider.radioLivestreams(for: vendor, contentProviders: .default) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .radioLiveSatellite: - return dataProvider.radioLivestreams(for: vendor, contentProviders: .swissSatelliteRadio) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .tvLiveCenter: - return dataProvider.liveCenterVideos(for: vendor, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - case .tvScheduledLivestreams: - return dataProvider.tvScheduledLivestreams(for: vendor, pageSize: pageSize) - .map { $0.medias.map { HomeRowItem(rowId: self, content: .media($0)) } } - .eraseToAnyPublisher() - } - } - - var title: String? { - switch self { - case let .tvTrending(appearance: appearance): - return appearance != .hero ? NSLocalizedString("Trending videos", comment: "Title label used to present trending TV videos") : nil - case .tvLatest: - return NSLocalizedString("Latest videos", comment: "Title label used to present the latest videos") - case .tvMostPopular: - return NSLocalizedString("Most popular", comment: "Title label used to present the TV most popular videos") - case .tvSoonExpiring: - return NSLocalizedString("Available for a limited time", comment: "Title label used to present the soon expiring videos") - case let .tvLatestForModule(module, _): - return module?.title ?? NSLocalizedString("Highlights", comment: "Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available") - case let .tvLatestForTopic(topic): - return topic?.title ?? NSLocalizedString("Topics", comment: "Title label used to present TV topics while loading. It appears if no network connection is available and no cache is available") - case .radioLatestEpisodes: - return NSLocalizedString("The latest episodes", comment: "Title label used to present the radio latest audio episodes") - case .radioMostPopular: - return NSLocalizedString("Most listened to", comment: "Title label used to present the radio most popular audio medias") - case .radioLatest: - return NSLocalizedString("The latest audios", comment: "Title label used to present the radio latest audios") - case .radioLatestVideos: - return NSLocalizedString("Latest videos", comment: "Title label used to present the radio latest videos") - case .radioAllShows: - return NSLocalizedString("Shows", comment: "Title label used to present radio associated shows") - case .tvLive: - return NSLocalizedString("TV channels", comment: "Title label to present main TV livestreams") - case .radioLive: - return NSLocalizedString("Radio channels", comment: "Title label to present main radio livestreams") - case .radioLiveSatellite: - return NSLocalizedString("Thematic radios", comment: "Title label to present Swiss satellite radios") - case .tvLiveCenter: - return NSLocalizedString("Sport", comment: "Title label used to present live center medias") - case .tvScheduledLivestreams: - return NSLocalizedString("Events", comment: "Title label used to present scheduled livestream medias") - default: - return nil - } - } - - var lead: String? { - if case let .tvLatestForModule(module, type: _) = self { - return module?.lead - } - else { - return nil - } - } -} - -struct HomeRowItem: Hashable { - // Various kinds of objects which can be displayed on the home. - enum Content: Hashable { - case mediaPlaceholder(index: Int) - case media(_ media: SRGMedia) - - case showPlaceholder(index: Int) - case show(_ show: SRGShow) - - case topicPlaceholder(index: Int) - case topic(_ topic: SRGTopic) - } - - // Some items might appear in several rows but need to be uniquely defined. We thus add the section to each item - // to ensure unicity. - let rowId: HomeRowId - let content: Content - - static func == (lhs: HomeRowItem, rhs: HomeRowItem) -> Bool { - return lhs.rowId == rhs.rowId && lhs.content == rhs.content - } -} class HomeModel: Identifiable, ObservableObject { enum Id { @@ -202,12 +16,12 @@ class HomeModel: Identifiable, ObservableObject { static let numberOfPlaceholders = 10 let id: Id - let rowIds: [HomeRowId] + let rowIds: [RowId] - private var eventRowIds: [HomeRowId] = [] - private var topicRowIds: [HomeRowId] = [] + private var eventRowIds: [RowId] = [] + private var topicRowIds: [RowId] = [] - typealias Row = CollectionRow + typealias Row = CollectionRow @Published private(set) var rows = [Row]() @@ -232,7 +46,7 @@ class HomeModel: Identifiable, ObservableObject { cancellables = [] } - private static func rowIds(for id: Id) -> [HomeRowId] { + private static func rowIds(for id: Id) -> [RowId] { switch id { case .video: return ApplicationConfiguration.shared.videoHomeRowIds() @@ -243,26 +57,26 @@ class HomeModel: Identifiable, ObservableObject { } } - private func addRow(with id: HomeRowId, to rows: inout [Row]) { + private func addRow(with id: RowId, to rows: inout [Row]) { if let existingRow = self.rows.first(where: { $0.section == id }) { rows.append(existingRow) } else { - func items(for id: HomeRowId) -> [HomeRowItem] { + func items(for id: RowId) -> [RowItem] { switch id { case .tvTopicsAccess: - return (0.. AnyPublisher<[RowItem], Error>? { + let dataProvider = SRGDataProvider.current! + let configuration = ApplicationConfiguration.shared + + let vendor = configuration.vendor + let pageSize = configuration.pageSize + + switch self { + case .tvTrending: + if configuration.tvTrendingPrefersHeroStage { + return dataProvider.tvHeroStageMedias(for: vendor) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + } + else { + return dataProvider.tvTrendingMedias(for: vendor, limit: pageSize, editorialLimit: configuration.tvTrendingEditorialLimit?.uintValue, + episodesOnly: configuration.tvTrendingEpisodesOnly) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + } + case .tvLatest: + return dataProvider.tvLatestMedias(for: vendor, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .tvWebFirst: + return dataProvider.tvLatestWebFirstEpisodes(for: vendor, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .tvMostPopular: + return dataProvider.tvMostPopularMedias(for: vendor, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .tvSoonExpiring: + return dataProvider.tvSoonExpiringMedias(for: vendor, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case let .tvLatestForModule(module, type: _): + guard let urn = module?.urn else { return nil } + return dataProvider.latestMediasForModule(withUrn: urn, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .tvTopicsAccess: + return SRGDataProvider.current!.tvTopics(for: vendor) + .map { $0.topics.map { RowItem(rowId: self, content: .topic($0)) } } + .eraseToAnyPublisher() + case let .tvLatestForTopic(topic): + guard let urn = topic?.urn else { return nil } + return dataProvider.latestMediasForTopic(withUrn: urn, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case let .radioLatestEpisodes(channelUid: channelUid): + return dataProvider.radioLatestEpisodes(for: vendor, channelUid: channelUid, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case let .radioMostPopular(channelUid: channelUid): + return dataProvider.radioMostPopularMedias(for: vendor, channelUid: channelUid, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case let .radioLatest(channelUid: channelUid): + return dataProvider.radioLatestMedias(for: vendor, channelUid: channelUid, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case let .radioLatestVideos(channelUid: channelUid): + return dataProvider.radioLatestVideos(for: vendor, channelUid: channelUid, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case let .radioAllShows(channelUid): + return SRGDataProvider.current!.radioShows(for: vendor, channelUid: channelUid, pageSize: SRGDataProviderUnlimitedPageSize) + .map { $0.shows.map { RowItem(rowId: self, content: .show($0)) } } + .eraseToAnyPublisher() + case .tvLive: + return dataProvider.tvLivestreams(for: vendor) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .radioLive: + return dataProvider.radioLivestreams(for: vendor, contentProviders: .default) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .radioLiveSatellite: + return dataProvider.radioLivestreams(for: vendor, contentProviders: .swissSatelliteRadio) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .tvLiveCenter: + return dataProvider.liveCenterVideos(for: vendor, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + case .tvScheduledLivestreams: + return dataProvider.tvScheduledLivestreams(for: vendor, pageSize: pageSize) + .map { $0.medias.map { RowItem(rowId: self, content: .media($0)) } } + .eraseToAnyPublisher() + } + } + + var title: String? { + switch self { + case let .tvTrending(appearance: appearance): + return appearance != .hero ? NSLocalizedString("Trending videos", comment: "Title label used to present trending TV videos") : nil + case .tvLatest: + return NSLocalizedString("Latest videos", comment: "Title label used to present the latest videos") + case .tvWebFirst: + return NSLocalizedString("Already available", comment: "Title label used to present already available videos, usually badged as web first") + case .tvMostPopular: + return NSLocalizedString("Most popular", comment: "Title label used to present the TV most popular videos") + case .tvSoonExpiring: + return NSLocalizedString("Available for a limited time", comment: "Title label used to present the soon expiring videos") + case let .tvLatestForModule(module, _): + return module?.title ?? NSLocalizedString("Highlights", comment: "Title label used to present TV modules while loading. It appears if no network connection is available and no cache is available") + case let .tvLatestForTopic(topic): + return topic?.title ?? NSLocalizedString("Topics", comment: "Title label used to present TV topics while loading. It appears if no network connection is available and no cache is available") + case .radioLatestEpisodes: + return NSLocalizedString("The latest episodes", comment: "Title label used to present the radio latest audio episodes") + case .radioMostPopular: + return NSLocalizedString("Most listened to", comment: "Title label used to present the radio most popular audio medias") + case .radioLatest: + return NSLocalizedString("The latest audios", comment: "Title label used to present the radio latest audios") + case .radioLatestVideos: + return NSLocalizedString("Latest videos", comment: "Title label used to present the radio latest videos") + case .radioAllShows: + return NSLocalizedString("Shows", comment: "Title label used to present radio associated shows") + case .tvLive: + return NSLocalizedString("TV channels", comment: "Title label to present main TV livestreams") + case .radioLive: + return NSLocalizedString("Radio channels", comment: "Title label to present main radio livestreams") + case .radioLiveSatellite: + return NSLocalizedString("Thematic radios", comment: "Title label to present Swiss satellite radios") + case .tvLiveCenter: + return NSLocalizedString("Sport", comment: "Title label used to present live center medias") + case .tvScheduledLivestreams: + return NSLocalizedString("Events", comment: "Title label used to present scheduled livestream medias") + default: + return nil + } + } + + var lead: String? { + if case let .tvLatestForModule(module, type: _) = self { + return module?.lead + } + else { + return nil + } + } + } +} + +extension HomeModel { + struct RowItem: Hashable { + // Various kinds of objects which can be displayed on the home. + enum Content: Hashable { + case mediaPlaceholder(index: Int) + case media(_ media: SRGMedia) + + case showPlaceholder(index: Int) + case show(_ show: SRGShow) + + case topicPlaceholder(index: Int) + case topic(_ topic: SRGTopic) + } + + // Some items might appear in several rows but need to be uniquely defined. We thus add the section to each item + // to ensure unicity. + let rowId: RowId + let content: Content + + static func == (lhs: RowItem, rhs: RowItem) -> Bool { + return lhs.rowId == rhs.rowId && lhs.content == rhs.content + } + } +} diff --git a/TV Application/Sources/HomeView.swift b/TV Application/Sources/HomeView.swift index 5389ab09c..5e7952cf4 100644 --- a/TV Application/Sources/HomeView.swift +++ b/TV Application/Sources/HomeView.swift @@ -4,98 +4,33 @@ // License information is available from the LICENSE file. // +import SRGAnalyticsSwiftUI import SwiftUI struct HomeView: View { @ObservedObject var model: HomeModel - private struct Cell: View { - let item: HomeRowItem - - private static func isHeroAppearance(for item: HomeRowItem) -> Bool { - if case let .tvTrending(appearance: appearance) = item.rowId, appearance == .hero { - return true - } - else { - return false - } - } - - var body: some View { - switch item.content { - case let .media(media): - if Self.isHeroAppearance(for: item) { - HeroMediaCell(media: media) - } - else { - MediaCell(media: media) - } - case .mediaPlaceholder: - if Self.isHeroAppearance(for: item) { - HeroMediaCell(media: nil) - } - else { - MediaCell(media: nil) - } - case let .show(show): - ShowCell(show: show) - case .showPlaceholder: - ShowCell(show: nil) - case let .topic(topic): - TopicCell(topic: topic) - case .topicPlaceholder: - TopicCell(topic: nil) - } - } - } - - private struct SupplementaryView: View { - let rowId: HomeRowId - let kind: String - - var body: some View { - if kind == UICollectionView.elementKindSectionHeader { - VStack(alignment: .leading) { - if let title = rowId.title { - Text(title) - .srgFont(.medium, size: .title) - .lineLimit(1) - } - if let lead = rowId.lead { - Text(lead) - .srgFont(.light, size: .headline) - .lineLimit(1) - .opacity(0.8) - Spacer() - .frame(height: 10) - } - } - .opacity(0.8) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) - } - } - } - - private static func swimlaneLayoutSection(for rowId: HomeRowId) -> NSCollectionLayoutSection { - func layoutGroupSize(for rowId: HomeRowId) -> NSCollectionLayoutSize { + private static func swimlaneLayoutSection(for rowId: HomeModel.RowId) -> NSCollectionLayoutSection { + func layoutGroupSize(for rowId: HomeModel.RowId) -> NSCollectionLayoutSize { switch rowId { case let .tvTrending(appearance: appearance): if appearance == .hero { return NSCollectionLayoutSize(widthDimension: .absolute(1740), heightDimension: .absolute(680)) } else { - return NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(211)) + return NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(360)) } case .tvTopicsAccess: - return NSCollectionLayoutSize(widthDimension: .absolute(250), heightDimension: .absolute(141)) + let width = CGFloat(250) + return NSCollectionLayoutSize(widthDimension: .absolute(width), heightDimension: .absolute(width * 9 / 16)) case .radioAllShows: - return NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(211)) + return NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(260)) default: - return NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(340)) + return NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(360)) } } - func contentInsets(for rowId: HomeRowId) -> NSDirectionalEdgeInsets { + func contentInsets(for rowId: HomeModel.RowId) -> NSDirectionalEdgeInsets { switch rowId { case .tvTopicsAccess: return NSDirectionalEdgeInsets(top: 80, leading: 0, bottom: 80, trailing: 0) @@ -104,7 +39,7 @@ struct HomeView: View { } } - func continuousGroupLeadingBoundary(for rowId: HomeRowId) -> UICollectionLayoutSectionOrthogonalScrollingBehavior { + func continuousGroupLeadingBoundary(for rowId: HomeModel.RowId) -> UICollectionLayoutSectionOrthogonalScrollingBehavior { if case let .tvTrending(appearance: appearance) = rowId, appearance == .hero { return .continuous } @@ -113,7 +48,7 @@ struct HomeView: View { } } - func boundarySupplementaryItems(for rowId: HomeRowId) -> [NSCollectionLayoutBoundarySupplementaryItem] { + func header(for rowId: HomeModel.RowId) -> [NSCollectionLayoutBoundarySupplementaryItem] { guard let headerHeight = swimlaneSectionHeaderHeight(for: rowId) else { return [] } let header = NSCollectionLayoutBoundarySupplementaryItem( layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(headerHeight)), @@ -133,11 +68,11 @@ struct HomeView: View { section.orthogonalScrollingBehavior = continuousGroupLeadingBoundary(for: rowId) section.interGroupSpacing = 40 section.contentInsets = contentInsets(for: rowId) - section.boundarySupplementaryItems = boundarySupplementaryItems(for: rowId) + section.boundarySupplementaryItems = header(for: rowId) return section } - private static func swimlaneSectionHeaderHeight(for rowId: HomeRowId) -> CGFloat? { + private static func swimlaneSectionHeaderHeight(for rowId: HomeModel.RowId) -> CGFloat? { guard rowId.title != nil else { return nil } if let lead = rowId.lead, !lead.isEmpty { return 140 @@ -148,17 +83,107 @@ struct HomeView: View { } var body: some View { - CollectionView(rows: model.rows) { sectionIndex, layoutEnvironment in + CollectionView(rows: model.rows) { sectionIndex, _ in let rowId = model.rows[sectionIndex].section return Self.swimlaneLayoutSection(for: rowId) } cell: { _, item in Cell(item: item) - } supplementaryView: { kind, indexPath in + } supplementaryView: { _, indexPath in let rowId = model.rows[indexPath.section].section - SupplementaryView(rowId: rowId, kind: kind) + HeaderView(rowId: rowId) } .synchronizeParentTabScrolling() .frame(maxWidth: .infinity, maxHeight: .infinity) .ignoresSafeArea(.all) + .tracked(withTitle: analyticsPageTitle, levels: analyticsPageLevels) + } + + private struct Cell: View { + let item: HomeModel.RowItem + + private static func isHeroAppearance(for item: HomeModel.RowItem) -> Bool { + if case let .tvTrending(appearance: appearance) = item.rowId, appearance == .hero { + return true + } + else { + return false + } + } + + var body: some View { + switch item.content { + case let .media(media): + if Self.isHeroAppearance(for: item) { + HeroMediaCell(media: media) + } + else if HomeModel.RowId.liveIds.contains(item.rowId) { + if media.contentType == .livestream || media.contentType == .scheduledLivestream { + LiveMediaCell(media: media) + } + else { + MediaCell(media: media, style: .date) { + navigateToMedia(media, play: true) + } + } + } + else { + MediaCell(media: media, style: .show) + } + case .mediaPlaceholder: + if Self.isHeroAppearance(for: item) { + HeroMediaCell(media: nil) + } + else { + MediaCell(media: nil, style: .show) + } + case let .show(show): + ShowCell(show: show) + case .showPlaceholder: + ShowCell(show: nil) + case let .topic(topic): + TopicCell(topic: topic) + case .topicPlaceholder: + TopicCell(topic: nil) + } + } + } + + private struct HeaderView: View { + let rowId: HomeModel.RowId + + var body: some View { + VStack(alignment: .leading) { + if let title = rowId.title { + Text(title) + .srgFont(.title2) + .lineLimit(1) + } + if let lead = rowId.lead { + Text(lead) + .srgFont(.subtitle) + .lineLimit(1) + .opacity(0.8) + } + } + .opacity(0.8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) + } + } +} + +extension HomeView { + private var analyticsPageTitle: String { + return AnalyticsPageTitle.home.rawValue + } + + private var analyticsPageLevels: [String] { + switch self.model.id { + case .video: + return [AnalyticsPageLevel.play.rawValue, AnalyticsPageLevel.video.rawValue] + case let .audio(channel): + return [AnalyticsPageLevel.play.rawValue, AnalyticsPageLevel.audio.rawValue, channel.name] + case .live: + return [AnalyticsPageLevel.play.rawValue, AnalyticsPageLevel.live.rawValue] + } } } diff --git a/TV Application/Sources/ImageView.swift b/TV Application/Sources/ImageView.swift index 015baa149..d3f56326c 100644 --- a/TV Application/Sources/ImageView.swift +++ b/TV Application/Sources/ImageView.swift @@ -8,42 +8,91 @@ import FetchImage import SwiftUI struct ImageView: View { + let url: URL? + let contentMode: ContentMode + + init(url: URL?, contentMode: ContentMode = .fit) { + self.url = url + self.contentMode = contentMode + } + + var body: some View { + if let url = url { + FetchView(url: url, contentMode: contentMode) + } + } + private struct FetchView: View { - @ObservedObject var image: FetchImage let contentMode: ContentMode + @ObservedObject var fetchImage: FetchImage + + // Use separate state so that we can track image loading and only animate such changes. Since FetchImage + // immediately fetches the image the state is initially set to true. + @State var isLoading: Bool = true + init(url: URL, contentMode: ContentMode) { - image = FetchImage(url: url) + fetchImage = FetchImage(url: url) self.contentMode = contentMode } - + + private func optimalContentMode(for size: CGSize) -> ContentMode { + guard contentMode == .fit, + let imageSize = fetchImage.image?.size else { return contentMode } + + let tolerance = CGFloat(0.01) + + // Calculate the size of the fitted image in the provided size. If matching up to a given tolerance, + // then apply filling behavior instead to have a perfect fit (thus entirely hiding the image background) + // while only slightly stretching the image. + if size.width > size.height { + let resizedImageHeight = imageSize.height * size.width / imageSize.width + return (resizedImageHeight - size.height).magnitude / size.height < tolerance ? .fill : .fit + } + else { + let resizedImageWidth = imageSize.width * size.height / imageSize.height + return (resizedImageWidth - size.width).magnitude / size.width < tolerance ? .fill : .fit + } + } + public var body: some View { GeometryReader { geometry in - image.view? + fetchImage.view? .resizable() - .aspectRatio(contentMode: contentMode) + .aspectRatio(contentMode: optimalContentMode(for: geometry.size)) .frame(width: geometry.size.width, height: geometry.size.height) .clipped() - .onAppear(perform: image.fetch) - .onDisappear(perform: image.cancel) + .onReceive(fetchImage.$isLoading) { loading in + // Use async dispatch to avoid animation glitches + DispatchQueue.main.async { + withAnimation { + isLoading = loading + } + } + } + .onAppear(perform: fetchImage.fetch) + .onDisappear(perform: fetchImage.cancel) + .opacity(isLoading ? 0 : 1) } } } - - let url: URL? - let contentMode: ContentMode - - init(url: URL?, contentMode: ContentMode = .fit) { - self.url = url - self.contentMode = contentMode - } - - var body: some View { - ZStack { - if let url = url { - FetchView(url: url, contentMode: contentMode) - } +} + +struct ImageView_Previews: PreviewProvider { + static var previews: some View { + Group { + ImageView(url: URL(string: "https://www.rts.ch/2020/11/09/11/29/11737826.image/16x9/scale/width/450")!) + .previewLayout(PreviewLayout.sizeThatFits) + .previewDisplayName("Intrinsic size") + + ImageView(url: URL(string: "https://www.rts.ch/2020/11/09/11/29/11737826.image/16x9/scale/width/450")!) + .previewLayout(.fixed(width: 600, height: 600)) + .previewDisplayName("600x600, fit") + + ImageView(url: URL(string: "https://www.rts.ch/2020/11/09/11/29/11737826.image/16x9/scale/width/450")!, contentMode: .fill) + .previewLayout(.fixed(width: 600, height: 600)) + .previewDisplayName("600x600, fill") } - .animation(.default) } } + diff --git a/TV Application/Sources/LabeledButton.swift b/TV Application/Sources/LabeledButton.swift new file mode 100644 index 000000000..d0fd87ebb --- /dev/null +++ b/TV Application/Sources/LabeledButton.swift @@ -0,0 +1,50 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +struct LabeledButton: View { + let icon: String + let label: String + let action: () -> Void + + @State private var isFocused: Bool = false + + var body: some View { + VStack { + Button(action: action) { + Image(icon) + .foregroundColor(isFocused ? .darkGray : .white) + .onFocusChange { isFocused = $0 } + .accessibilityElement() + .accessibilityLabel(label) + .accessibility(addTraits: .isButton) + } + Text(label) + .srgFont(.button2) + .lineLimit(2) + .multilineTextAlignment(.center) + .foregroundColor(isFocused ? .white : .gray) + } + .frame(width: 130) + } +} + +struct LabeledButton_Previews: PreviewProvider { + static var previews: some View { + Group { + LabeledButton(icon: "episodes-22", label: "Episodes", action: {}) + .previewLayout(PreviewLayout.sizeThatFits) + .padding() + .previewDisplayName("Short label") + + LabeledButton(icon: "favorite-22", label: "Watch later", action: {}) + .previewLayout(PreviewLayout.sizeThatFits) + .padding() + .previewDisplayName("Long label") + } + } +} diff --git a/TV Application/Sources/LiveMediaCell.swift b/TV Application/Sources/LiveMediaCell.swift new file mode 100644 index 000000000..e92a2e743 --- /dev/null +++ b/TV Application/Sources/LiveMediaCell.swift @@ -0,0 +1,204 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// +import SwiftUI + +protocol LiveMediaData { + var media: SRGMedia? { get } + var programComposition: SRGProgramComposition? { get } +} + +extension LiveMediaData { + var channel: SRGChannel? { + return programComposition?.channel ?? media?.channel + } + + func program(at date: Date) -> SRGProgram? { + return programComposition?.play_program(at: date) + } +} + +struct LiveMediaCell: View, LiveMediaData { + let media: SRGMedia? + + @State var programComposition: SRGProgramComposition? + @State private var channelObserver: Any? + @State private var date = Date() + @State private var isFocused: Bool = false + + private var redactionReason: RedactionReasons { + return media == nil ? .placeholder : .init() + } + + private var accessibilityLabel: String { + if let channel = channel { + var label = String(format: PlaySRGAccessibilityLocalizedString("%@ live", "Live content label, with a channel title"), channel.title) + if let currentProgram = program(at: Date()) { + label.append(", \(currentProgram.title)") + } + return label + } + else { + return MediaDescription.accessibilityLabel(for: media) + } + } + + private func registerForChannelUpdates() { + guard let media = media, + let channel = media.channel, + media.contentType == .livestream else { return } + channelObserver = ChannelService.shared.addObserverForUpdates(with: channel, livestreamUid: media.uid) { composition in + programComposition = composition + date = Date() + } + } + + private func unregisterChannelUpdates() { + ChannelService.shared.removeObserver(channelObserver) + } + + var body: some View { + GeometryReader { geometry in + VStack(spacing: 10) { + Button(action: { + if let media = media { + navigateToMedia(media, play: true) + } + }) { + VisualView(media: media, programComposition: programComposition, date: date) + .frame(width: geometry.size.width, height: geometry.size.width * 9 / 16) + .onFocusChange { isFocused = $0 } + .accessibilityElement() + .accessibilityLabel(accessibilityLabel) + .accessibility(addTraits: .isButton) + } + .buttonStyle(CardButtonStyle()) + + DescriptionView(media: media, programComposition: programComposition, date: date) + .frame(width: geometry.size.width, alignment: .leading) + .animation(nil) + .opacity(isFocused ? 1 : 0.5) + .offset(x: 0, y: isFocused ? 10 : 0) + .scaleEffect(isFocused ? 1.1 : 1, anchor: .top) + .animation(.easeInOut(duration: 0.2)) + } + .redacted(reason: redactionReason) + } + .onAppear { + registerForChannelUpdates() + } + .onDisappear { + unregisterChannelUpdates() + } + } + + private struct VisualView: View, LiveMediaData { + let media: SRGMedia? + let programComposition: SRGProgramComposition? + let date: Date + + private var imageUrl: URL? { + let width = SizeForImageScale(.small).width + if let channel = channel { + return program(at: date)?.imageURL(for: .width, withValue: width, type: .default) ?? channel.imageURL(for: .width, withValue: width, type: .default) + } + else { + return media?.imageURL(for: .width, withValue: width, type: .default) + } + } + + private var logoImage: UIImage? { + if let channel = channel { + return channel.play_logo60Image + } + else { + return nil + } + } + + var body: some View { + ZStack { + ImageView(url: imageUrl) + if let logoImage = logoImage { + Rectangle() + .fill(Color(white: 0, opacity: 0.6)) + Image(uiImage: logoImage) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding() + } + else if let media = media, media.timeAvailability(at: Date()) == .notYetAvailable { + Rectangle() + .fill(Color(white: 0, opacity: 0.6)) + Badge(text: NSLocalizedString("Soon", comment: "Short label identifying content which will be available soon."), color: Color(.play_gray)) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding([.leading, .top], 8) + } + BlockingOverlay(media: media) + } + } + } + + private struct DescriptionView: View, LiveMediaData { + let media: SRGMedia? + let programComposition: SRGProgramComposition? + let date: Date + + private var title: String { + if let channel = channel { + return program(at: date)?.title ?? channel.title + } + else { + return MediaDescription.title(for: media) + } + } + + private var subtitle: String? { + if let media = media, media.contentType == .scheduledLivestream { + return MediaDescription.subtitle(for: media) + } + else { + guard let currentProgram = program(at: date) else { return nil } + let remainingTimeInterval = currentProgram.endDate.timeIntervalSince(date) + let remainingTime = PlayRemainingTimeFormattedDuration(remainingTimeInterval) + return String(format: NSLocalizedString("%@ remaining", comment: "Text displayed on live cells telling how much time remains for a program currently on air"), remainingTime) + } + } + + private var progress: Double? { + if channel != nil { + guard let currentProgram = program(at: date) else { return 1 } + return date.timeIntervalSince(currentProgram.startDate) / currentProgram.endDate.timeIntervalSince(currentProgram.startDate) + } + else if let media = media, media.contentType == .scheduledLivestream, media.timeAvailability(at: Date()) == .available, + let startDate = media.startDate, + let endDate = media.endDate { + let progress = Date().timeIntervalSince(startDate) / endDate.timeIntervalSince(startDate) + return progress.clamped(to: 0...1) + } + else { + return nil + } + } + + var body: some View { + VStack(alignment: .leading) { + if let progress = progress { + ProgressBar(value: progress) + .frame(maxWidth: .infinity, maxHeight: 8) + .cornerRadius(4) + } + Text(title) + .srgFont(.subtitle) + .lineLimit(2) + + if let subtitle = subtitle { + Text(subtitle) + .srgFont(.overline) + .lineLimit(2) + } + } + } + } +} diff --git a/TV Application/Sources/LiveView.swift b/TV Application/Sources/LiveView.swift index 834a73aa0..e5645077d 100644 --- a/TV Application/Sources/LiveView.swift +++ b/TV Application/Sources/LiveView.swift @@ -17,7 +17,7 @@ struct LiveView: View { .onDisappear { model.cancelRefresh() } - .onResume { + .onWake { model.refresh() } } diff --git a/TV Application/Sources/Logger.swift b/TV Application/Sources/Logger.swift new file mode 100644 index 000000000..c589a8d05 --- /dev/null +++ b/TV Application/Sources/Logger.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGLoggerSwift + +func PlayLogVerbose(category: String?, message: String, file: String = #file, function: String = #function, line: UInt = #line) { + SRGLogVerbose(subsystem: "ch.srgssr.play", category: category, message: message, file: file, function: function, line: line); +} + +func PlayLogDebug(category: String?, message: String, file: String = #file, function: String = #function, line: UInt = #line) { + SRGLogDebug(subsystem: "ch.srgssr.play", category: category, message: message, file: file, function: function, line: line); +} + +func PlayLogInfo(category: String?, message: String, file: String = #file, function: String = #function, line: UInt = #line) { + SRGLogInfo(subsystem: "ch.srgssr.play", category: category, message: message, file: file, function: function, line: line); +} + +func PlayLogWarning(category: String?, message: String, file: String = #file, function: String = #function, line: UInt = #line) { + SRGLogWarning(subsystem: "ch.srgssr.play", category: category, message: message, file: file, function: function, line: line); +} + +func PlayLogError(category: String?, message: String, file: String = #file, function: String = #function, line: UInt = #line) { + SRGLogError(subsystem: "ch.srgssr.play", category: category, message: message, file: file, function: function, line: line); +} diff --git a/TV Application/Sources/MediaCell.swift b/TV Application/Sources/MediaCell.swift index d1104ca0c..22726d53a 100644 --- a/TV Application/Sources/MediaCell.swift +++ b/TV Application/Sources/MediaCell.swift @@ -5,27 +5,23 @@ // import SRGAppearance -import SRGLetterbox import SwiftUI struct MediaCell: View { - private struct DescriptionView: View { - let media: SRGMedia? - - var body: some View { - Text(MediaDescription.title(for: media)) - .srgFont(.medium, size: .subtitle) - .lineLimit(2) - Text(MediaDescription.subtitle(for: media)) - .srgFont(.light, size: .subtitle) - .lineLimit(2) - } - } - let media: SRGMedia? + let style: MediaDescription.Style + let action: (() -> Void)? + + fileprivate var onFocusAction: ((Bool) -> Void)? = nil - @State var isFocused: Bool = false + @State private var isFocused: Bool = false + init(media: SRGMedia?, style: MediaDescription.Style = .date, action: (() -> Void)? = nil) { + self.media = media + self.style = style + self.action = action + } + private var redactionReason: RedactionReasons { return media == nil ? .placeholder : .init() } @@ -33,30 +29,90 @@ struct MediaCell: View { var body: some View { GeometryReader { geometry in VStack { - Button(action: { - // TODO: Could / should be presented with SwiftUI, but presentation flag must be part of topmost state - if let media = media, - let rootViewController = UIApplication.shared.windows.first?.rootViewController { - let letterboxViewController = SRGLetterboxViewController() - letterboxViewController.controller.playMedia(media, at: nil, withPreferredSettings: nil) - rootViewController.present(letterboxViewController, animated: true, completion: nil) + Button(action: action ?? { + if let media = media { + navigateToMedia(media) } }) { - MediaVisual(media: media, scale: .small, contentMode: .fit) - .frame(width: geometry.size.width, height: geometry.size.width * 9 / 16) - .reportFocusChanges() + ZStack { + MediaVisualView(media: media, scale: .small, contentMode: .fit) + .frame(width: geometry.size.width, height: geometry.size.width * 9 / 16) + .onFocusChange { focused in + isFocused = focused + + if let onFocusAction = self.onFocusAction { + onFocusAction(focused) + } + } + .accessibilityElement() + .accessibilityLabel(MediaDescription.accessibilityLabel(for: media)) + .accessibility(addTraits: .isButton) + + if let media = media { + AvailabilityBadge(media: media) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .accessibility(hidden: true) + } + } } .buttonStyle(CardButtonStyle()) - DescriptionView(media: media) + DescriptionView(media: media, style: style) .frame(width: geometry.size.width, alignment: .leading) + .animation(nil) .opacity(isFocused ? 1 : 0.5) .offset(x: 0, y: isFocused ? 10 : 0) .scaleEffect(isFocused ? 1.1 : 1, anchor: .top) .animation(.easeInOut(duration: 0.2)) } .redacted(reason: redactionReason) - .onFocusChange { isFocused = $0 } + } + } + + private struct DescriptionView: View { + let media: SRGMedia? + let style: MediaDescription.Style + + var body: some View { + VStack(alignment: .leading) { + Text(MediaDescription.title(for: media, style: style)) + .srgFont(.subtitle) + .lineLimit(2) + Text(MediaDescription.subtitle(for: media, style: style)) + .srgFont(style == .date ? .overline : .headline2) + .lineLimit(2) + .layoutPriority(1) + } + } + } +} + +extension MediaCell { + func onFocus(perform action: @escaping (Bool) -> Void) -> MediaCell { + var mediaCell = self + mediaCell.onFocusAction = action + return mediaCell + } +} + +struct MediaCell_Previews: PreviewProvider { + + static var mediaPreview: SRGMedia { + let asset = NSDataAsset(name: "media-rts-tv")! + let jsonData = try! JSONSerialization.jsonObject(with: asset.data, options: []) as? [String: Any] + + return try! MTLJSONAdapter(modelClass: SRGMedia.self)?.model(fromJSONDictionary: jsonData) as! SRGMedia + } + + static var previews: some View { + Group { + MediaCell(media: mediaPreview) + .previewLayout(.fixed(width: 375, height: 400)) + .previewDisplayName("RTS media, default date style") + + MediaCell(media: mediaPreview, style: .show) + .previewLayout(.fixed(width: 375, height: 400)) + .previewDisplayName("RTS media, show style") } } } diff --git a/TV Application/Sources/MediaDescription.swift b/TV Application/Sources/MediaDescription.swift index 99f6ea78c..c04dbf73b 100644 --- a/TV Application/Sources/MediaDescription.swift +++ b/TV Application/Sources/MediaDescription.swift @@ -7,23 +7,55 @@ import Foundation struct MediaDescription { + enum Style { + /// Show information emphasis + case show + /// Date information emphasis + case date + } + private static func placeholder(length: Int) -> String { return String(repeating: " ", count: length) } - static func title(for media: SRGMedia?) -> String { - guard let media = media else { return placeholder(length: 20) } - return media.show?.title ?? media.title + private static func formattedDuration(from: Date, to: Date) -> String? { + guard let days = Calendar.current.dateComponents([.day], from: from, to: to).day else { return nil } + switch days { + case 0: + // Minimum displayed is 1 hour + return PlayFormattedHours(max(to.timeIntervalSince(from), 60 * 60)) + case 1...30: + return PlayFormattedDays(to.timeIntervalSince(from)) + default: + return nil + } } - static func subtitle(for media: SRGMedia?) -> String { - guard let media = media else { return placeholder(length: 25) } - guard media.contentType != .livestream else { return "" } - if let showTitle = media.show?.title, !media.title.contains(showTitle) { + static func title(for media: SRGMedia?, style: Style = .date) -> String { + guard let media = media else { return placeholder(length: 15) } + + switch style { + case .show: + return media.show?.title ?? media.title + case .date: return media.title } - else { - return DateFormatters.formattedRelativeDateAndTime(for: media.date) + } + + static func subtitle(for media: SRGMedia?, style: Style = .date) -> String { + guard let media = media else { return placeholder(length: 20) } + guard media.contentType != .livestream else { return "" } + + switch style { + case .show: + if let showTitle = media.show?.title, media.title.lowercased() != showTitle.lowercased() { + return media.title + } + else { + return DateFormatter.play_relativeDateAndTime.string(from: media.date).capitalizedFirstLetter + } + case .date: + return DateFormatter.play_relativeDateAndTime.string(from: media.date).capitalizedFirstLetter } } @@ -31,4 +63,31 @@ struct MediaDescription { guard let media = media else { return placeholder(length: 160) } return media.summary } + + static func availability(for media: SRGMedia?) -> String? { + guard let media = media else { return placeholder(length: 25) } + let now = Date() + switch media.timeAvailability(at: now) { + case .notAvailableAnymore: + let endDate = (media.endDate != nil) ? media.endDate! : media.date.addingTimeInterval(media.duration / 1000) + guard let expiringDays = Self.formattedDuration(from: now, to: endDate) else { return nil } + return String(format: NSLocalizedString("Not available since %@", comment:"Explains that a content has expired (days or hours ago). Displayed in the media player view."), expiringDays) + case .available: + guard let endDate = media.endDate, media.contentType != .livestream, media.contentType != .scheduledLivestream, media.contentType != .trailer else { return nil } + guard let remainingDays = Self.formattedDuration(from: now, to: endDate) else { return nil } + return String(format: NSLocalizedString("Still available for %@", comment:"Explains that a content is still online (for days or hours) but will expire. Displayed in the media player view."), remainingDays) + default: + return nil + } + } + + static func accessibilityLabel(for media: SRGMedia?) -> String { + guard let media = media else { return "" } + if let showTitle = media.show?.title, !media.title.lowercased().contains(showTitle.lowercased()) { + return showTitle.appending(", \(media.title)") + } + else { + return media.title + } + } } diff --git a/TV Application/Sources/MediaDetailModel.swift b/TV Application/Sources/MediaDetailModel.swift new file mode 100644 index 000000000..ce6d6f8e3 --- /dev/null +++ b/TV Application/Sources/MediaDetailModel.swift @@ -0,0 +1,54 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderCombine + +class MediaDetailModel: ObservableObject { + struct Recommendation: Codable { + let recommendationId: String + let urns: [String] + } + + private let initialMedia: SRGMedia + + @Published private(set) var relatedMedias: [SRGMedia] = [] + @Published var selectedMedia: SRGMedia? = nil + + var cancellables = Set() + + init(media: SRGMedia) { + self.initialMedia = media + } + + var media: SRGMedia { + return selectedMedia ?? initialMedia + } + + func refresh() { + if initialMedia.contentType == .livestream { return } + + let middlewareUrl = ApplicationConfiguration.shared.middlewareURL + + let resourcePath = "api/v2/playlist/recommendation/relatedContent/" + initialMedia.urn + let url = URL(string: resourcePath, relativeTo: middlewareUrl)! + + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: Recommendation.self, decoder: JSONDecoder()) + .flatMap { recommendation in + return SRGDataProvider.current!.medias(withUrns: recommendation.urns) + } + .map { $0.medias } + .replaceError(with: []) + .receive(on: DispatchQueue.main) + .assign(to: \.relatedMedias, on: self) + .store(in: &cancellables) + } + + func cancelRefresh() { + cancellables = [] + } +} diff --git a/TV Application/Sources/MediaDetailView.swift b/TV Application/Sources/MediaDetailView.swift new file mode 100644 index 000000000..f63f9837f --- /dev/null +++ b/TV Application/Sources/MediaDetailView.swift @@ -0,0 +1,278 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGAnalyticsSwiftUI +import SRGAppearance +import SRGLetterbox +import SwiftUI + +struct MediaDetailView: View { + @ObservedObject var model: MediaDetailModel + + init(media: SRGMedia) { + model = MediaDetailModel(media: media) + } + + private var imageUrl: URL? { + return model.media.imageURL(for: .width, withValue: SizeForImageScale(.large).width, type: .default) + } + + var body: some View { + ZStack { + ImageView(url: imageUrl) + Rectangle() + .fill(Color(white: 0, opacity: 0.6)) + VStack { + DescriptionView(model: model) + .padding([.top, .leading, .trailing], 100) + .padding(.bottom, 30) + RelatedMediasView(model: model) + .frame(maxWidth: .infinity, maxHeight: 350) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.play_black)) + .edgesIgnoringSafeArea(.all) + .onAppear { + model.refresh() + } + .onDisappear { + model.cancelRefresh() + } + .onWake { + model.refresh() + } + .tracked(withTitle: analyticsPageTitle, levels: analyticsPageLevels) + } + + private struct DescriptionView: View { + @ObservedObject var model: MediaDetailModel + + @Namespace private var namespace + + var body: some View { + FocusableRegion { + GeometryReader { geometry in + VStack(alignment: .leading, spacing: 0) { + Text(model.media.title) + .srgFont(.title1) + .lineLimit(3) + .foregroundColor(.white) + if let showTitle = model.media.show?.title, showTitle.lowercased() != model.media.title.lowercased() { + Text(showTitle) + .srgFont(.headline1) + .foregroundColor(.white) + } + Spacer() + .frame(height: 20) + VStack(alignment: .leading, spacing: 0) { + AttributesView(model: model) + Spacer() + .frame(height: 20) + SummaryView(model: model) + Spacer() + ActionsView(model: model) + .layoutPriority(1) + .prefersDefaultFocus(in: namespace) + } + .frame(maxWidth: geometry.size.width / 2, maxHeight: .infinity, alignment: .leading) + } + .focusScope(namespace) + } + } + } + } + + struct AttributeView: View { + let icon: String + let values: [String] + + var body: some View { + HStack(spacing: 10) { + Image(icon) + Text(values.joined(separator: " - ")) + .srgFont(.overline) + .foregroundColor(.white) + } + } + } + + struct AttributesView: View { + @ObservedObject var model: MediaDetailModel + + var body: some View { + HStack(spacing: 30) { + HStack(spacing: 4) { + if let youthProtectionLogoImage = YouthProtectionImageForColor(model.media.youthProtectionColor) { + Image(uiImage: youthProtectionLogoImage) + } + DurationLabel(media: model.media) + } + + if let isWebFirst = model.media.play_isWebFirst, isWebFirst { + Badge(text: NSLocalizedString("Web first", comment: "Web first label on media detail page"), color: Color(.srg_blue)) + } + if let subtitleLanguages = model.media.play_subtitleLanguages, !subtitleLanguages.isEmpty { + AttributeView(icon: "subtitles_off-22", values: subtitleLanguages) + } + if let audioLanguages = model.media.play_audioLanguages, !audioLanguages.isEmpty { + AttributeView(icon: "audios-22", values: audioLanguages) + } + } + } + } + + struct SummaryView: View { + private struct TextButtonStyle: ButtonStyle { + let focused: Bool + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .background(focused ? Color(UIColor.init(white: 1, alpha: 0.3)) : Color.clear) + .scaleEffect(focused && !configuration.isPressed ? 1.02 : 1) + } + } + + @ObservedObject var model: MediaDetailModel + @State var isFocused: Bool = false + + var availabilityInformation: String { + var publication = DateFormatter.play_dateAndTime.string(from: model.media.date) + if let availability = MediaDescription.availability(for: model.media) { + publication += " - " + availability + } + return publication + } + + var body: some View { + GeometryReader { geometry in + VStack(alignment: .leading, spacing: 0) { + if let summary = model.media.play_fullSummary { + Button(action: { + showText(summary) + }, label: { + Text(summary) + .foregroundColor(.white) + .srgFont(.body) + .frame(width: geometry.size.width, alignment: .leading) + .padding([.top, .bottom], 5) + .onFocusChange { isFocused = $0 } + }) + .buttonStyle(TextButtonStyle(focused: isFocused)) + } + + Text(availabilityInformation) + .srgFont(.overline) + .foregroundColor(.white) + .padding([.top, .bottom], 5) + } + } + } + } + + struct ActionsView: View { + @ObservedObject var model: MediaDetailModel + + var playButtonLabel: String { + let progress = HistoryPlaybackProgressForMediaMetadata(model.media) + if progress == 0 || progress == 1 { + return model.media.mediaType == .audio ? NSLocalizedString("Listen", comment: "Play button label for audio in media detail view") : NSLocalizedString("Watch", comment: "Play button label for video in media detail view") + } + else { + return NSLocalizedString("Resume", comment: "Resume playback button label") + } + } + + var body: some View { + HStack(alignment: .top, spacing: 30) { + // TODO: 22 icon? + LabeledButton(icon: "play-50", label: playButtonLabel) { + navigateToMedia(model.media, play: true) + } + #if DEBUG + LabeledButton(icon: "watch_later-22", label: NSLocalizedString("Later", comment: "Watch or listen later button label in media detail view")) { + /* Toggle Watch Later state */ + } + #endif + if let show = model.media.show { + LabeledButton(icon: "episodes-22", label: NSLocalizedString("More episodes", comment:"Button to access more episodes from the media detail view")) { + navigateToShow(show) + } + } + } + } + } + + private struct RelatedMediasView: View { + @ObservedObject var model: MediaDetailModel + + var body: some View { + ZStack { + if !model.relatedMedias.isEmpty { + ZStack { + Rectangle() + .fill(Color(.srg_color(fromHexadecimalString: "#222222")!)) + .opacity(0.8) + ZStack { + Text(NSLocalizedString("This might interest you", comment: "Related content media list title")) + .srgFont(.body) + .foregroundColor(.gray) + .padding([.leading, .trailing], 40) + .padding(.top, 15) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + ScrollView(.horizontal) { + HStack(spacing: 40) { + ForEach(model.relatedMedias, id: \.uid) { media in + MediaCell(media: media, style: .show) { + navigateToMedia(media, play: true) + } + .onFocus { isFocused in + if isFocused { + model.selectedMedia = media + } + } + .frame(width: 280) + } + } + .padding(.top, 70) + .padding([.leading, .trailing], 40) + } + } + } + } + else { + Rectangle() + .fill(Color.clear) + } + } + } + } +} + +extension MediaDetailView { + private var analyticsPageTitle: String { + return AnalyticsPageTitle.media.rawValue + } + + private var analyticsPageLevels: [String]? { + return [AnalyticsPageLevel.play.rawValue] + } +} + +struct MediaDetailView_Previews: PreviewProvider { + + static var mediaPreview: SRGMedia { + let asset = NSDataAsset(name: "media-rts-tv")! + let jsonData = try! JSONSerialization.jsonObject(with: asset.data, options: []) as? [String: Any] + + return try! MTLJSONAdapter(modelClass: SRGMedia.self)?.model(fromJSONDictionary: jsonData) as! SRGMedia + } + + static var previews: some View { + MediaDetailView(media: mediaPreview) + .previewDisplayName("RTS media") + } +} diff --git a/TV Application/Sources/MediaVisual.swift b/TV Application/Sources/MediaVisual.swift deleted file mode 100644 index 42343fb0e..000000000 --- a/TV Application/Sources/MediaVisual.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -import SwiftUI - -struct MediaVisual: View { - private struct DurationLabel: View { - let media: SRGMedia? - - private var duration: String? { - guard let media = media else { return nil } - let isLivestreamOrScheduledLivestream = (media.contentType == SRGContentType.livestream || media.contentType == SRGContentType.scheduledLivestream) - if isLivestreamOrScheduledLivestream { - return NSLocalizedString("Live", comment: "Short label identifying a livestream. Display in uppercase.") - } - else { - return DurationFormatters.minutes(for: media.duration / 1000) - } - } - - var body: some View { - if let duration = duration { - Text(duration) - .srgFont(.medium, size: .caption) - .foregroundColor(.white) - .padding([.top, .bottom], 5) - .padding([.leading, .trailing], 8) - .background(Color.init(white: 0, opacity: 0.5)) - .cornerRadius(4) - } - } - } - - private struct BlockingOverlay: View { - let media: SRGMedia? - - private var blockingIconImage: UIImage? { - guard let blockingReason = media?.blockingReason(at: Date()) else { return nil } - return UIImage.play_image(for: blockingReason) - } - - var body: some View { - if let blockingIconImage = blockingIconImage { - ZStack { - Rectangle() - .fill(Color(white: 0, opacity: 0.6)) - Image(uiImage: blockingIconImage) - .foregroundColor(.white) - } - } - } - } - - private struct Badge: View { - let text: String - let color: Color - - var body: some View { - Text(text) - .srgFont(.medium, size: .caption) - .foregroundColor(.white) - .padding([.top, .bottom], 5) - .padding([.leading, .trailing], 8) - .background(color) - .cornerRadius(4) - } - } - - let media: SRGMedia? - let scale: ImageScale - let contentMode: ContentMode - - init(media: SRGMedia?, scale: ImageScale, contentMode: ContentMode = .fit) { - self.media = media - self.scale = scale - self.contentMode = contentMode - } - - private var imageUrl: URL? { - return media?.imageURL(for: .width, withValue: SizeForImageScale(scale).width, type: .default) - } - - private var youthProtectionLogoImage: UIImage? { - guard let youthProtectionColor = media?.youthProtectionColor else { return nil } - return YouthProtectionImageForColor(youthProtectionColor) - } - - static func formattedDuration(from: Date, to: Date) -> String? { - guard let days = Calendar.current.dateComponents([.day], from: from, to: to).day else { return nil } - switch days { - case 0: - return DurationFormatters.hours(for: to.timeIntervalSince(from)) - case 1: - return DurationFormatters.days(for: to.timeIntervalSince(from)) - default: - return nil - } - } - - private func availabilityBadgeProperties() -> (text: String, color: Color)? { - guard let media = media else { return nil } - - let now = Date() - let availability = media.timeAvailability(at: now) - switch availability { - case .notYetAvailable: - return (NSLocalizedString("Soon", comment: "Short label identifying content which will be available soon."), Color(.play_orange)) - case .notAvailableAnymore: - return (NSLocalizedString("Expired", comment: "Short label identifying content which has expired."), Color(.play_gray)) - case .available: - guard let endDate = media.endDate, media.contentType != .livestream, media.contentType != .scheduledLivestream else { return nil } - if let remainingDays = Self.formattedDuration(from: now, to: endDate) { - return (NSLocalizedString("\(remainingDays) left", comment: "Short label displayed on medias expiring soon"), Color(.play_orange)) - } - else { - return nil - } - default: - return nil - } - } - - var body: some View { - ZStack { - ImageView(url: imageUrl, contentMode: contentMode) - .whenRedacted { $0.hidden() } - - BlockingOverlay(media: media) - - HStack(spacing: 4) { - if media?.presentation == .presentation360 { - Image("360_media-25") - .foregroundColor(.white) - } - Spacer() - if let youthProtectionLogoImage = youthProtectionLogoImage { - Image(uiImage: youthProtectionLogoImage) - } - DurationLabel(media: media) - } - .padding([.leading, .trailing, .bottom], 8) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) - - Group { - if let isWebFirst = media?.play_isWebFirst, isWebFirst { - Badge(text: NSLocalizedString("Web first", comment: "Web first label on media cells"), color: Color(.srg_blue)) - } - else if let availabilityBadgeProperties = availabilityBadgeProperties() { - Badge(text: availabilityBadgeProperties.text, color: availabilityBadgeProperties.color) - } - } - .padding([.leading, .top], 8) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - } - } -} diff --git a/TV Application/Sources/MediaVisualView.swift b/TV Application/Sources/MediaVisualView.swift new file mode 100644 index 000000000..c1f77c530 --- /dev/null +++ b/TV Application/Sources/MediaVisualView.swift @@ -0,0 +1,75 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGUserData +import SwiftUI + +struct MediaVisualView: View { + let media: SRGMedia? + let scale: ImageScale + let contentMode: ContentMode + + @State private var progress: Double = 0 + @State private var taskHandle: String? = nil + + init(media: SRGMedia?, scale: ImageScale, contentMode: ContentMode = .fit) { + self.media = media + self.scale = scale + self.contentMode = contentMode + } + + private var imageUrl: URL? { + return media?.imageURL(for: .width, withValue: SizeForImageScale(scale).width, type: .default) + } + + private var youthProtectionLogoImage: UIImage? { + guard let youthProtectionColor = media?.youthProtectionColor else { return nil } + return YouthProtectionImageForColor(youthProtectionColor) + } + + private func updateProgress() { + HistoryPlaybackProgressAsyncCancel(taskHandle) + taskHandle = HistoryPlaybackProgressForMediaMetadataAsync(media, { progress = Double($0) }) + } + + var body: some View { + ZStack { + ImageView(url: imageUrl, contentMode: contentMode) + BlockingOverlay(media: media) + + HStack(spacing: 4) { + if media?.presentation == .presentation360 { + Image("360_media-25") + .foregroundColor(.white) + } + Spacer() + if let youthProtectionLogoImage = youthProtectionLogoImage { + Image(uiImage: youthProtectionLogoImage) + } + DurationLabel(media: media) + } + .padding([.leading, .trailing, .bottom], 8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) + + if let progress = progress { + ProgressBar(value: progress) + .opacity(progress != 0 ? 1 : 0) + .frame(height: 8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) + } + } + .onAppear { + updateProgress() + } + .onReceive(NotificationCenter.default.publisher(for: Notification.Name.SRGHistoryEntriesDidChange)) { notification in + if let updatedUrns = notification.userInfo?[SRGHistoryEntriesUidsKey] as? Set, + let media = media, + updatedUrns.contains(media.urn) { + updateProgress() + } + } + } +} diff --git a/TV Application/Sources/Modifiers.swift b/TV Application/Sources/Modifiers.swift index 2680bf693..680329f9b 100644 --- a/TV Application/Sources/Modifiers.swift +++ b/TV Application/Sources/Modifiers.swift @@ -8,7 +8,10 @@ import FXReachability import SwiftUI extension View { - func onResume(perform action: @escaping () -> Void) -> some View { + /** + * Called when the application is woken up, either by the user or network being reachable again. + */ + func onWake(perform action: @escaping () -> Void) -> some View { onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in action() } diff --git a/TV Application/Sources/Navigation.swift b/TV Application/Sources/Navigation.swift new file mode 100644 index 000000000..1dd56b491 --- /dev/null +++ b/TV Application/Sources/Navigation.swift @@ -0,0 +1,69 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGAnalytics +import TvOSTextViewer +import SwiftUI + +func navigateToMedia(_ media: SRGMedia, play: Bool = false, animated: Bool = true) { + guard let topViewController = UIApplication.shared.keyWindow?.topViewController else { return } + + if !play && media.contentType != .livestream { + let hostController = UIHostingController(rootView: MediaDetailView(media: media)) + topViewController.present(hostController, animated: animated, completion: nil) + } + else { + let letterboxViewController = SRGLetterboxViewController() + + let controller = letterboxViewController.controller + applyLetterboxControllerSettings(to: controller) + controller.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: Int32(NSEC_PER_SEC)), queue: nil) { _ in + HistoryUpdateLetterboxPlaybackProgress(controller) + } + + let position = HistoryResumePlaybackPositionForMedia(media) + controller.playMedia(media, at: position, withPreferredSettings: nil) + topViewController.present(letterboxViewController, animated: animated, completion: nil) + SRGAnalyticsTracker.shared.trackPageView(withTitle: AnalyticsPageTitle.player.rawValue, levels: [AnalyticsPageLevel.play.rawValue]) + } +} + +func navigateToShow(_ show: SRGShow, animated: Bool = true) { + guard let topViewController = UIApplication.shared.keyWindow?.topViewController else { return } + + let hostController = UIHostingController(rootView: ShowDetailView(show: show)) + topViewController.present(hostController, animated: animated, completion: nil) +} + +func navigateToTopic(_ topic: SRGTopic, animated: Bool = true) { + guard let topViewController = UIApplication.shared.keyWindow?.topViewController else { return } + + let hostController = UIHostingController(rootView: TopicDetailView(topic: topic)) + topViewController.present(hostController, animated: animated, completion: nil) +} + +func showText(_ text: String, animated: Bool = true) { + guard let topViewController = UIApplication.shared.keyWindow?.topViewController else { return } + + let textViewController = TvOSTextViewerViewController() + textViewController.text = text + textViewController.textAttributes = [ + .foregroundColor: UIColor.white, + .font: SRGFont.uiFont(.body) + ] + textViewController.textEdgeInsets = UIEdgeInsets(top: 100, left: 250, bottom: 100, right: 250) + textViewController.modalPresentationStyle = .overFullScreen + topViewController.present(textViewController, animated: animated) +} + +fileprivate func applyLetterboxControllerSettings(to controller: SRGLetterboxController) { + controller.serviceURL = SRGDataProvider.current?.serviceURL + controller.globalParameters = SRGDataProvider.current?.globalParameters + + let applicationConfiguration = ApplicationConfiguration.shared + controller.endTolerance = applicationConfiguration.endTolerance; + controller.endToleranceRatio = applicationConfiguration.endToleranceRatio; +} diff --git a/TV Application/Sources/Play-TV-ObjectiveC-Bridge.h b/TV Application/Sources/Play-TV-ObjectiveC-Bridge.h index 2435e25fa..75569a6e3 100755 --- a/TV Application/Sources/Play-TV-ObjectiveC-Bridge.h +++ b/TV Application/Sources/Play-TV-ObjectiveC-Bridge.h @@ -7,8 +7,17 @@ // This file lists all Objective-C headers which must be exposed to Swift code. Update the list as needed. All referenced // Objective-C headers will be automatically exposed to all Swift source files. +#import "AnalyticsConstants.h" #import "ApplicationConfiguration.h" +#import "ChannelService.h" +#import "History.h" +#import "NSBundle+PlaySRG.h" +#import "NSDateFormatter+PlaySRG.h" +#import "PlayDurationFormatter.h" +#import "SRGChannel+PlaySRG.h" #import "SRGMedia+PlaySRG.h" +#import "SRGProgramComposition+PlaySRG.h" #import "UIColor+PlaySRG.h" #import "UIImage+PlaySRG.h" +#import "UIImageView+PlaySRG.h" #import "UIView+PlaySRG.h" diff --git a/TV Application/Sources/ProfileView.swift b/TV Application/Sources/ProfileView.swift index 006310160..894393bf3 100644 --- a/TV Application/Sources/ProfileView.swift +++ b/TV Application/Sources/ProfileView.swift @@ -10,8 +10,9 @@ struct ProfileView: View { private static let version: String = { let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String let bundleNameSuffix = Bundle.main.infoDictionary!["BundleNameSuffix"] as! String + let buildName = Bundle.main.infoDictionary!["BuildName"] as! String let buildString = Bundle.main.infoDictionary!["CFBundleVersion"] as! String - return String(format: "%@%@ (%@)", appVersion, bundleNameSuffix, buildString) + return String(format: "%@%@%@ (%@)", appVersion, bundleNameSuffix, buildName, buildString) }() var body: some View { diff --git a/TV Application/Sources/ProgressBar.swift b/TV Application/Sources/ProgressBar.swift new file mode 100644 index 000000000..46fbbb0b0 --- /dev/null +++ b/TV Application/Sources/ProgressBar.swift @@ -0,0 +1,46 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SwiftUI + +struct ProgressBar: View { + let value: Double + + var body: some View { + GeometryReader { geometry in + ZStack(alignment: .leading) { + Rectangle() + .fill(Color(white: 1, opacity: 0.3)) + .frame(width: geometry.size.width, height: geometry.size.height) + Rectangle() + .fill(Color(.play_progressRed)) + .frame(width: geometry.size.width * CGFloat(value), height: geometry.size.height) + } + } + } + + init(value: Double) { + self.value = value.clamped(to: 0...1) + } +} + +struct ProgressBar_Previews: PreviewProvider { + static var previews: some View { + Group { + ProgressBar(value: 0) + .previewLayout(.fixed(width: 400, height: 2)) + .previewDisplayName("0%") + + ProgressBar(value: 0.6) + .previewLayout(.fixed(width: 400, height: 2)) + .previewDisplayName("60%") + + ProgressBar(value: 1) + .previewLayout(.fixed(width: 400, height: 2)) + .previewDisplayName("100%") + } + } +} diff --git a/TV Application/Sources/RadioChannel.swift b/TV Application/Sources/RadioChannel.swift index 048e9a001..b6b1d4c3a 100644 --- a/TV Application/Sources/RadioChannel.swift +++ b/TV Application/Sources/RadioChannel.swift @@ -5,7 +5,7 @@ // extension RadioChannel { - private func homeRowId(from homeSection: HomeSection, withChannelUid channelUid: String) -> HomeRowId? { + private func homeRowId(from homeSection: HomeSection, withChannelUid channelUid: String) -> HomeModel.RowId? { switch homeSection { case .radioLatestEpisodes: return .radioLatestEpisodes(channelUid: channelUid) @@ -22,8 +22,8 @@ extension RadioChannel { } } - func homeRowIds() -> [HomeRowId] { - var rowIds = [HomeRowId]() + func homeRowIds() -> [HomeModel.RowId] { + var rowIds = [HomeModel.RowId]() for homeSection in homeSections { if let homeSection = HomeSection(rawValue: homeSection.intValue), let rowId = homeRowId(from: homeSection, withChannelUid: uid) { diff --git a/TV Application/Sources/ShowCell.swift b/TV Application/Sources/ShowCell.swift index 3862b68ba..bb479fe94 100644 --- a/TV Application/Sources/ShowCell.swift +++ b/TV Application/Sources/ShowCell.swift @@ -7,20 +7,17 @@ import SwiftUI struct ShowCell: View { - private struct VisualView: View { - let show: SRGShow? - - private var imageUrl: URL? { - return show?.imageURL(for: .width, withValue: SizeForImageScale(.small).width, type: .default) - } - - var body: some View { - ImageView(url: imageUrl) - .whenRedacted { $0.hidden() } - } - } - let show: SRGShow? + let action: (() -> Void)? + + fileprivate var onFocusAction: ((Bool) -> Void)? = nil + + @State private var isFocused: Bool = false + + init(show: SRGShow?, action: (() -> Void)? = nil) { + self.show = show + self.action = action + } private var title: String { guard let show = show else { return String(repeating: " ", count: .random(in: 10..<20)) } @@ -34,17 +31,73 @@ struct ShowCell: View { var body: some View { GeometryReader { geometry in VStack { - Button(action: {}) { + Button(action: action ?? { + if let show = show { + navigateToShow(show) + } + }) { VisualView(show: show) .frame(width: geometry.size.width, height: geometry.size.width * 9 / 16) + .onFocusChange { focused in + isFocused = focused + + if let onFocusAction = self.onFocusAction { + onFocusAction(focused) + } + } + .accessibilityElement() + .accessibilityLabel(show?.title ?? "") + .accessibility(addTraits: .isButton) } .buttonStyle(CardButtonStyle()) Text(title) - .srgFont(.medium, size: .subtitle) + .srgFont(.subtitle) .frame(width: geometry.size.width, alignment: .leading) + .opacity(isFocused ? 1 : 0.5) + .offset(x: 0, y: isFocused ? 10 : 0) + .scaleEffect(isFocused ? 1.1 : 1, anchor: .top) + .animation(.easeInOut(duration: 0.2)) } .redacted(reason: redactionReason) } } + + private struct VisualView: View { + let show: SRGShow? + + private var imageUrl: URL? { + return show?.imageURL(for: .width, withValue: SizeForImageScale(.small).width, type: .default) + } + + var body: some View { + ImageView(url: imageUrl) + } + } +} + +extension ShowCell { + func onFocus(perform action: @escaping (Bool) -> Void) -> ShowCell { + var showCell = self + showCell.onFocusAction = action + return showCell + } +} + +struct ShowCell_Previews: PreviewProvider { + + static var showPreview: SRGShow { + let asset = NSDataAsset(name: "show-srf-tv")! + let jsonData = try! JSONSerialization.jsonObject(with: asset.data, options: []) as? [String: Any] + + return try! MTLJSONAdapter(modelClass: SRGShow.self)?.model(fromJSONDictionary: jsonData) as! SRGShow + } + + static var previews: some View { + Group { + ShowCell(show: showPreview) + .previewLayout(.fixed(width: 375, height: 211)) + .previewDisplayName("SRF show") + } + } } diff --git a/TV Application/Sources/ShowDetailModel.swift b/TV Application/Sources/ShowDetailModel.swift new file mode 100644 index 000000000..47b44a619 --- /dev/null +++ b/TV Application/Sources/ShowDetailModel.swift @@ -0,0 +1,70 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderCombine + +class ShowDetailModel: ObservableObject { + let show: SRGShow + + enum State { + case loading + case failed(error: Error) + case loaded(medias: [SRGMedia]) + } + + @Published private(set) var state = State.loaded(medias: []) + + private var cancellables = Set() + private var medias: [SRGMedia] = [] + private var nextPage: SRGDataProvider.LatestMediasForShow.Page? = nil + + init(show: SRGShow) { + self.show = show + } + + func refresh() { + guard medias.isEmpty else { return } + loadNextPage() + } + + // Triggers a load only if the media is `nil` (first page) or the last one. We cannot observe scrolling yet, + // so cell appearance must trigger a reload, and the last cell is used to load more content. + // Also read https://www.donnywals.com/implementing-an-infinite-scrolling-list-with-swiftui-and-combine/ + func loadNextPage(from media: SRGMedia? = nil) { + guard let publisher = publisher(from: media) else { return } + publisher + .receive(on: DispatchQueue.main) + .handleEvents(receiveRequest: { _ in + if self.medias.isEmpty { + self.state = .loading + } + }) + .sink(receiveCompletion: { completion in + if case let .failure(error) = completion { + self.state = .failed(error: error) + } + }, receiveValue: { result in + self.medias.append(contentsOf: result.medias) + self.state = .loaded(medias: self.medias) + self.nextPage = result.nextPage + }) + .store(in: &cancellables) + } + + func cancelRefresh() { + cancellables = [] + } + + private func publisher(from media: SRGMedia?) -> AnyPublisher? { + if media != nil { + guard let nextPage = nextPage, media == medias.last else { return nil } + return SRGDataProvider.current!.latestMediasForShow(at: nextPage) + } + else { + return SRGDataProvider.current!.latestMediasForShow(withUrn: show.urn, pageSize: ApplicationConfiguration.shared.pageSize) + } + } +} diff --git a/TV Application/Sources/ShowDetailView.swift b/TV Application/Sources/ShowDetailView.swift new file mode 100644 index 000000000..015ee3ebc --- /dev/null +++ b/TV Application/Sources/ShowDetailView.swift @@ -0,0 +1,217 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGAnalyticsSwiftUI +import SRGAppearance +import SwiftUI + +struct ShowDetailView: View { + @ObservedObject var model: ShowDetailModel + + static let headerHeight: CGFloat = 450 + + enum Section: Hashable { + case medias + case information + } + + enum Content: Hashable { + case loading + case message(_ message: String, iconName: String) + case media(_ media: SRGMedia) + } + + typealias Row = CollectionRow + + init(show: SRGShow) { + model = ShowDetailModel(show: show) + } + + private var rows: [Row] { + switch model.state { + case .loading: + return [Row(section: .information, items: [.loading])] + case let .failed(error: error): + let item = Content.message(friendlyMessage(for: error), iconName: "error-90") + return [Row(section: .information, items: [item])] + case let .loaded(medias: medias): + if !medias.isEmpty { + return [Row(section: .medias, items: medias.map { .media($0) })] + } + else { + let item = Content.message(NSLocalizedString("No results", comment: "Default text displayed when no results are available"), iconName: "media-90") + return [Row(section: .information, items: [item])] + } + } + } + + private static func header() -> [NSCollectionLayoutBoundarySupplementaryItem] { + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(Self.headerHeight)), + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .topLeading + ) + return [header] + } + + private static func layoutSection(for section: Section, geometry: GeometryProxy) -> NSCollectionLayoutSection { + switch section { + case .medias: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(380)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 4) + group.interItemSpacing = .fixed(40) + + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0) + section.interGroupSpacing = 40 + section.boundarySupplementaryItems = Self.header() + return section + case .information: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let height = geometry.size.height - Self.headerHeight + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(height)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + section.boundarySupplementaryItems = Self.header() + return section + } + } + + var body: some View { + GeometryReader { geometry in + CollectionView(rows: rows) { _, _ in + return Self.layoutSection(for: rows.first!.section, geometry: geometry) + } cell: { _, item in + switch item { + case .loading: + ActivityIndicator() + case let .message(text, iconName): + VStack(spacing: 20) { + Image(iconName) + Text(text) + .srgFont(.body) + .lineLimit(2) + .foregroundColor(.white) + } + .opacity(0.8) + .padding() + case let .media(media): + MediaCell(media: media) + .onAppear { + model.loadNextPage(from: media) + } + } + } supplementaryView: { _, _ in + HeaderView(show: model.show) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.play_black)) + .edgesIgnoringSafeArea(.all) + .onAppear { + model.refresh() + } + .onDisappear { + model.cancelRefresh() + } + .onWake { + model.refresh() + } + .tracked(withTitle: analyticsPageTitle, levels: analyticsPageLevels) + } + } + + private struct VisualView: View { + let show: SRGShow + + private static let height: CGFloat = 300 + + private var imageUrl: URL? { + return show.imageURL(for: .width, withValue: SizeForImageScale(.medium).width, type: .default) + } + + var body: some View { + HStack(alignment: .top) { + ImageView(url: imageUrl) + .frame(width: Self.height * 16 / 9, height: Self.height) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 5) { + Text(show.title) + .srgFont(.title1) + .lineLimit(3) + .foregroundColor(.white) + if let broadcastInformationMessage = show.broadcastInformation?.message { + Badge(text: broadcastInformationMessage, color: Color(.play_gray)) + } + if let lead = show.lead { + Text(lead) + .srgFont(.subtitle) + .foregroundColor(.white) + } + Spacer() + } + Spacer() + #if DEBUG + LabeledButton(icon: "favorite-22", label: NSLocalizedString("Favorite", comment:"Show favorite button label")) { + /* Toggle Favorite state */ + } + .padding(.leading, 100) + #else + Spacer() + .frame(width: 120) + .padding(.leading, 100) + #endif + } + } + } + + private struct HeaderView: View { + let show: SRGShow + + var body: some View { + GeometryReader { geometry in + FocusableRegion { + VStack { + VisualView(show: show) + Spacer() + } + } + } + } + } +} + +extension ShowDetailView { + private var analyticsPageTitle: String { + return self.model.show.title + } + + private var analyticsPageLevels: [String] { + let level1: AnalyticsPageLevel = self.model.show.transmission == .radio ? .audio : .video + return [AnalyticsPageLevel.play.rawValue, level1.rawValue, AnalyticsPageLevel.show.rawValue] + } +} + +struct ShowDetailView_Previews: PreviewProvider { + + static var showPreview: SRGShow { + let asset = NSDataAsset(name: "show-srf-tv")! + let jsonData = try! JSONSerialization.jsonObject(with: asset.data, options: []) as? [String: Any] + + return try! MTLJSONAdapter(modelClass: SRGShow.self)?.model(fromJSONDictionary: jsonData) as! SRGShow + } + + static var previews: some View { + ShowDetailView(show: showPreview) + .previewDisplayName("SRF show") + } +} diff --git a/TV Application/Sources/ShowsModel.swift b/TV Application/Sources/ShowsModel.swift new file mode 100644 index 000000000..4c86bb9f9 --- /dev/null +++ b/TV Application/Sources/ShowsModel.swift @@ -0,0 +1,56 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderCombine + +class ShowsModel: ObservableObject { + enum State { + case loading + case failed(error: Error) + case loaded(alphabeticalShows: [(character: Character, shows: [SRGShow])]) + } + + @Published private(set) var state = State.loaded(alphabeticalShows: []) + + private var cancellables = Set() + private var alphabeticalShows: [(character: Character, shows: [SRGShow])] = [] + + func refresh() { + guard alphabeticalShows.isEmpty else { return } + loadPage() + } + + func loadPage() { + SRGDataProvider.current!.tvShows(for: ApplicationConfiguration.shared.vendor, pageSize: SRGDataProviderUnlimitedPageSize) + .receive(on: DispatchQueue.main) + .handleEvents(receiveRequest: { _ in + if self.alphabeticalShows.isEmpty { + self.state = .loading + } + }) + .sink(receiveCompletion: { completion in + if case let .failure(error) = completion { + self.state = .failed(error: error) + } + }, receiveValue: { result in + self.alphabeticalShows = Dictionary(grouping: result.shows) { show in + // Remove accents / diacritics and extract the first character (for wide chars / emoji support) + guard let character = show.title.folding(options: .diacriticInsensitive, locale: .current).uppercased().first else { return "#" } + return !character.isLetter ? "#" : character + } + .map { (character: $0, shows: $1) } + .sorted { left, right in + left.character < right.character + } + self.state = .loaded(alphabeticalShows: self.alphabeticalShows) + }) + .store(in: &cancellables) + } + + func cancelRefresh() { + cancellables = [] + } +} diff --git a/TV Application/Sources/ShowsView.swift b/TV Application/Sources/ShowsView.swift index 19e790f49..a6847adbb 100644 --- a/TV Application/Sources/ShowsView.swift +++ b/TV Application/Sources/ShowsView.swift @@ -4,10 +4,171 @@ // License information is available from the LICENSE file. // +import SRGAnalyticsSwiftUI +import SRGAppearance import SwiftUI struct ShowsView: View { + @ObservedObject var model: ShowsModel + + enum Section: Hashable { + case shows(character: Character) + case information + } + + enum Content: Hashable { + case loading + case message(_ message: String, iconName: String) + case show(_ show: SRGShow) + } + + typealias Row = CollectionRow + + init() { + model = ShowsModel() + } + + private var rows: [Row] { + switch model.state { + case .loading: + return [Row(section: .information, items: [.loading])] + case let .failed(error: error): + let item = Content.message(friendlyMessage(for: error), iconName: "error-90") + return [Row(section: .information, items: [item])] + case let .loaded(alphabeticalShows: alphabeticalShows): + if !alphabeticalShows.isEmpty { + return alphabeticalShows.map { entry in + Row(section: .shows(character: entry.character), items: entry.shows.map { .show($0) }) + } + } + else { + let item = Content.message(NSLocalizedString("No results", comment: "Default text displayed when no results are available"), iconName: "media-90") + return [Row(section: .information, items: [item])] + } + } + } + + private static func header() -> [NSCollectionLayoutBoundarySupplementaryItem] { + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(60)), + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .topLeading + ) + return [header] + } + + private static func layoutSection(for section: Section, geometry: GeometryProxy) -> NSCollectionLayoutSection { + switch section { + case .shows: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(375), heightDimension: .absolute(260)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary + section.interGroupSpacing = 40 + section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 80, trailing: 0) + section.boundarySupplementaryItems = Self.header() + return section + case .information: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(geometry.size.height)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + return NSCollectionLayoutSection(group: group) + } + } + var body: some View { - Text("Shows") + GeometryReader { geometry in + CollectionView(rows: rows) { _, _ in + return Self.layoutSection(for: rows.first!.section, geometry: geometry) + } cell: { _, item in + switch item { + case .loading: + ActivityIndicator() + case let .message(text, iconName): + VStack(spacing: 20) { + Image(iconName) + Text(text) + .srgFont(.body) + .lineLimit(2) + .foregroundColor(.white) + } + .opacity(0.8) + .padding() + case let .show(show): + ShowCell(show: show) + } + } supplementaryView: { _, indexPath in + switch model.state { + case .loading, .failed: + Rectangle() + .fill(Color.clear) + case let .loaded(alphabeticalShows: alphabeticalShows): + let character = alphabeticalShows[indexPath.section].character + let title = (character == "#") ? "#0-9" : String(character) + HeaderView(title: title) + } + } + .synchronizeParentTabScrolling() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.play_black)) + .edgesIgnoringSafeArea(.all) + .onAppear { + model.refresh() + } + .onDisappear { + model.cancelRefresh() + } + .onWake { + model.refresh() + } + .tracked(withTitle: analyticsPageTitle, levels: analyticsPageLevels) + } + } + + private struct HeaderView: View { + let title: String + + var body: some View { + GeometryReader { geometry in + Text(title) + .srgFont(.title2) + .lineLimit(1) + .foregroundColor(.white) + .opacity(0.8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) + } + } + } +} + +extension ShowsView { + private var analyticsPageTitle: String { + return AnalyticsPageTitle.showsAZ.rawValue + } + + private var analyticsPageLevels: [String] { + return [AnalyticsPageLevel.play.rawValue, AnalyticsPageLevel.video.rawValue] + } +} + +struct ShowsView_Previews: PreviewProvider { + + static var showPreview: SRGShow { + let asset = NSDataAsset(name: "show-srf-tv")! + let jsonData = try! JSONSerialization.jsonObject(with: asset.data, options: []) as? [String: Any] + + return try! MTLJSONAdapter(modelClass: SRGShow.self)?.model(fromJSONDictionary: jsonData) as! SRGShow + } + + static var previews: some View { + ShowsView() + .previewDisplayName("SRF shows") } } diff --git a/TV Application/Sources/TopicCell.swift b/TV Application/Sources/TopicCell.swift index 8ff91eb22..bfa931bdf 100644 --- a/TV Application/Sources/TopicCell.swift +++ b/TV Application/Sources/TopicCell.swift @@ -23,14 +23,17 @@ struct TopicCell: View { var body: some View { GeometryReader { geometry in - Button(action: {}) { + Button(action: { + if let topic = topic { + navigateToTopic(topic) + } + }) { ZStack { ImageView(url: imageUrl) - .whenRedacted { $0.hidden() } Rectangle() .fill(Color(white: 0, opacity: 0.4)) Text(title) - .srgFont(.medium, size: .subtitle) + .srgFont(.overline) .lineLimit(1) .foregroundColor(.white) .padding(20) @@ -38,6 +41,9 @@ struct TopicCell: View { } .frame(width: geometry.size.width, height: geometry.size.height) .redacted(reason: redactionReason) + .accessibilityElement() + .accessibilityLabel(topic?.title ?? "") + .accessibility(addTraits: .isButton) } .buttonStyle(CardButtonStyle()) } diff --git a/TV Application/Sources/TopicDetailModel.swift b/TV Application/Sources/TopicDetailModel.swift new file mode 100644 index 000000000..588fa54a1 --- /dev/null +++ b/TV Application/Sources/TopicDetailModel.swift @@ -0,0 +1,74 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGDataProviderCombine + +class TopicDetailModel: ObservableObject { + let topic: SRGTopic + + enum State { + case loading + case failed(error: Error) + case loaded(mostPopularMedias: [SRGMedia], latestMedias: [SRGMedia]) + } + + @Published private(set) var state = State.loaded(mostPopularMedias: [], latestMedias: []) + + private var cancellables = Set() + + private var mostPopularMedias: [SRGMedia] = [] + + private var latestMedias: [SRGMedia] = [] + private var nextPage: SRGDataProvider.LatestMediasForTopic.Page? = nil + + init(topic: SRGTopic) { + self.topic = topic + } + + func refresh() { + // Pagination is on latest medias, so if some are already loaded do not perform a refresh + guard latestMedias.isEmpty else { return } + loadNextPage() + } + + func loadNextPage(from media: SRGMedia? = nil) { + guard let latestPublisher = latestPublisher(from: media) else { return } + + let mostPopularPublisher = SRGDataProvider.current!.mostPopularMediasForTopic(withUrn: topic.urn, pageSize: ApplicationConfiguration.shared.pageSize) + Publishers.CombineLatest(mostPopularPublisher, latestPublisher) + .receive(on: DispatchQueue.main) + .handleEvents(receiveRequest: { _ in + if self.mostPopularMedias.isEmpty && self.latestMedias.isEmpty { + self.state = .loading + } + }) + .sink(receiveCompletion: { completion in + if case let .failure(error) = completion { + self.state = .failed(error: error) + } + }, receiveValue: { combined in + self.mostPopularMedias = combined.0.medias + self.latestMedias.append(contentsOf: combined.1.medias) + self.state = .loaded(mostPopularMedias: self.mostPopularMedias, latestMedias: self.latestMedias) + self.nextPage = combined.1.nextPage + }) + .store(in: &cancellables) + } + + func cancelRefresh() { + cancellables = [] + } + + private func latestPublisher(from media: SRGMedia?) -> AnyPublisher? { + if media != nil { + guard let nextPage = nextPage, media == latestMedias.last else { return nil } + return SRGDataProvider.current!.latestMediasForTopic(at: nextPage) + } + else { + return SRGDataProvider.current!.latestMediasForTopic(withUrn: topic.urn, pageSize: ApplicationConfiguration.shared.pageSize) + } + } +} diff --git a/TV Application/Sources/TopicDetailView.swift b/TV Application/Sources/TopicDetailView.swift new file mode 100644 index 000000000..2e6e6e41a --- /dev/null +++ b/TV Application/Sources/TopicDetailView.swift @@ -0,0 +1,200 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import SRGAnalyticsSwiftUI +import SwiftUI + +struct TopicDetailView: View { + @ObservedObject var model: TopicDetailModel + + static let headerHeight: CGFloat = 60 + + enum Section: Hashable { + case mostPopularMedias + case latestMedias + case information + } + + enum Content: Hashable { + case loading + case message(_ message: String, iconName: String) + case media(_ media: SRGMedia, section: Section) + } + + typealias Row = CollectionRow + + init(topic: SRGTopic) { + model = TopicDetailModel(topic: topic) + } + + private var rows: [Row] { + switch model.state { + case .loading: + return [Row(section: .information, items: [.loading])] + case let .failed(error: error): + let item = Content.message(friendlyMessage(for: error), iconName: "error-90") + return [Row(section: .information, items: [item])] + case let .loaded(mostPopularMedias: mostPopularMedias, latestMedias: latestMedias): + if mostPopularMedias.isEmpty && latestMedias.isEmpty { + let item = Content.message(NSLocalizedString("No results", comment: "Default text displayed when no results are available"), iconName: "media-90") + return [Row(section: .information, items: [item])] + } + + var rows = [Row]() + if !mostPopularMedias.isEmpty { + rows.append(Row(section: .mostPopularMedias, items: mostPopularMedias.map { .media($0, section: .mostPopularMedias) })) + } + if !latestMedias.isEmpty { + rows.append(Row(section: .latestMedias, items: latestMedias.map { .media($0, section: .latestMedias) })) + } + return rows + } + } + + private static func header(withHeight height: CGFloat = Self.headerHeight) -> [NSCollectionLayoutBoundarySupplementaryItem] { + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(height)), + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .topLeading + ) + return [header] + } + + private static func layoutSection(for section: Section, geometry: GeometryProxy) -> NSCollectionLayoutSection { + switch section { + case .mostPopularMedias: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(1740), heightDimension: .absolute(680)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuous + section.interGroupSpacing = 40 + section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0) + section.boundarySupplementaryItems = Self.header() + return section + case .latestMedias: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(380)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 4) + group.interItemSpacing = .fixed(40) + + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0) + section.interGroupSpacing = 40 + section.boundarySupplementaryItems = Self.header(withHeight: 100) + return section + case .information: + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let height = geometry.size.height - Self.headerHeight + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(height)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + section.boundarySupplementaryItems = Self.header() + return section + } + } + + var body: some View { + GeometryReader { geometry in + CollectionView(rows: rows) { sectionIndex, _ in + let section = rows[sectionIndex].section + return Self.layoutSection(for: section, geometry: geometry) + } cell: { _, item in + switch item { + case .loading: + ActivityIndicator() + case let .message(text, iconName): + VStack(spacing: 20) { + Image(iconName) + Text(text) + .srgFont(.body) + .lineLimit(2) + .foregroundColor(.white) + } + .opacity(0.8) + .padding() + case let .media(media, section): + switch section { + case .latestMedias: + MediaCell(media: media, style: .show) + .onAppear { + model.loadNextPage(from: media) + } + default: + HeroMediaCell(media: media) + } + } + } supplementaryView: { _, indexPath in + if rows.count > 1 { + let section = rows[indexPath.section].section + switch section { + case .latestMedias: + HeaderView(title: NSLocalizedString("Latest videos", comment: "Title label used to present the latest videos")) + default: + TitleView(title: model.topic.title) + } + } + else { + TitleView(title: model.topic.title) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.play_black)) + .edgesIgnoringSafeArea(.all) + .onAppear { + model.refresh() + } + .onDisappear { + model.cancelRefresh() + } + .onWake { + model.refresh() + } + .tracked(withTitle: analyticsPageTitle, levels: analyticsPageLevels) + } + } + + private struct TitleView: View { + let title: String + + var body: some View { + Text(title) + .srgFont(.title1) + .foregroundColor(.white) + .opacity(0.8) + } + } + + private struct HeaderView: View { + let title: String + + var body: some View { + Text(title) + .srgFont(.title2) + .foregroundColor(.white) + .opacity(0.8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) + } + } +} + +extension TopicDetailView { + private var analyticsPageTitle: String { + return self.model.topic.title + } + + private var analyticsPageLevels: [String] { + return [AnalyticsPageLevel.play.rawValue, AnalyticsPageLevel.video.rawValue] + } +} diff --git a/TV Application/Sources/UIKit+Extensions.swift b/TV Application/Sources/UIKit+Extensions.swift new file mode 100644 index 000000000..9d560935d --- /dev/null +++ b/TV Application/Sources/UIKit+Extensions.swift @@ -0,0 +1,23 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import UIKit + +extension UIWindow { + var topViewController: UIViewController? { + return rootViewController?.topViewController + } +} + +extension UIViewController { + var topViewController: UIViewController { + var topViewController = self + while let topPresentedViewController = topViewController.presentedViewController { + topViewController = topPresentedViewController + } + return topViewController + } +} diff --git a/TV Application/Sources/VideosView.swift b/TV Application/Sources/VideosView.swift index bc7f58eef..4665ac247 100644 --- a/TV Application/Sources/VideosView.swift +++ b/TV Application/Sources/VideosView.swift @@ -17,7 +17,7 @@ struct VideosView: View { .onDisappear { model.cancelRefresh() } - .onResume { + .onWake { model.refresh() } } diff --git a/TV Application/TV-Application-Info.plist b/TV Application/TV-Application-Info.plist index c7b36dcab..c4bee00ab 100644 --- a/TV Application/TV-Application-Info.plist +++ b/TV Application/TV-Application-Info.plist @@ -26,6 +26,10 @@ LSRequiresIPhoneOS + UIBackgroundModes + + audio + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -34,5 +38,18 @@ UIUserInterfaceStyle $(USER_INTERFACE_STYLE) + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLName + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleURLSchemes + + $(URL_SCHEME) + + + diff --git a/WhatsNew-beta.json b/WhatsNew-iOS-beta.json similarity index 97% rename from WhatsNew-beta.json rename to WhatsNew-iOS-beta.json index 0a0404062..40d4397b2 100755 --- a/WhatsNew-beta.json +++ b/WhatsNew-iOS-beta.json @@ -122,5 +122,9 @@ "3.1.3-343": "- Age rating icons have been replaced with a generic one without reference to FSK ratings.\n- Update measurement components for iOS 14.", "3.1.4-344": "- iOS 14 support.\n- New Google Cast onboard user experience (local network access).\n- Add permissions section in application settings view.\n- Fix glitches associated with restoration from picture in picture.", "3.1.4-345": "- Fix playback incorrectly restarting in background without user interaction.\n- Fix video playback incorrectly stopped when disconnecting a Bluetooth headset or speaker.", - "3.1.4-346": "- Fix minor user interface issues.\n- Avoid playback starting on a Google Cast receiver and on the device simultaneously.\n- Fix a VoiceOver crash when opening a media." + "3.1.4-346": "- Fix minor user interface issues.\n- Avoid playback starting on a Google Cast receiver and on the device simultaneously.\n- Fix a VoiceOver crash when opening a media.", + "3.1.5-347": "- Enable control center when using Picture in Picture.\n- Fix sharing and search filter popover anchoring issues for iPadOS 14.
- Update analytics with new segment specifications.", + "3.1.5-348": "- Support future SRG event livestreams.", + "3.1.5-349": "- Improve suggestions displayed for SRF at the top of the homepage.\n- Add support for web first video display on the homepage.", + "3.1.5-350": "- All SRG libraries updated. \n- AppStore release." } \ No newline at end of file diff --git a/WhatsNew-tvOS-beta.json b/WhatsNew-tvOS-beta.json new file mode 100755 index 000000000..bfc5ef7c3 --- /dev/null +++ b/WhatsNew-tvOS-beta.json @@ -0,0 +1,7 @@ +{ + "1.0.0-1": "First beta for tvOS.\n\n- Videos tab with TV topics, trending, events.\n- Livestreams tab with TV and radio live channels.\n- Shows tab with main TV shows.", + "1.0.0-2": "- Current program display on the livestreams tab.\n- Playback can be resumed.\n- Standard page view measurements.", + "1.0.0-3": "- Updated translations for all BUs.\n- Add date information in media view.\n- \"Resume\" button instead of \"Watch\" button if needed.\n- Only one tab for Play SWI.", + "1.0.0-4": "- Redesigned TV show A-Z page.\n- Add most popular videos at the top of topic pages.\n- Correctly update play button state when playback can be resumed.\n- Improve suggestions displayed for SRF at the top of the homepage.\n- Add support for web first video display on the homepage.\n- Conform to official page view measurement specifications.", + "1.0.0-5": "- Basic accessibility.\n- More episodes button renamed on the media detail page.\n- Remove episode header on the show detail page.\n- Correctly update play button state when content playback is completed." +} \ No newline at end of file diff --git a/Xcode/Play RSI Notification Service Extension.xcconfig b/Xcode/Play RSI Notification Service Extension.xcconfig new file mode 100755 index 000000000..c1674cf45 --- /dev/null +++ b/Xcode/Play RSI Notification Service Extension.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Notification Service Extension.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI Screenshots.xcconfig b/Xcode/Play RSI Screenshots.xcconfig new file mode 100755 index 000000000..190558192 --- /dev/null +++ b/Xcode/Play RSI Screenshots.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Screenshots.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI TV.appstore.xcconfig b/Xcode/Play RSI TV.appstore.xcconfig index 65c96e7bb..c5bf4e772 100755 --- a/Xcode/Play RSI TV.appstore.xcconfig +++ b/Xcode/Play RSI TV.appstore.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RSI TV/Pods-Play SRG-tvOS-Play RSI TV.appstore.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI TV.beta.xcconfig b/Xcode/Play RSI TV.beta.xcconfig index a1139de43..b9e65423d 100755 --- a/Xcode/Play RSI TV.beta.xcconfig +++ b/Xcode/Play RSI TV.beta.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RSI TV/Pods-Play SRG-tvOS-Play RSI TV.beta.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI TV.debug.xcconfig b/Xcode/Play RSI TV.debug.xcconfig index 9a1c4102d..7ab09fb4a 100755 --- a/Xcode/Play RSI TV.debug.xcconfig +++ b/Xcode/Play RSI TV.debug.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RSI TV/Pods-Play SRG-tvOS-Play RSI TV.debug.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI TV.nightly.xcconfig b/Xcode/Play RSI TV.nightly.xcconfig index 9f11bb474..16c5b90e8 100755 --- a/Xcode/Play RSI TV.nightly.xcconfig +++ b/Xcode/Play RSI TV.nightly.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RSI TV/Pods-Play SRG-tvOS-Play RSI TV.nightly.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI.appstore.xcconfig b/Xcode/Play RSI.appstore.xcconfig index 7dae24e08..b0d549375 100755 --- a/Xcode/Play RSI.appstore.xcconfig +++ b/Xcode/Play RSI.appstore.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RSI.appstore.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RSI/Pods-Play SRG-iOS-Play RSI.appstore.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI.beta.xcconfig b/Xcode/Play RSI.beta.xcconfig index 962db8c56..a176160e3 100755 --- a/Xcode/Play RSI.beta.xcconfig +++ b/Xcode/Play RSI.beta.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RSI.beta.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RSI/Pods-Play SRG-iOS-Play RSI.beta.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI.debug.xcconfig b/Xcode/Play RSI.debug.xcconfig index 40515bdc3..533ca0d5f 100755 --- a/Xcode/Play RSI.debug.xcconfig +++ b/Xcode/Play RSI.debug.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RSI.debug.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RSI/Pods-Play SRG-iOS-Play RSI.debug.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI.nightly.xcconfig b/Xcode/Play RSI.nightly.xcconfig index 46d548eb4..69e4e9c06 100755 --- a/Xcode/Play RSI.nightly.xcconfig +++ b/Xcode/Play RSI.nightly.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RSI.nightly.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RSI/Pods-Play SRG-iOS-Play RSI.nightly.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RSI.xcconfig" diff --git a/Xcode/Play RSI.xcconfig b/Xcode/Play RSI.xcconfig new file mode 100755 index 000000000..70f961e0b --- /dev/null +++ b/Xcode/Play RSI.xcconfig @@ -0,0 +1,3 @@ +APP_BASE_IDENTIFIER = ch.rsi.rsiplayer +BASE_PRODUCT_NAME = Play RSI +URL_BASE_SCHEME = playrsi \ No newline at end of file diff --git a/Xcode/Play RTR Notification Service Extension.xcconfig b/Xcode/Play RTR Notification Service Extension.xcconfig new file mode 100755 index 000000000..7da0fc448 --- /dev/null +++ b/Xcode/Play RTR Notification Service Extension.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Notification Service Extension.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR Screenshots.xcconfig b/Xcode/Play RTR Screenshots.xcconfig new file mode 100755 index 000000000..9da6fdeed --- /dev/null +++ b/Xcode/Play RTR Screenshots.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Screenshots.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR TV.appstore.xcconfig b/Xcode/Play RTR TV.appstore.xcconfig index 7aa44ee59..f3e9138c4 100755 --- a/Xcode/Play RTR TV.appstore.xcconfig +++ b/Xcode/Play RTR TV.appstore.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTR TV/Pods-Play SRG-tvOS-Play RTR TV.appstore.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR TV.beta.xcconfig b/Xcode/Play RTR TV.beta.xcconfig index e918bb325..f17368ded 100755 --- a/Xcode/Play RTR TV.beta.xcconfig +++ b/Xcode/Play RTR TV.beta.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTR TV/Pods-Play SRG-tvOS-Play RTR TV.beta.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR TV.debug.xcconfig b/Xcode/Play RTR TV.debug.xcconfig index da9fc760c..adb3d055c 100755 --- a/Xcode/Play RTR TV.debug.xcconfig +++ b/Xcode/Play RTR TV.debug.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTR TV/Pods-Play SRG-tvOS-Play RTR TV.debug.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR TV.nightly.xcconfig b/Xcode/Play RTR TV.nightly.xcconfig index 4c49d6615..c7869403f 100755 --- a/Xcode/Play RTR TV.nightly.xcconfig +++ b/Xcode/Play RTR TV.nightly.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTR TV/Pods-Play SRG-tvOS-Play RTR TV.nightly.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR.appstore.xcconfig b/Xcode/Play RTR.appstore.xcconfig index 0af729351..70e5897c4 100755 --- a/Xcode/Play RTR.appstore.xcconfig +++ b/Xcode/Play RTR.appstore.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTR.appstore.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTR/Pods-Play SRG-iOS-Play RTR.appstore.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR.beta.xcconfig b/Xcode/Play RTR.beta.xcconfig index 7c451c4aa..7bbd5a3d8 100755 --- a/Xcode/Play RTR.beta.xcconfig +++ b/Xcode/Play RTR.beta.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTR.beta.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTR/Pods-Play SRG-iOS-Play RTR.beta.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR.debug.xcconfig b/Xcode/Play RTR.debug.xcconfig index e1ec5b073..fc2ee9c5b 100755 --- a/Xcode/Play RTR.debug.xcconfig +++ b/Xcode/Play RTR.debug.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTR.debug.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTR/Pods-Play SRG-iOS-Play RTR.debug.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR.nightly.xcconfig b/Xcode/Play RTR.nightly.xcconfig index 2f0311831..3cfd14320 100755 --- a/Xcode/Play RTR.nightly.xcconfig +++ b/Xcode/Play RTR.nightly.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTR.nightly.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTR/Pods-Play SRG-iOS-Play RTR.nightly.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTR.xcconfig" diff --git a/Xcode/Play RTR.xcconfig b/Xcode/Play RTR.xcconfig new file mode 100755 index 000000000..32700fb28 --- /dev/null +++ b/Xcode/Play RTR.xcconfig @@ -0,0 +1,3 @@ +APP_BASE_IDENTIFIER = ch.rtr.rtrplayer +BASE_PRODUCT_NAME = Play RTR +URL_BASE_SCHEME = playrtr \ No newline at end of file diff --git a/Xcode/Play RTS Notification Service Extension.xcconfig b/Xcode/Play RTS Notification Service Extension.xcconfig new file mode 100755 index 000000000..eca53c706 --- /dev/null +++ b/Xcode/Play RTS Notification Service Extension.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Notification Service Extension.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS Screenshots.xcconfig b/Xcode/Play RTS Screenshots.xcconfig new file mode 100755 index 000000000..862618e14 --- /dev/null +++ b/Xcode/Play RTS Screenshots.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Screenshots.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS TV.appstore.xcconfig b/Xcode/Play RTS TV.appstore.xcconfig index 78bbec639..52bdcc19f 100755 --- a/Xcode/Play RTS TV.appstore.xcconfig +++ b/Xcode/Play RTS TV.appstore.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTS TV/Pods-Play SRG-tvOS-Play RTS TV.appstore.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS TV.beta.xcconfig b/Xcode/Play RTS TV.beta.xcconfig index 7114e6d6c..9d9eadb63 100755 --- a/Xcode/Play RTS TV.beta.xcconfig +++ b/Xcode/Play RTS TV.beta.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTS TV/Pods-Play SRG-tvOS-Play RTS TV.beta.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS TV.debug.xcconfig b/Xcode/Play RTS TV.debug.xcconfig index d5a4cef98..becc5b8a0 100755 --- a/Xcode/Play RTS TV.debug.xcconfig +++ b/Xcode/Play RTS TV.debug.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTS TV/Pods-Play SRG-tvOS-Play RTS TV.debug.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS TV.nightly.xcconfig b/Xcode/Play RTS TV.nightly.xcconfig index 0da8da223..d45f804e6 100755 --- a/Xcode/Play RTS TV.nightly.xcconfig +++ b/Xcode/Play RTS TV.nightly.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play RTS TV/Pods-Play SRG-tvOS-Play RTS TV.nightly.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS.appstore.xcconfig b/Xcode/Play RTS.appstore.xcconfig index 85a004444..4f3f3ea82 100755 --- a/Xcode/Play RTS.appstore.xcconfig +++ b/Xcode/Play RTS.appstore.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTS.appstore.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTS/Pods-Play SRG-iOS-Play RTS.appstore.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS.beta.xcconfig b/Xcode/Play RTS.beta.xcconfig index 798a891ad..735338e9c 100755 --- a/Xcode/Play RTS.beta.xcconfig +++ b/Xcode/Play RTS.beta.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTS.beta.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTS/Pods-Play SRG-iOS-Play RTS.beta.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS.debug.xcconfig b/Xcode/Play RTS.debug.xcconfig index 129ec958e..6fe1995d5 100755 --- a/Xcode/Play RTS.debug.xcconfig +++ b/Xcode/Play RTS.debug.xcconfig @@ -1,3 +1,4 @@ #include "Links/Play RTS.debug.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTS/Pods-Play SRG-iOS-Play RTS.debug.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS.nightly.xcconfig b/Xcode/Play RTS.nightly.xcconfig index d4e4519f2..5f6a6de65 100755 --- a/Xcode/Play RTS.nightly.xcconfig +++ b/Xcode/Play RTS.nightly.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play RTS.nightly.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play RTS/Pods-Play SRG-iOS-Play RTS.nightly.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play RTS.xcconfig" diff --git a/Xcode/Play RTS.xcconfig b/Xcode/Play RTS.xcconfig new file mode 100755 index 000000000..879f4fcd9 --- /dev/null +++ b/Xcode/Play RTS.xcconfig @@ -0,0 +1,3 @@ +APP_BASE_IDENTIFIER = ch.rts.rtsplayer +BASE_PRODUCT_NAME = Play RTS +URL_BASE_SCHEME = playrts \ No newline at end of file diff --git a/Xcode/Play SRF Notification Service Extension.xcconfig b/Xcode/Play SRF Notification Service Extension.xcconfig new file mode 100755 index 000000000..4ba747f1b --- /dev/null +++ b/Xcode/Play SRF Notification Service Extension.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Notification Service Extension.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF Screenshots.xcconfig b/Xcode/Play SRF Screenshots.xcconfig new file mode 100755 index 000000000..58ffc957c --- /dev/null +++ b/Xcode/Play SRF Screenshots.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Screenshots.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF TV.appstore.xcconfig b/Xcode/Play SRF TV.appstore.xcconfig index afabc93b9..2e2b0fac7 100755 --- a/Xcode/Play SRF TV.appstore.xcconfig +++ b/Xcode/Play SRF TV.appstore.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SRF TV/Pods-Play SRG-tvOS-Play SRF TV.appstore.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF TV.beta.xcconfig b/Xcode/Play SRF TV.beta.xcconfig index 691a39282..fda8fe979 100755 --- a/Xcode/Play SRF TV.beta.xcconfig +++ b/Xcode/Play SRF TV.beta.xcconfig @@ -1,3 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SRF TV/Pods-Play SRG-tvOS-Play SRF TV.beta.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" - +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF TV.debug.xcconfig b/Xcode/Play SRF TV.debug.xcconfig index f293740e4..ba8a49b23 100755 --- a/Xcode/Play SRF TV.debug.xcconfig +++ b/Xcode/Play SRF TV.debug.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SRF TV/Pods-Play SRG-tvOS-Play SRF TV.debug.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF TV.nightly.xcconfig b/Xcode/Play SRF TV.nightly.xcconfig index 3883322e7..cff083eac 100755 --- a/Xcode/Play SRF TV.nightly.xcconfig +++ b/Xcode/Play SRF TV.nightly.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SRF TV/Pods-Play SRG-tvOS-Play SRF TV.nightly.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF.appstore.xcconfig b/Xcode/Play SRF.appstore.xcconfig index 49f591774..c143af032 100755 --- a/Xcode/Play SRF.appstore.xcconfig +++ b/Xcode/Play SRF.appstore.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SRF.appstore.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SRF/Pods-Play SRG-iOS-Play SRF.appstore.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF.beta.xcconfig b/Xcode/Play SRF.beta.xcconfig index 140c0c70f..37fed8787 100755 --- a/Xcode/Play SRF.beta.xcconfig +++ b/Xcode/Play SRF.beta.xcconfig @@ -1,4 +1,4 @@ #include "Xcode/Links/Play SRF.beta.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SRF/Pods-Play SRG-iOS-Play SRF.beta.xcconfig" #include "Xcode/Play iOS Application.xcconfig" - +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF.debug.xcconfig b/Xcode/Play SRF.debug.xcconfig index faed2e8fc..987a0220e 100755 --- a/Xcode/Play SRF.debug.xcconfig +++ b/Xcode/Play SRF.debug.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SRF.debug.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SRF/Pods-Play SRG-iOS-Play SRF.debug.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF.nightly.xcconfig b/Xcode/Play SRF.nightly.xcconfig index da8a1ef0e..3482029ee 100755 --- a/Xcode/Play SRF.nightly.xcconfig +++ b/Xcode/Play SRF.nightly.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SRF.nightly.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SRF/Pods-Play SRG-iOS-Play SRF.nightly.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SRF.xcconfig" diff --git a/Xcode/Play SRF.xcconfig b/Xcode/Play SRF.xcconfig new file mode 100755 index 000000000..274299304 --- /dev/null +++ b/Xcode/Play SRF.xcconfig @@ -0,0 +1,3 @@ +APP_BASE_IDENTIFIER = ch.srf.srfplayer +BASE_PRODUCT_NAME = Play SRF +URL_BASE_SCHEME = playsrf \ No newline at end of file diff --git a/Xcode/Play SWI Notification Service Extension.xcconfig b/Xcode/Play SWI Notification Service Extension.xcconfig new file mode 100755 index 000000000..8a2253c82 --- /dev/null +++ b/Xcode/Play SWI Notification Service Extension.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Notification Service Extension.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI Screenshots.xcconfig b/Xcode/Play SWI Screenshots.xcconfig new file mode 100755 index 000000000..8e502af15 --- /dev/null +++ b/Xcode/Play SWI Screenshots.xcconfig @@ -0,0 +1,2 @@ +#include "Xcode/Play iOS Screenshots.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI TV.appstore.xcconfig b/Xcode/Play SWI TV.appstore.xcconfig index 64c0361ea..0844f0314 100755 --- a/Xcode/Play SWI TV.appstore.xcconfig +++ b/Xcode/Play SWI TV.appstore.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SWI TV/Pods-Play SRG-tvOS-Play SWI TV.appstore.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI TV.beta.xcconfig b/Xcode/Play SWI TV.beta.xcconfig index 2cd51eae9..1a7a9af81 100755 --- a/Xcode/Play SWI TV.beta.xcconfig +++ b/Xcode/Play SWI TV.beta.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SWI TV/Pods-Play SRG-tvOS-Play SWI TV.beta.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI TV.debug.xcconfig b/Xcode/Play SWI TV.debug.xcconfig index 54de2b7fe..fac66ee74 100755 --- a/Xcode/Play SWI TV.debug.xcconfig +++ b/Xcode/Play SWI TV.debug.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SWI TV/Pods-Play SRG-tvOS-Play SWI TV.debug.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI TV.nightly.xcconfig b/Xcode/Play SWI TV.nightly.xcconfig index 951575cd0..54404a1de 100755 --- a/Xcode/Play SWI TV.nightly.xcconfig +++ b/Xcode/Play SWI TV.nightly.xcconfig @@ -1,2 +1,3 @@ #include "Pods/Target Support Files/Pods-Play SRG-tvOS-Play SWI TV/Pods-Play SRG-tvOS-Play SWI TV.nightly.xcconfig" #include "Xcode/Play tvOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI.appstore.xcconfig b/Xcode/Play SWI.appstore.xcconfig index 2a3fb5227..6abe58b12 100755 --- a/Xcode/Play SWI.appstore.xcconfig +++ b/Xcode/Play SWI.appstore.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SWI.appstore.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SWI/Pods-Play SRG-iOS-Play SWI.appstore.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI.beta.xcconfig b/Xcode/Play SWI.beta.xcconfig index bdb805e56..1cf42ee28 100755 --- a/Xcode/Play SWI.beta.xcconfig +++ b/Xcode/Play SWI.beta.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SWI.beta.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SWI/Pods-Play SRG-iOS-Play SWI.beta.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI.debug.xcconfig b/Xcode/Play SWI.debug.xcconfig index 0b33731ee..bd3c7dd81 100755 --- a/Xcode/Play SWI.debug.xcconfig +++ b/Xcode/Play SWI.debug.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SWI.debug.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SWI/Pods-Play SRG-iOS-Play SWI.debug.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI.nightly.xcconfig b/Xcode/Play SWI.nightly.xcconfig index 2d87e5ad4..11a3d970e 100755 --- a/Xcode/Play SWI.nightly.xcconfig +++ b/Xcode/Play SWI.nightly.xcconfig @@ -1,3 +1,4 @@ #include "Xcode/Links/Play SWI.nightly.xcconfig" #include "Pods/Target Support Files/Pods-Play SRG-iOS-Play SWI/Pods-Play SRG-iOS-Play SWI.nightly.xcconfig" #include "Xcode/Play iOS Application.xcconfig" +#include "Xcode/Play SWI.xcconfig" diff --git a/Xcode/Play SWI.xcconfig b/Xcode/Play SWI.xcconfig new file mode 100755 index 000000000..bea0de063 --- /dev/null +++ b/Xcode/Play SWI.xcconfig @@ -0,0 +1,3 @@ +APP_BASE_IDENTIFIER = ch.swi.swiplayer +BASE_PRODUCT_NAME = Play SWI +URL_BASE_SCHEME = playswi \ No newline at end of file diff --git a/Xcode/Play iOS Application.xcconfig b/Xcode/Play iOS Application.xcconfig index 58f6318e2..aee93b8be 100755 --- a/Xcode/Play iOS Application.xcconfig +++ b/Xcode/Play iOS Application.xcconfig @@ -1,3 +1,6 @@ #include "Xcode/Play iOS.xcconfig" +PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER) +PRODUCT_NAME = $(BASE_PRODUCT_NAME) +SDKROOT = iphoneos SWIFT_OBJC_BRIDGING_HEADER=$(PROJECT_DIR)/Application/Sources/Bridges/Play-ObjectiveC-Bridge.h diff --git a/Xcode/Play iOS Notification Service Extension.xcconfig b/Xcode/Play iOS Notification Service Extension.xcconfig new file mode 100755 index 000000000..f974000bb --- /dev/null +++ b/Xcode/Play iOS Notification Service Extension.xcconfig @@ -0,0 +1,5 @@ +#include "Xcode/Play iOS.xcconfig" + +PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).notification-service-extension +PRODUCT_NAME = $(BASE_PRODUCT_NAME) notification service extension +SDKROOT = iphoneos diff --git a/Xcode/Screenshots.xcconfig b/Xcode/Play iOS Screenshots.xcconfig similarity index 51% rename from Xcode/Screenshots.xcconfig rename to Xcode/Play iOS Screenshots.xcconfig index 3781bcb94..64bdb1a6f 100755 --- a/Xcode/Screenshots.xcconfig +++ b/Xcode/Play iOS Screenshots.xcconfig @@ -1,3 +1,6 @@ #include "Xcode/Play iOS.xcconfig" +PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).screenshots-uitest +PRODUCT_NAME = $(BASE_PRODUCT_NAME) screenshots +SDKROOT = iphoneos SWIFT_OBJC_BRIDGING_HEADER=$(PROJECT_DIR)/UITests/Screenshots/Sources/Bridges/Screenshots-ObjectiveC-Bridge.h diff --git a/Xcode/Play iOS.xcconfig b/Xcode/Play iOS.xcconfig index 853dd4010..ef852de2a 100755 --- a/Xcode/Play iOS.xcconfig +++ b/Xcode/Play iOS.xcconfig @@ -1,3 +1,3 @@ // Version information -MARKETING_VERSION = 3.1.4 -CURRENT_PROJECT_VERSION = 346 +MARKETING_VERSION = 3.1.5 +CURRENT_PROJECT_VERSION = 350 diff --git a/Xcode/Play tvOS Application.xcconfig b/Xcode/Play tvOS Application.xcconfig index c6549749d..b62cb591b 100755 --- a/Xcode/Play tvOS Application.xcconfig +++ b/Xcode/Play tvOS Application.xcconfig @@ -1,3 +1,6 @@ #include "Xcode/Play tvOS.xcconfig" +PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER) +PRODUCT_NAME = $(BASE_PRODUCT_NAME) +SDKROOT = appletvos SWIFT_OBJC_BRIDGING_HEADER=$(PROJECT_DIR)/TV Application/Sources/Play-TV-ObjectiveC-Bridge.h diff --git a/Xcode/Play tvOS.xcconfig b/Xcode/Play tvOS.xcconfig index e784b5168..9f5602b8f 100755 --- a/Xcode/Play tvOS.xcconfig +++ b/Xcode/Play tvOS.xcconfig @@ -1,3 +1,3 @@ // Version information MARKETING_VERSION = 1.0.0 -CURRENT_PROJECT_VERSION = 1 +CURRENT_PROJECT_VERSION = 5 diff --git a/docs/README-images/home-tv.jpg b/docs/README-images/home-tv.jpg new file mode 100644 index 000000000..188d0a0ee Binary files /dev/null and b/docs/README-images/home-tv.jpg differ diff --git a/docs/README.md b/docs/README.md index 0eb84969b..2059aa528 100755 --- a/docs/README.md +++ b/docs/README.md @@ -2,36 +2,38 @@ ## About -Play SRG is the [SRG SSR (Swiss Broadcasting Corporation)](https://www.srgssr.ch/en/who-we-are/organisation/) audio and video platform, provided as a distinct service for each of its business units ([RSI](https://www.rsi.ch), [RTR](https://www.rtr.ch), [RTS](https://www.rts.ch), [SRF](https://www.srf.ch) and [SWI](https://www.swissinfo.ch)). This repository contains the source code of the Play SRG application for iOS. +Play SRG is the [SRG SSR (Swiss Broadcasting Corporation)](https://www.srgssr.ch/en/who-we-are/organisation/) audio and video platform, provided as a distinct service for each of its business units ([RSI](https://www.rsi.ch), [RTR](https://www.rtr.ch), [RTS](https://www.rts.ch), [SRF](https://www.srf.ch) and [SWI](https://www.swissinfo.ch)). This repository contains the source code of the Play SRG applications for iOS and tvOS. -The Play platform is more generally accessible on the web and on Android phones: +The Play platform is more generally available on the web and on Android phones: | Platform | Play RSI | Play RTR | Play RTS | Play SRF | Play SWI | |:-- |:--:|:--:|:--:|:--:|:--:| -| iOS | [📱](https://itunes.apple.com/ch/app/play-rsi/id920753497) | [📱](https://itunes.apple.com/ch/app/play-rtr/id920754925) | [📱](https://itunes.apple.com/ch/app/play-rts/id920754415) | [📱](https://itunes.apple.com/ch/app/play-srf/id638194352) | [📱](https://itunes.apple.com/ch/app/play-swi/id920785201) | +| iOS / tvOS | [📱📺](https://itunes.apple.com/ch/app/play-rsi/id920753497) | [📱📺](https://itunes.apple.com/ch/app/play-rtr/id920754925) | [📱📺](https://itunes.apple.com/ch/app/play-rts/id920754415) | [📱📺](https://itunes.apple.com/ch/app/play-srf/id638194352) | [📱📺](https://itunes.apple.com/ch/app/play-swi/id920785201) | | Android | [🤖](https://play.google.com/store/apps/details?id=ch.rsi.player) | [🤖](https://play.google.com/store/apps/details?id=ch.rtr.player) | [🤖](https://play.google.com/store/apps/details?id=ch.rts.player) | [🤖](https://play.google.com/store/apps/details?id=ch.srf.mobile.srfplayer) | [🤖](https://play.google.com/store/apps/details?id=ch.swissinfo.player) | | Web | [🖥](https://www.rsi.ch/play) | [🖥](https://www.rtr.ch/play) | [🖥](https://www.rts.ch/play) | [🖥](https://www.srf.ch/play) | [🖥](https://www.swissinfo.ch/play) | -![Home](README-images/home.jpg) ![Home](README-images/player.jpg) +![Home](README-images/home.jpg) ![Player](README-images/player.jpg) +![Home TV](README-images/home-tv.jpg) ## Features -The iOS application has a rich set of functionalities, among which: +The application provides a rich set of functionalities, among which: * Access to to our content, whether its is broadcast on TV, radio or online. -* An audio mini player. -* Subscriptions with push notifications. +* An audio mini player (iOS only). +* Subscriptions with push notifications (iOS only). * Resume playback and continuous playback. -* Favorites and downloads. -* AirPlay and Google Cast support. +* Favorites. +* Downloads (iOS only). +* AirPlay and Google Cast support (iOS only). * VoiceOver and larger font support for better accessibility. -* Handoff support. +* Handoff support (iOS only). -Depending on the business unit, some functionalities might not be available (e.g. SWI has no associated radio channel). +Depending on the business unit, some functionalities might not be available (e.g. SWI has no radio content). ## Compatibility -The project runs on iOS 12 and above and must be opened with the latest Xcode version. +The project runs on iOS 12, tvOS 14 and above and must be opened with the latest Xcode version. ## Contributing @@ -44,7 +46,7 @@ Building the project requires command-line tools for icon generation, easily ins brew install imagemagick ghostscript brew install jq -Project dependencies are retrieved using both [Carthage](https://github.com/Carthage/Carthage) and [CocoaPods](https://cocoapods.org/). Be sure that these tools are available on your system. +Some project dependencies are retrieved using [Carthage](https://github.com/Carthage/Carthage) and [CocoaPods](https://cocoapods.org/). Be sure that these tools are available on your system. ## Building the project @@ -56,15 +58,7 @@ make help Commands are available both for internal use (proprietary builds) and for wider use (public builds). -### Proprietary builds - -Dependencies must be updated and built first: - -``` -make bootstrap -``` - -Then open Xcode to build and run the project. +### Private settings Private project settings (keys, tokens, etc.) are stored [in a private repository](https://github.com/SRGSSR/playsrg-configuration-ios), pulled under the `Configuration` directory when running `make setup` (or any other target depending on it). The SHA-1 of the configuration commit which is used is explicitly provided in the `Makefile`. Settings are therefore versioned alongside the project, providing for reproducible builds. @@ -75,26 +69,25 @@ If you need to make changes to the settings: 1. Update the [Makefile](../Makefile) `CONFIGURATION_COMMIT_SHA1` variable to point at the configuration commit to use. 1. Push all commits when you are ready. -### Public builds - -Dependencies must be updated and built first: +If you are part of the SRG SSR organization be sure to setup the project first: ``` -make public.bootstrap +make setup ``` -Then open Xcode to build and run the project. +### Running the project -Public builds differ from proprietary builds in a few areas: +Dependencies must retrieve and built first: -* Some content cannot be played (e.g. TV livestreams or series). -* Analytics are not collected. -* Push notifications are not setup. -* Remote configuration is disabled. +``` +make bootstrap +``` + +Then open Xcode to build and run the project. ## Releasing binaries -The proprietary project uses [fastlane](https://fastlane.tools/) for releasing binaries (either for internal builds or for the AppStore). +The proprietary project uses [fastlane](https://fastlane.tools/) for releasing binaries, either for internal purposes or for the AppStore. ## Specifications diff --git a/docs/REMOTE_CONFIGURATION.md b/docs/REMOTE_CONFIGURATION.md index da23ed28d..215a2a2eb 100755 --- a/docs/REMOTE_CONFIGURATION.md +++ b/docs/REMOTE_CONFIGURATION.md @@ -33,7 +33,8 @@ If a remote configuration is found to be invalid (usually a mandatory parameter ## Analytics -* `comScoreVirtualSite` (mandatory, string): The comScore virtual site to send events to. +* `siteName` (mandatory, string): The iOS site name to send events to. +* `tvSiteName` (mandatory, string): The tvOS site name to send events to. * `netMetrixIdentifier` (mandatory, string): The NetMetrix application identifier. ## Channel configuration @@ -66,12 +67,13 @@ The keys common to both TV and radio channels JSON dictionaries are: * `tvEvents`: Event modules (expand into a row per event). * `tvFavoriteShows`: TV shows added to the favorites. * `tvLatest`: The latest medias. +* `tvWebFirst`: Medias available first on Play. * `tvMostPopular`: The most popular videos. * `tvShowsAccess`: A-Z and By date access buttons. * `tvSoonExpiring`: Soon expiring videos. * `tvTopics`: Topics (expand into a row per topic). * `tvTopicsAccess`: Topic access. -* `tvTrending`: Trending medias and editorial picks. See `tvTrendingEpisodesOnly` and `tvTrendingEditorialLimit` options. +* `tvTrending`: Trending medias and editorial picks. See `tvTrendingEpisodesOnly`, `tvTrendingEditorialLimit` and `tvTrendingPrefersHeroStage` options. ### Topic configuration @@ -86,6 +88,7 @@ The keys common to both TV and radio channels JSON dictionaries are: * `tvFeaturedHomeSectionHeaderHidden` (optional, boolean): If set to `true`, featured TV media lists will not display any header. * `tvTrendingEpisodesOnly` (optional, boolean): If set to `true`, `tvTrending` only returns episodes. * `tvTrendingEditorialLimit` (optional, number): The maximum number of editorial recommendations returned by `tvTrending`. If not set, all are returned. +* `tvTrendingPrefersHeroStage` (optional, boolean): If set to `true`, `tvTrending` returns hero stage content (`tvTrendingEpisodesOnly` and `tvTrendingEditorialLimit` are ignored). ## Audio homepage diff --git a/fastlane/Appfile b/fastlane/Appfile index 840069fe8..2c52822af 100755 --- a/fastlane/Appfile +++ b/fastlane/Appfile @@ -12,6 +12,13 @@ for_platform :ios do team_id "#{ENV['SWI_TEAM_ID']}" end + for_lane :tvOSswiAppStoreUpload do + app_identifier "#{ENV['SWI_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['SWI_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['SWI_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SWI_TEAM_ID']}" + end + for_lane :swiDSYMs do app_identifier "#{ENV['SWI_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['SWI_ITUNES_CONNECT_ID']}" @@ -19,6 +26,13 @@ for_platform :ios do team_id "#{ENV['SWI_TEAM_ID']}" end + for_lane :tvOSswiDSYMs do + app_identifier "#{ENV['SWI_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['SWI_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['SWI_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SWI_TEAM_ID']}" + end + for_lane :swiScreenshots do app_identifier "#{ENV['SWI_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['SWI_ITUNES_CONNECT_ID']}" @@ -26,6 +40,13 @@ for_platform :ios do team_id "#{ENV['SWI_TEAM_ID']}" end + for_lane :tvOSswiScreenshots do + app_identifier "#{ENV['SWI_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['SWI_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['SWI_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SWI_TEAM_ID']}" + end + for_lane :srfAppStoreUpload do app_identifier "#{ENV['SRF_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['SRF_ITUNES_CONNECT_ID']}" @@ -33,6 +54,13 @@ for_platform :ios do team_id "#{ENV['SRF_TEAM_ID']}" end + for_lane :tvOSsrfAppStoreUpload do + app_identifier "#{ENV['SRF_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['SRF_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['SRF_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SRF_TEAM_ID']}" + end + for_lane :srfDSYMs do app_identifier "#{ENV['SRF_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['SRF_ITUNES_CONNECT_ID']}" @@ -40,6 +68,13 @@ for_platform :ios do team_id "#{ENV['SRF_TEAM_ID']}" end + for_lane :tvOSsrfDSYMs do + app_identifier "#{ENV['SRF_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['SRF_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['SRF_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SRF_TEAM_ID']}" + end + for_lane :srfScreenshots do app_identifier "#{ENV['SRF_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['SRF_ITUNES_CONNECT_ID']}" @@ -47,6 +82,13 @@ for_platform :ios do team_id "#{ENV['SRF_TEAM_ID']}" end + for_lane :tvOSsrfScreenshots do + app_identifier "#{ENV['SRF_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['SRF_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['SRF_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SRF_TEAM_ID']}" + end + for_lane :rtsAppStoreUpload do app_identifier "#{ENV['RTS_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RTS_ITUNES_CONNECT_ID']}" @@ -54,6 +96,13 @@ for_platform :ios do team_id "#{ENV['RTS_TEAM_ID']}" end + for_lane :tvOSrtsAppStoreUpload do + app_identifier "#{ENV['RTS_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RTS_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RTS_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RTS_TEAM_ID']}" + end + for_lane :rtsDSYMs do app_identifier "#{ENV['RTS_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RTS_ITUNES_CONNECT_ID']}" @@ -61,6 +110,13 @@ for_platform :ios do team_id "#{ENV['RTS_TEAM_ID']}" end + for_lane :tvOSrtsDSYMs do + app_identifier "#{ENV['RTS_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RTS_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RTS_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RTS_TEAM_ID']}" + end + for_lane :rtsScreenshots do app_identifier "#{ENV['RTS_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RTS_ITUNES_CONNECT_ID']}" @@ -68,6 +124,13 @@ for_platform :ios do team_id "#{ENV['RTS_TEAM_ID']}" end + for_lane :tvOSrtsScreenshots do + app_identifier "#{ENV['RTS_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RTS_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RTS_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RTS_TEAM_ID']}" + end + for_lane :rsiAppStoreUpload do app_identifier "#{ENV['RSI_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RSI_ITUNES_CONNECT_ID']}" @@ -75,6 +138,13 @@ for_platform :ios do team_id "#{ENV['RSI_TEAM_ID']}" end + for_lane :tvOSrsiAppStoreUpload do + app_identifier "#{ENV['RSI_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RSI_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RSI_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RSI_TEAM_ID']}" + end + for_lane :rsiDSYMs do app_identifier "#{ENV['RSI_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RSI_ITUNES_CONNECT_ID']}" @@ -82,6 +152,13 @@ for_platform :ios do team_id "#{ENV['RSI_TEAM_ID']}" end + for_lane :tvOSrsiDSYMs do + app_identifier "#{ENV['RSI_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RSI_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RSI_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RSI_TEAM_ID']}" + end + for_lane :rsiScreenshots do app_identifier "#{ENV['RSI_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RSI_ITUNES_CONNECT_ID']}" @@ -89,6 +166,13 @@ for_platform :ios do team_id "#{ENV['RSI_TEAM_ID']}" end + for_lane :tvOSrsiScreenshots do + app_identifier "#{ENV['RSI_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RSI_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RSI_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RSI_TEAM_ID']}" + end + for_lane :rtrAppStoreUpload do app_identifier "#{ENV['RTR_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RTR_ITUNES_CONNECT_ID']}" @@ -96,6 +180,13 @@ for_platform :ios do team_id "#{ENV['RTR_TEAM_ID']}" end + for_lane :tvOSrtrAppStoreUpload do + app_identifier "#{ENV['RTR_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RTR_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RTR_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RTR_TEAM_ID']}" + end + for_lane :rtrDSYMs do app_identifier "#{ENV['RTR_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RTR_ITUNES_CONNECT_ID']}" @@ -103,6 +194,13 @@ for_platform :ios do team_id "#{ENV['RTR_TEAM_ID']}" end + for_lane :tvOSrtrDSYMs do + app_identifier "#{ENV['RTR_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RTR_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RTR_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RTR_TEAM_ID']}" + end + for_lane :rtrScreenshots do app_identifier "#{ENV['RTR_APP_IDENTIFIER']}" itunes_connect_id "#{ENV['RTR_ITUNES_CONNECT_ID']}" @@ -110,10 +208,24 @@ for_platform :ios do team_id "#{ENV['RTR_TEAM_ID']}" end + for_lane :tvOSrtrScreenshots do + app_identifier "#{ENV['RTR_APP_IDENTIFIER']}" + itunes_connect_id "#{ENV['RTR_ITUNES_CONNECT_ID']}" + apple_dev_portal_id "#{ENV['RTR_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['RTR_TEAM_ID']}" + end + for_lane :tvOSnightlies do itunes_connect_id "#{ENV['SRGSSR_ITUNES_CONNECT_ID']}" itc_team_id "#{ENV['SRGSSR_ITUNES_CONNECT_TEAM_ID']}" apple_dev_portal_id "#{ENV['SRGSSR_APPLE_DEV_PORTAL_ID']}" team_id "#{ENV['SRGSSR_TEAM_ID']}" end + + for_lane :tvOSbetas do + itunes_connect_id "#{ENV['SRGSSR_ITUNES_CONNECT_ID']}" + itc_team_id "#{ENV['SRGSSR_ITUNES_CONNECT_TEAM_ID']}" + apple_dev_portal_id "#{ENV['SRGSSR_APPLE_DEV_PORTAL_ID']}" + team_id "#{ENV['SRGSSR_TEAM_ID']}" + end end diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7c54d88f6..d2a5ff55e 100755 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -22,10 +22,10 @@ platform :ios do platform = lane.to_s.downcase.include?('tvos') ? 'tvOS' : 'iOS' # For betas, before all, check if we have a beta release description - if lane.to_s == 'betas' + if lane.to_s.downcase.include?('betas') what_s_new = what_s_new_for_beta(platform) if what_s_new.empty? - UI.user_error!('Whoops, WhatsNew-beta.json has no release note for ' + tag_version(platform) + '.') + UI.user_error!('Whoops, WhatsNew-' + platform + '-beta.json has no release note for ' + tag_version(platform) + '.') end end @@ -37,7 +37,9 @@ platform :ios do end end - desc 'For each BUs, build a new iOS nightly on App Center' + # Nightlies + + desc 'Builds a new iOS nightly on App Center.' lane :nightlies do platform = 'iOS' @@ -74,7 +76,7 @@ platform :ios do save_last_nightlies_success_git_commit_hash(platform) end - desc 'For each BUs, build a new tvOS nightly on AppStore Connect and wait build processing.' + desc 'Builds a new tvOS nightly on AppStore Connect and waits build processing.' lane :tvOSnightlies do platform = 'tvOS' @@ -89,7 +91,7 @@ platform :ios do tvos_schemes.each_index do |index| app_identifier = appstore_nightly_identifiers[index] - update_app_identifier_to_appstore(app_identifier) + update_app_identifier_to_appstore(platform, app_identifier) build_lane( configuration: 'Nightly', @@ -117,6 +119,7 @@ platform :ios do update_dsyms(app_identifier, platform, appcenter_tv_nightly_names[index], nil) clean_build_artifacts + reset_app_identifier_to_appstore(platform) UI.message tvos_schemes[index] + ' (Nightly ' + build_number + ') delivered. ✅' end @@ -124,7 +127,9 @@ platform :ios do save_last_nightlies_success_git_commit_hash(platform) end - desc 'For each BUs, build a new beta on App Center with the current build number. If we\'re not in a release or hotfix process (master, release/* or hotfix/*), tag the current version on the repository and bump the build number' + # Betas + + desc 'Builds a new iOS beta on App Center with the current build number. If not in a release/hotfix process (master, release/* or hotfix/*), tags the current version, bumps the build number and pushes.' lane :betas do platform = 'iOS' @@ -152,7 +157,59 @@ platform :ios do bump_build_number_beta_workflow(platform) end - desc 'Upload a new build (bitcode) on AppStore Connect with the current build number.' + desc 'Builds a new tvOS beta on AppStore Connect with the current build number and waits build processing. If not in a release/hotfix process (master, release/* or hotfix/*), tags the current version, bumps the build number and pushes.' + lane :tvOSbetas do + platform = 'tvOS' + + notify_build_number_to_ci(platform, nil) + + build_number = xcode_build_number(platform) + + tvos_schemes.each_index do |index| + app_identifier = appstore_beta_identifiers[index] + + can_upload = can_upload_testflight_build(platform, app_identifier) + UI.message(tvos_schemes[index] + ' (Beta ' + build_number + ') already uploaded 🔁✅') unless can_upload + next unless can_upload + + update_app_identifier_to_appstore(platform, app_identifier) + + build_lane( + configuration: 'Beta', + scheme: tvos_schemes[index], + display_name_suffix: ' 🎯', + version_suffix: '-beta', + platform: platform, + export_to_appstore: true + ) + + pilot( + app_identifier: app_identifier, + app_platform: appstore_platform(platform), + changelog: what_s_new_for_beta(platform), + distribute_external: true, + demo_account_required: false, + beta_app_review_info: beta_app_review_info, + groups: ENV['ITUNES_CONNECT_BETA_GROUPS'] + ) + + UI.message 'Wait 90 seconds before asking dSYMs. Not ready yet.' + sleep(90) + + update_dsyms(app_identifier, platform, appcenter_tv_beta_names[index], nil) + + clean_build_artifacts + reset_app_identifier_to_appstore(platform) + + UI.message tvos_schemes[index] + ' (Beta ' + build_number + ') delivered. ✅' + end + + bump_build_number_beta_workflow(platform) + end + + # Prods + + desc 'Uploads a new iOS build on AppStore Connect with the current build number.' lane :appStoreUploads do platform = 'iOS' @@ -165,32 +222,20 @@ platform :ios do sh 'bundle exec fastlane ios rtrAppStoreUpload' end - desc 'SWI: Upload a new build (bitcode) on AppStore Connect with the current build number.' - lane :swiAppStoreUpload do - testflight_lane(platform: 'iOS', scheme: 'Play SWI') - end - - desc 'SRF: Upload a new build (bitcode) on AppStore Connect with the current build number.' - lane :srfAppStoreUpload do - testflight_lane(platform: 'iOS', scheme: 'Play SRF') - end - - desc 'RTS: Upload a new build (bitcode) on AppStore Connect with the current build number.' - lane :rtsAppStoreUpload do - testflight_lane(platform: 'iOS', scheme: 'Play RTS') - end + desc 'Uploads a new tvOS build on AppStore Connect with the current build number.' + lane :tvOSappStoreUploads do + platform = 'tvOS' - desc 'RSI: Upload a new build (bitcode) on AppStore Connect with the current build number.' - lane :rsiAppStoreUpload do - testflight_lane(platform: 'iOS', scheme: 'Play RSI') - end + notify_build_number_to_ci(platform, nil) - desc 'RTR: Upload a new build (bitcode) on AppStore Connect with the current build number.' - lane :rtrAppStoreUpload do - testflight_lane(platform: 'iOS', scheme: 'Play RTR') + sh 'bundle exec fastlane ios tvOSswiAppStoreUpload' + sh 'bundle exec fastlane ios tvOSsrfAppStoreUpload' + sh 'bundle exec fastlane ios tvOSrtsAppStoreUpload' + sh 'bundle exec fastlane ios tvOSrsiAppStoreUpload' + sh 'bundle exec fastlane ios tvOSrtrAppStoreUpload' end - desc 'Send latest dSYMs to App Center. Optional \'version\' or \'min_version\' parameters.' + desc 'Sends latest iOS dSYMs to App Center. Optional \'version\' or \'min_version\' parameters.' lane :dSYMs do |options| min_version = options[:min_version] version = options[:version] @@ -206,77 +251,182 @@ platform :ios do sh 'bundle exec fastlane ios rtrDSYMs' + options end - desc 'SWI: Send latest dSYMs to App Center, with same parameters.' - lane :swiDSYMs do |options| - update_dsyms(nil, nil, ENV['PLAY_SWI_APPSTORE_APPCENTER_APPNAME'], options) - end - - desc 'SFR: Send latest dSYMs to App Center, with same parameters.' - lane :srfDSYMs do |options| - update_dsyms(nil, nil, ENV['PLAY_SRF_APPSTORE_APPCENTER_APPNAME'], options) - end + desc 'Sends latest tvOS dSYMs to App Center. Optional \'version\' or \'min_version\' parameters.' + lane :tvOSdSYMs do |options| + min_version = options[:min_version] + version = options[:version] - desc 'RTS: Send latest dSYMs to App Center, with same parameters.' - lane :rtsDSYMs do |options| - update_dsyms(nil, nil, ENV['PLAY_RTS_APPSTORE_APPCENTER_APPNAME'], options) - end + options = '' + options += ' min_version:' + min_version if min_version + options += ' version:' + version if version - desc 'RSI: Send latest dSYMs to App Center, with same parameters.' - lane :rsiDSYMs do |options| - update_dsyms(nil, nil, ENV['PLAY_RSI_APPSTORE_APPCENTER_APPNAME'], options) + sh 'bundle exec fastlane ios tvOSswiDSYMs' + options + sh 'bundle exec fastlane ios tvOSsrfDSYMs' + options + sh 'bundle exec fastlane ios tvOSrtsDSYMs' + options + sh 'bundle exec fastlane ios tvOSrsiDSYMs' + options + sh 'bundle exec fastlane ios tvOSrtrDSYMs' + options end - desc 'RTR: Send latest dSYMs to App Center, with same parameters.' - lane :rtrDSYMs do |options| - update_dsyms(nil, nil, ENV['PLAY_RTR_APPSTORE_APPCENTER_APPNAME'], options) - end + # Individual iOS screenshots - desc 'SWI: Make screenshots and overwrite on AppStoreConnect.' + desc 'SWI: Makes iOS screenshots and replaces currents on AppStoreConnect.' lane :swiScreenshots do screenshots('Play SWI screenshots', ['en-US']) upload_screenshots(nil) end - desc 'SRF: Make screenshots and overwrite on AppStoreConnect.' + desc 'SRF: Makes iOS screenshots and replaces currents on AppStoreConnect.' lane :srfScreenshots do screenshots('Play SRF screenshots', ['de-DE']) upload_screenshots(nil) end - desc 'RTS: Make screenshots and overwrite on AppStoreConnect.' + desc 'RTS: Makes iOS screenshots and replaces currents on AppStoreConnect.' lane :rtsScreenshots do screenshots('Play RTS screenshots', ['fr-FR']) upload_screenshots(nil) end - desc 'RSI: Make screenshots and overwrite on AppStoreConnect.' + desc 'RSI: Makes iOS screenshots and replaces currents on AppStoreConnect.' lane :rsiScreenshots do screenshots('Play RSI screenshots', ['it']) upload_screenshots(nil) end - desc 'RTR: Make screenshots and overwrite on AppStoreConnect.' + desc 'RTR: Makes iOS screenshots and replaces currents on AppStoreConnect.' lane :rtrScreenshots do screenshots('Play RTR screenshots', ['de-DE']) upload_screenshots(nil) end - desc 'Build, upload to TestFlight' + # Individual iOS build uploads + + desc 'SWI: Uploads a new iOS build on AppStore Connect with the current build number.' + lane :swiAppStoreUpload do + testflight_lane(platform: 'iOS', scheme: 'Play SWI') + end + + desc 'SRF: Uploads a new iOS build on AppStore Connect with the current build number.' + lane :srfAppStoreUpload do + testflight_lane(platform: 'iOS', scheme: 'Play SRF') + end + + desc 'RTS: Uploads a new iOS build on AppStore Connect with the current build number.' + lane :rtsAppStoreUpload do + testflight_lane(platform: 'iOS', scheme: 'Play RTS') + end + + desc 'RSI: Uploads a new iOS build on AppStore Connect with the current build number.' + lane :rsiAppStoreUpload do + testflight_lane(platform: 'iOS', scheme: 'Play RSI') + end + + desc 'RTR: Uploads a new iOS build on AppStore Connect with the current build number.' + lane :rtrAppStoreUpload do + testflight_lane(platform: 'iOS', scheme: 'Play RTR') + end + + # Individual latest iOS dSYMs uploads + + desc 'SWI: Sends latest iOS dSYMs to App Center, with same parameters as \'dSYMs\'.' + lane :swiDSYMs do |options| + update_dsyms(nil, 'iOS', ENV['PLAY_SWI_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'SFR: Sends latest iOS dSYMs to App Center, with same parameters as \'dSYMs\'.' + lane :srfDSYMs do |options| + update_dsyms(nil, 'iOS', ENV['PLAY_SRF_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'RTS: Sends latest iOS dSYMs to App Center, with same parameters as \'dSYMs\'.' + lane :rtsDSYMs do |options| + update_dsyms(nil, 'iOS', ENV['PLAY_RTS_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'RSI: Sends latest iOS dSYMs to App Center, with same parameters as \'dSYMs\'.' + lane :rsiDSYMs do |options| + update_dsyms(nil, 'iOS', ENV['PLAY_RSI_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'RTR: Sends latest iOS dSYMs to App Center, with same parameters as \'dSYMs\'.' + lane :rtrDSYMs do |options| + update_dsyms(nil, 'iOS', ENV['PLAY_RTR_APPSTORE_APPCENTER_APPNAME'], options) + end + + # Individual tvOS build uploads + + desc 'SWI: Uploads a new tvOS build on AppStore Connect with the current build number.' + lane :tvOSswiAppStoreUpload do + testflight_lane(platform: 'tvOS', scheme: 'Play SWI TV') + end + + desc 'SRF: Uploads a new tvOS build on AppStore Connect with the current build number.' + lane :tvOSsrfAppStoreUpload do + testflight_lane(platform: 'tvOS', scheme: 'Play SRF TV') + end + + desc 'RTS: Uploads a new tvOS build on AppStore Connect with the current build number.' + lane :tvOSrtsAppStoreUpload do + testflight_lane(platform: 'tvOS', scheme: 'Play RTS TV') + end + + desc 'RSI: Uploads a new tvOS build on AppStore Connect with the current build number.' + lane :tvOSrsiAppStoreUpload do + testflight_lane(platform: 'tvOS', scheme: 'Play RSI TV') + end + + desc 'RTR: Uploads a new tvOS build on AppStore Connect with the current build number.' + lane :tvOSrtrAppStoreUpload do + testflight_lane(platform: 'tvOS', scheme: 'Play RTR TV') + end + + # Individual latest tvOS dSYMs uploads + + desc 'SWI: Sends latest tvOS dSYMs to App Center, with same parameters as \'tvOSdSYMs\'.' + lane :tvOSswiDSYMs do |options| + update_dsyms(nil, 'tvOS', ENV['PLAY_SWI_TV_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'SFR: Sends latest tvOS dSYMs to App Center, with same parameters as \'tvOSdSYMs\'.' + lane :tvOSsrfDSYMs do |options| + update_dsyms(nil, 'tvOS', ENV['PLAY_SRF_TV_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'RTS: Sends latest tvOS dSYMs to App Center, with same parameters as \'tvOSdSYMs\'.' + lane :tvOSrtsDSYMs do |options| + update_dsyms(nil, 'tvOS', ENV['PLAY_RTS_TV_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'RSI: Sends latest tvOS dSYMs to App Center, with same parameters as \'tvOSdSYMs\'.' + lane :tvOSrsiDSYMs do |options| + update_dsyms(nil, 'tvOS', ENV['PLAY_RSI_TV_APPSTORE_APPCENTER_APPNAME'], options) + end + + desc 'RTR: Sends latest tvOS dSYMs to App Center, with same parameters as \'tvOSdSYMs\'.' + lane :tvOSrtrDSYMs do |options| + update_dsyms(nil, 'tvOS', ENV['PLAY_RTR_TV_APPSTORE_APPCENTER_APPNAME'], options) + end + + # Private lanes + + desc 'Builds and uploads to TestFlight' private_lane :testflight_lane do |options| options[:platform] ||= 'iOS' build_number = xcode_build_number(options[:platform]) - can_upload = can_upload_testflight_build(options[:platform]) + can_upload = can_upload_testflight_build(options[:platform], nil) UI.message(options[:scheme] + ' (' + options[:platform] + ') ' + build_number + ' already uploaded 🔁✅') unless can_upload next unless can_upload build_lane( configuration: 'AppStore', scheme: options[:scheme], + platform: options[:platform], export_to_appstore: true ) pilot( + app_platform: appstore_platform(options[:platform]), skip_waiting_for_build_processing: true ) UI.message(options[:scheme] + ' (' + options[:platform] + ') ' + build_number + ' uploaded ✅') @@ -284,7 +434,7 @@ platform :ios do clean_build_artifacts end - desc 'Build for a scheme and a configuration' + desc 'Builds for a scheme and a configuration' private_lane :build_lane do |options| options[:export_to_appstore] ||= false options[:display_name_suffix] ||= '' @@ -314,7 +464,7 @@ platform :ios do ) end - desc 'Upload a build on App Center or just a dSYM file.' + desc 'Uploads a build on App Center or just a dSYM file' private_lane :appcenter_lane do |options| options[:notify_testers] ||= false @@ -403,6 +553,14 @@ def appcenter_beta_names ENV['PLAY_RTR_BETA_APPCENTER_APPNAME']] end +def appcenter_tv_beta_names + [ENV['PLAY_SWI_TV_BETA_APPCENTER_APPNAME'], + ENV['PLAY_RTS_TV_BETA_APPCENTER_APPNAME'], + ENV['PLAY_SRF_TV_BETA_APPCENTER_APPNAME'], + ENV['PLAY_RSI_TV_BETA_APPCENTER_APPNAME'], + ENV['PLAY_RTR_TV_BETA_APPCENTER_APPNAME']] +end + def appstore_nightly_identifiers [ENV['ITUNES_CONNECT_NIGHLTY_SWI_APP_IDENTIFIER'], ENV['ITUNES_CONNECT_NIGHLTY_RTS_APP_IDENTIFIER'], @@ -411,6 +569,14 @@ def appstore_nightly_identifiers ENV['ITUNES_CONNECT_NIGHLTY_RTR_APP_IDENTIFIER']] end +def appstore_beta_identifiers + [ENV['ITUNES_CONNECT_BETA_SWI_APP_IDENTIFIER'], + ENV['ITUNES_CONNECT_BETA_RTS_APP_IDENTIFIER'], + ENV['ITUNES_CONNECT_BETA_SRF_APP_IDENTIFIER'], + ENV['ITUNES_CONNECT_BETA_RSI_APP_IDENTIFIER'], + ENV['ITUNES_CONNECT_BETA_RTR_APP_IDENTIFIER']] +end + # Returns current tag version def tag_version(platform) xcode_marketing_version(platform) + '-' + xcode_build_number(platform) @@ -418,7 +584,7 @@ end # Returns the what's new text from WhatsNew-beta.json file in the repository def what_s_new_for_beta(platform) - file = File.open('../WhatsNew-beta.json', 'r') # TODO: by platform + file = File.open('../WhatsNew-' + platform + '-beta.json', 'r') json = JSON.parse(file.read) file.close what_s_new = json[tag_version(platform)] @@ -545,11 +711,21 @@ def xcodebuid_grep_output(output) output.split(/\n/) [line_count - 1] end -# Update only the demo app identifier for App Store Connect submit. -def update_app_identifier_to_appstore(app_identifier) - update_app_identifier( - plist_path: 'TV Application/TV-Application-Info.plist', - app_identifier: app_identifier +# Update only the app identifier for App Store Connect submit. +def update_app_identifier_to_appstore(platform, app_identifier) + update_xcconfig_value( + path: 'Xcode/Play ' + platform + ' Application.xcconfig', + name: 'PRODUCT_BUNDLE_IDENTIFIER', + value: app_identifier + ) + app_identifier +end + +# Reset the updated app identifier after App Store Connect submission. +def reset_app_identifier_to_appstore(platform) + reset_git_repo( + force: true, + files: ['Xcode/Play ' + platform + ' Application.xcconfig'] ) end @@ -655,10 +831,14 @@ def upload_screenshots(platform) ) end -def can_upload_testflight_build(platform) +def can_upload_testflight_build(platform, app_identifier) platform ||= 'iOS' + app_identifier ||= app_config.try_fetch_value(:app_identifier) - appstore_build_number = latest_testflight_build_number(platform: appstore_platform(platform)) + appstore_build_number = latest_testflight_build_number( + platform: appstore_platform(platform), + app_identifier: app_identifier + ) local_build_number = xcode_build_number(platform) (appstore_build_number < local_build_number.to_i) @@ -676,6 +856,7 @@ def bump_build_number_beta_workflow(platform) add_git_tag(tag: platform.downcase + '/' + tag_version(platform)) bump_build_number_commit(platform) + git_pull(rebase: true) push_to_git_remote end diff --git a/fastlane/README.md b/fastlane/README.md index 094f11b58..ce855ae0e 100755 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -20,102 +20,167 @@ or alternatively using `brew install fastlane` ``` fastlane ios nightlies ``` -For each BUs, build a new iOS nightly on App Center +Builds a new iOS nightly on App Center. ### ios tvOSnightlies ``` fastlane ios tvOSnightlies ``` -For each BUs, build a new tvOS nightly on AppStore Connect and wait build processing. +Builds a new tvOS nightly on AppStore Connect and waits build processing. ### ios betas ``` fastlane ios betas ``` -For each BUs, build a new beta on App Center with the current build number. If we're not in a release or hotfix process (master, release/* or hotfix/*), tag the current version on the repository and bump the build number +Builds a new iOS beta on App Center with the current build number. If not in a release/hotfix process (master, release/* or hotfix/*), tags the current version, bumps the build number and pushes. +### ios tvOSbetas +``` +fastlane ios tvOSbetas +``` +Builds a new tvOS beta on AppStore Connect with the current build number and waits build processing. If not in a release/hotfix process (master, release/* or hotfix/*), tags the current version, bumps the build number and pushes. ### ios appStoreUploads ``` fastlane ios appStoreUploads ``` -Upload a new build (bitcode) on AppStore Connect with the current build number. +Uploads a new iOS build on AppStore Connect with the current build number. +### ios tvOSappStoreUploads +``` +fastlane ios tvOSappStoreUploads +``` +Uploads a new tvOS build on AppStore Connect with the current build number. +### ios dSYMs +``` +fastlane ios dSYMs +``` +Sends latest iOS dSYMs to App Center. Optional 'version' or 'min_version' parameters. +### ios tvOSdSYMs +``` +fastlane ios tvOSdSYMs +``` +Sends latest tvOS dSYMs to App Center. Optional 'version' or 'min_version' parameters. +### ios swiScreenshots +``` +fastlane ios swiScreenshots +``` +SWI: Makes iOS screenshots and replaces currents on AppStoreConnect. +### ios srfScreenshots +``` +fastlane ios srfScreenshots +``` +SRF: Makes iOS screenshots and replaces currents on AppStoreConnect. +### ios rtsScreenshots +``` +fastlane ios rtsScreenshots +``` +RTS: Makes iOS screenshots and replaces currents on AppStoreConnect. +### ios rsiScreenshots +``` +fastlane ios rsiScreenshots +``` +RSI: Makes iOS screenshots and replaces currents on AppStoreConnect. +### ios rtrScreenshots +``` +fastlane ios rtrScreenshots +``` +RTR: Makes iOS screenshots and replaces currents on AppStoreConnect. ### ios swiAppStoreUpload ``` fastlane ios swiAppStoreUpload ``` -SWI: Upload a new build (bitcode) on AppStore Connect with the current build number. +SWI: Uploads a new iOS build on AppStore Connect with the current build number. ### ios srfAppStoreUpload ``` fastlane ios srfAppStoreUpload ``` -SRF: Upload a new build (bitcode) on AppStore Connect with the current build number. +SRF: Uploads a new iOS build on AppStore Connect with the current build number. ### ios rtsAppStoreUpload ``` fastlane ios rtsAppStoreUpload ``` -RTS: Upload a new build (bitcode) on AppStore Connect with the current build number. +RTS: Uploads a new iOS build on AppStore Connect with the current build number. ### ios rsiAppStoreUpload ``` fastlane ios rsiAppStoreUpload ``` -RSI: Upload a new build (bitcode) on AppStore Connect with the current build number. +RSI: Uploads a new iOS build on AppStore Connect with the current build number. ### ios rtrAppStoreUpload ``` fastlane ios rtrAppStoreUpload ``` -RTR: Upload a new build (bitcode) on AppStore Connect with the current build number. -### ios dSYMs -``` -fastlane ios dSYMs -``` -Send latest dSYMs to App Center. Optional 'version' or 'min_version' parameters. +RTR: Uploads a new iOS build on AppStore Connect with the current build number. ### ios swiDSYMs ``` fastlane ios swiDSYMs ``` -SWI: Send latest dSYMs to App Center, with same parameters. +SWI: Sends latest iOS dSYMs to App Center, with same parameters as 'dSYMs'. ### ios srfDSYMs ``` fastlane ios srfDSYMs ``` -SFR: Send latest dSYMs to App Center, with same parameters. +SFR: Sends latest iOS dSYMs to App Center, with same parameters as 'dSYMs'. ### ios rtsDSYMs ``` fastlane ios rtsDSYMs ``` -RTS: Send latest dSYMs to App Center, with same parameters. +RTS: Sends latest iOS dSYMs to App Center, with same parameters as 'dSYMs'. ### ios rsiDSYMs ``` fastlane ios rsiDSYMs ``` -RSI: Send latest dSYMs to App Center, with same parameters. +RSI: Sends latest iOS dSYMs to App Center, with same parameters as 'dSYMs'. ### ios rtrDSYMs ``` fastlane ios rtrDSYMs ``` -RTR: Send latest dSYMs to App Center, with same parameters. -### ios swiScreenshots +RTR: Sends latest iOS dSYMs to App Center, with same parameters as 'dSYMs'. +### ios tvOSswiAppStoreUpload ``` -fastlane ios swiScreenshots +fastlane ios tvOSswiAppStoreUpload ``` -SWI: Make screenshots and overwrite on AppStoreConnect. -### ios srfScreenshots +SWI: Uploads a new tvOS build on AppStore Connect with the current build number. +### ios tvOSsrfAppStoreUpload ``` -fastlane ios srfScreenshots +fastlane ios tvOSsrfAppStoreUpload ``` -SRF: Make screenshots and overwrite on AppStoreConnect. -### ios rtsScreenshots +SRF: Uploads a new tvOS build on AppStore Connect with the current build number. +### ios tvOSrtsAppStoreUpload ``` -fastlane ios rtsScreenshots +fastlane ios tvOSrtsAppStoreUpload ``` -RTS: Make screenshots and overwrite on AppStoreConnect. -### ios rsiScreenshots +RTS: Uploads a new tvOS build on AppStore Connect with the current build number. +### ios tvOSrsiAppStoreUpload ``` -fastlane ios rsiScreenshots +fastlane ios tvOSrsiAppStoreUpload ``` -RSI: Make screenshots and overwrite on AppStoreConnect. -### ios rtrScreenshots +RSI: Uploads a new tvOS build on AppStore Connect with the current build number. +### ios tvOSrtrAppStoreUpload ``` -fastlane ios rtrScreenshots +fastlane ios tvOSrtrAppStoreUpload +``` +RTR: Uploads a new tvOS build on AppStore Connect with the current build number. +### ios tvOSswiDSYMs +``` +fastlane ios tvOSswiDSYMs +``` +SWI: Sends latest tvOS dSYMs to App Center, with same parameters as 'tvOSdSYMs'. +### ios tvOSsrfDSYMs +``` +fastlane ios tvOSsrfDSYMs +``` +SFR: Sends latest tvOS dSYMs to App Center, with same parameters as 'tvOSdSYMs'. +### ios tvOSrtsDSYMs +``` +fastlane ios tvOSrtsDSYMs +``` +RTS: Sends latest tvOS dSYMs to App Center, with same parameters as 'tvOSdSYMs'. +### ios tvOSrsiDSYMs +``` +fastlane ios tvOSrsiDSYMs +``` +RSI: Sends latest tvOS dSYMs to App Center, with same parameters as 'tvOSdSYMs'. +### ios tvOSrtrDSYMs +``` +fastlane ios tvOSrtrDSYMs ``` -RTR: Make screenshots and overwrite on AppStoreConnect. +RTR: Sends latest tvOS dSYMs to App Center, with same parameters as 'tvOSdSYMs'. ---- diff --git a/license_plist.yml b/license_plist.yml index 72b5d38ef..aae501e23 100755 --- a/license_plist.yml +++ b/license_plist.yml @@ -1,6 +1,8 @@ # LicensePlist configuration file (https://github.com/mono0926/LicensePlist). exclude: - - Fingertips - - FLEX - - srgcontentprotection-ios \ No newline at end of file + - FetchImage # For tvOS builds + - Fingertips # For development builds + - FLEX # For development builds + - Nuke # For tvOS builds + - TvOSTextViewer # For tvOS builds \ No newline at end of file