diff --git a/Application/Application-Info.plist b/Application/Application-Info.plist index 367b5e4fe..78f64e93a 100755 --- a/Application/Application-Info.plist +++ b/Application/Application-Info.plist @@ -242,5 +242,7 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown + UIUserInterfaceStyle + Dark diff --git a/Application/Resources/Data/parsePlayUrl.js b/Application/Resources/Data/parsePlayUrl.js index dbedfd104..df9ac4f7c 100644 --- a/Application/Resources/Data/parsePlayUrl.js +++ b/Application/Resources/Data/parsePlayUrl.js @@ -1,60 +1,20 @@ // parsePlayUrl -var parsePlayUrlVersion = 19; +var parsePlayUrlVersion = 22; var parsePlayUrlBuild = "mmf"; -function parsePlayUrl(urlString) { - var url = urlString; - try { - url = new URL(urlString); - } - catch(error) { - console.log("Can't read URL: " + error); - return null; - } - - var queryParams = {}; - for (var queryItem of url.searchParams) { - queryParams[queryItem[0]] = queryItem[1]; - } - - return parseForPlayApp(url.protocol.replace(':', ''), url.hostname, url.pathname, queryParams, url.hash.replace('#', '')); +if(! console) { + var console = { + log:function(){} + } } function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { - // fix path issue pathname = pathname.replace("//", "/"); // Get BU - var bu = null; - switch (true) { - case hostname.endsWith("tp.srgssr.ch") || hostname.endsWith("player.rts.ch") || hostname.endsWith("player.rsi.ch") || hostname.endsWith("player.rtr.ch") || hostname.endsWith("player.swissinfo.ch") || hostname.endsWith("player.srf.ch"): - bu = "tp"; - break; - case hostname.includes("rts.ch") || hostname.includes("srgplayer-rts") || (hostname.includes("play-mmf") && pathname.startsWith("/rts/")): - bu = "rts"; - break; - case hostname.includes("rsi.ch") || hostname.includes("srgplayer-rsi") || (hostname.includes("play-mmf") && pathname.startsWith("/rsi/")): - bu = "rsi"; - break; - case hostname.includes("rtr.ch") || hostname.includes("srgplayer-rtr") || (hostname.includes("play-mmf") && pathname.startsWith("/rtr/")): - bu = "rtr"; - break; - case hostname.includes("swissinfo.ch") || hostname.includes("srgplayer-swi") || (hostname.includes("play-mmf") && pathname.startsWith("/swi/")): - bu = "swi"; - break; - case hostname.includes("srf.ch") || hostname.includes("srgplayer-srf") || (hostname.includes("play-mmf") && pathname.startsWith("/srf/")): - bu = "srf"; - break; - case hostname.includes("play-mmf") && pathname.startsWith("/mmf/"): - bu = "mmf"; - break; - case hostname.includes("radioswisspop.ch") || hostname.includes("radioswissclassic.ch") || hostname.includes("radioswissjazz.ch"): - bu = "radioswiss"; - break; - } - + var bu = getBuFromHostname(hostname,pathname); if (! bu) { console.log("This URL is not a Play SRG URL."); return null; @@ -91,7 +51,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { redirectBu = "swi"; break; } - var startTime = queryParams["start"]; + var startTime = queryParams["start"]; return openMediaURN(server, redirectBu, mediaURN, startTime); } } @@ -135,7 +95,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { } // Returns default TV homepage - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } if (hostname.includes("play-mmf") && ! pathname.startsWith("/mmf/")) { @@ -163,7 +123,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { } else if (pathname.startsWith("/video")) { // Returns default TV homepage - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } else { var channelId = null; @@ -186,7 +146,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { } } // Returns default radio homepage - return openPage(server, bu, "radio:home", channelId, null); + return openRadioHomePage(server, bu, channelId); } } @@ -213,7 +173,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { if (mediaType) { var mediaId = queryParams["id"]; if (mediaId) { - var startTime = queryParams["startTime"]; + var startTime = queryParams["startTime"]; return openMedia(server, bu, mediaType, mediaId, startTime); } else { @@ -238,7 +198,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { if (mediaType) { var mediaId = pathname.substr(pathname.lastIndexOf('/') + 1); if (mediaId) { - var startTime = queryParams["startTime"]; + var startTime = queryParams["startTime"]; return openMedia(server, bu, mediaType, mediaId, startTime); } else { @@ -258,7 +218,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { } else { // Returns default TV homepage - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } } @@ -334,7 +294,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { } else { // Returns default radio homepage - return openPage(server, bu, "radio:home", null, null); + return openRadioHomePage(server, bu, null); } } @@ -350,7 +310,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { } else { // Returns default TV homepage - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } } @@ -409,7 +369,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { * Ex: https://www.srf.ch/play/tv */ if (pathname.endsWith("/tv")) { - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } /** @@ -419,7 +379,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { */ if (pathname.endsWith("/radio")) { var channelId = queryParams["station"]; - return openPage(server, bu, "radio:home", channelId, null); + return openRadioHomePage(server, bu,channelId); } /** @@ -433,8 +393,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { index = index.toLowerCase(); index = (index.length > 1) ? null : index; } - var options = new Array( { key: "index", value: index } ); - return openPage(server, bu, "tv:az", null, options); + return openAtoZ(server, bu, null, index); } /** @@ -449,8 +408,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { index = index.toLowerCase(); index = (index.length > 1) ? null : index; } - var options = new Array( { key: "index", value: index } ); - return openPage(server, bu, "radio:az", channelId, options); + return openRadioAtoZ(server, bu, channelId, index); } /** @@ -464,14 +422,13 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { // Returns an ISO format var dateArray = date.split("-"); if (dateArray.length == 3 && dateArray[2].length == 4 && dateArray[1].length == 2 && dateArray[0].length == 2) { - date = dateArray[2] + "-" + dateArray[1] + "-" + dateArray[0]; + date = dateArray[2] + "-" + dateArray[1] + "-" + dateArray[0]; } else { date = null; } } - var options = new Array( { key: "date", value: date } ); - return openPage(server, bu, "tv:bydate", null, options); + return openByDate(server, bu, null, date); } /** @@ -486,14 +443,13 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { // Returns an ISO format var dateArray = date.split("-"); if (dateArray.length == 3 && dateArray[2].length == 4 && dateArray[1].length == 2 && dateArray[0].length == 2) { - date = dateArray[2] + "-" + dateArray[1] + "-" + dateArray[0]; + date = dateArray[2] + "-" + dateArray[1] + "-" + dateArray[0]; } else { date = null; } } - var options = new Array( { key: "date", value: date } ); - return openPage(server, bu, "radio:bydate", channelId, options); + return openRadioByDate(server, bu, channelId, date); } /** @@ -505,16 +461,13 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { if (pathname.endsWith("/suche") || pathname.endsWith("/recherche") || pathname.endsWith("/ricerca") || pathname.endsWith("/retschertga") || pathname.endsWith("/search")) { var query = queryParams["query"]; var mediaType = queryParams["mediaType"]; - var transmission = null; if (mediaType) { mediaType = mediaType.toLowerCase(); if (mediaType != "video" && mediaType != "audio") { mediaType = null; } } - var transmissionComponent = (transmission != null) ? transmission + ":" : ""; - var options = new Array( { key: "query", value: query }, { key: "mediaType", value: mediaType } ); - return openPage(server, bu, transmissionComponent + "search", null, options); + return openSearch(server, bu, query, mediaType); } /** @@ -523,10 +476,10 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { * Ex: https://www.rts.ch/play/tv/categories/info */ if (pathname.endsWith("/tv/themen") || pathname.endsWith("/tv/categories") || pathname.endsWith("/tv/categorie") || pathname.endsWith("/tv/tematicas") || pathname.endsWith("/tv/topics")) { - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } else if (pathname.includes("/tv/themen") || pathname.includes("/tv/categories") || pathname.includes("/tv/categorie") || pathname.includes("/tv/tematicas") || pathname.includes("/tv/topics")) { - var lastPathComponent = pathname.split("/").slice(-1)[0]; + var lastPathComponent = pathname.split("/").slice(-1)[0]; var topicId = null; @@ -540,7 +493,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { return openTopic(server, bu, "tv", topicId); } else { - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } } @@ -551,10 +504,10 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { *. Ex: https://www.rsi.ch/play/tv/event/event-playrsi-8858482 */ if (pathname.endsWith("/tv/event")) { - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } else if (pathname.includes("/tv/event")) { - var lastPathComponent = pathname.split("/").slice(-1)[0]; + var lastPathComponent = pathname.split("/").slice(-1)[0]; var eventId = null; @@ -568,7 +521,7 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { return openModule(server, bu, "event", eventId); } else { - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); } } @@ -579,85 +532,149 @@ function parseForPlayApp(scheme, hostname, pathname, queryParams, anchor) { *. Ex: https://www.rsi.ch/play */ if (pathname.endsWith("/play/") || pathname.endsWith("/play")) { - return openPage(server, bu, "tv:home", null, null); + return openTvHomePage(server,bu); + } + + /** + * Catch play help urls + * + * Ex: https://www.srf.ch/play/tv/hilfe + * Ex: https://www.rts.ch/play/tv/aide + * Ex: https://www.rsi.ch/play/tv/guida + * Ex: https://www.rtr.ch/play/tv/agid + * Ex: https://play.swissinfo.ch/play/tv/help + */ + if (pathname.endsWith("/hilfe") || pathname.endsWith("/aide") || pathname.endsWith("/guida") || pathname.endsWith("/agid") || pathname.endsWith("/help")) { + return openURL(server, bu, scheme, hostname, pathname, queryParams, anchor); } // Redirect fallback. console.log("Can't parse Play URL. Unsupported URL."); return schemeForBu(bu) + "://unsupported?server=" + server; }; + +// ---- Open functions + function openMedia(server, bu, mediaType, mediaId, startTime) { - var redirect = schemeForBu(bu) + "://open?media=urn:" + bu + ":" + mediaType + ":" + mediaId; - if (startTime) { - redirect = redirect + "&start-time=" + startTime; - } - if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); - } - return redirect; + var urn="urn:" + bu + ":" + mediaType + ":" + mediaId; + return openMediaURN(server,bu,urn,startTime); } function openMediaURN(server, bu, mediaURN, startTime) { - var redirect = schemeForBu(bu) + "://open?media=" + mediaURN; + var options = {}; if (startTime) { - redirect = redirect + "&start-time=" + startTime; + options['start_time'] = startTime; } if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); + options['server'] = server; } - return redirect; + return buildBuUri(bu,"media",mediaURN,options); } function openShow(server, bu, showTransmission, showId) { - var redirect = schemeForBu(bu) + "://open?show=urn:" + bu + ":show:" + showTransmission + ":" + showId; + var showUrn="urn:" + bu + ":show:" + showTransmission + ":" + showId; + var options = {}; if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); + options['server'] = server; } - return redirect; + return buildBuUri(bu,"show",showUrn,options); } function openTopic(server, bu, topicTransmission, topicId) { - var redirect = schemeForBu(bu) + "://open?topic=urn:" + bu + ":topic:" + topicTransmission + ":" + topicId; + var topicUrn="urn:" + bu + ":topic:" + topicTransmission + ":" + topicId; + var options = {}; if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); + options['server'] = server; } - return redirect; + return buildBuUri(bu,"topic",topicUrn,options); } function openModule(server, bu, moduleType, moduleId) { - var redirect = schemeForBu(bu) + "://open?module=urn:" + bu + ":module:" + moduleType + ":" + moduleId; + var topicUrn="urn:" + bu + ":module:" + moduleType + ":" + moduleId; + var options = {}; if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); + options['server'] = server; } - return redirect; + return buildBuUri(bu,"module",topicUrn,options); } -function openPage(server, bu, page, channelId, options) { - if (! page) { - page = "tv:home"; - } +function openTvHomePage(server,bu){ + var options = {}; + if (server) { + options['server'] = server; + } + return buildBuUri(bu,"home",null,options); +} - var pageUid = page.split(":").slice(-1)[0]; +function openRadioHomePage(server,bu,channelId){ + if (!channelId) { + channelId = primaryChannelUidForBu(bu); + } + var options={}; + if(channelId){ + options["channel_id"] = channelId; + } + if (server) { + options['server'] = server; + } + return buildBuUri(bu,"home",null,options); +} - if (page.startsWith("radio:") && ! channelId) { - channelId = primaryChannelUidForBu(bu); - } - - var redirect = schemeForBu(bu) + "://open?page=urn:" + bu + ":page:" + page + "&page-id=" + pageUid; - if (channelId) { - redirect = redirect + "&channel-id=" + channelId; - } - if (options && Array.isArray(options)) { - options.forEach(function(option) { - if (option.key && option.value) { - redirect = redirect + "&" + option.key + "=" + encodeURIComponent(option.value); - } - }); - } - if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); - } - return redirect; +function openAtoZ(server,bu,channelId,index){ + var options = {}; + if(channelId) { + options['channel_id'] = channelId; + } + if(index) { + options['index'] = index; + } + if (server) { + options['server'] = server; + } + return buildBuUri(bu,"az",null,options); +} + +function openRadioAtoZ(server,bu,channelId,index){ + if (!channelId) { + channelId = primaryChannelUidForBu(bu); + } + return openAtoZ(server,bu,channelId,index); +} + +function openByDate(server,bu,channelId,date){ + var options = {}; + if(channelId) { + options['channel_id'] = channelId; + } + + if(date) { + options['date'] = date; + } + if (server) { + options['server'] = server; + } + return buildBuUri(bu, "bydate", null, options); +} + +function openRadioByDate(server,bu,channelId,date) { + if (!channelId) { + channelId = primaryChannelUidForBu(bu); + } + return openByDate(server,bu,channelId,date); +} + +function openSearch(server, bu, query, mediaType){ + var options = {}; + if(query) { + options['query'] = query; + } + if(mediaType) { + options['media_type'] = mediaType; + } + if (server) { + options['server'] = server; + } + return buildBuUri(bu,"search", null, options); } function openURL(server, bu, scheme, hostname, pathname, queryParams, anchor) { @@ -681,14 +698,16 @@ function openURL(server, bu, scheme, hostname, pathname, queryParams, anchor) { } var url = scheme + "://" + hostname + pathname + queryParamsString + anchorString; - - var redirect = schemeForBu(bu) + "://open?url=" + encodeURIComponent(url); - if (server) { - redirect = redirect + "&server=" + encodeURIComponent(server); - } - return redirect; + var options = {}; + options['url'] = url; + if (server) { + options['server'] = server; + } + return buildBuUri(bu,"link",null,options) } +// --- parsing functions + function primaryChannelUidForBu(bu) { switch (bu) { case "srf": @@ -747,21 +766,68 @@ function serverForUrl(hostname, pathname, queryParams) { server = "play mmf"; } else { - var server = queryParams["server"]; - switch (server) { - case "production": - server = "production"; - break; + var serverParam = queryParams["server"]; + switch (serverParam) { case "stage": server = "stage"; break; case "test": server = "test"; break; - default: - server = null; } } } return server; } + +function getBuFromHostname(hostname, pathname) { + switch (true) { + case hostname.endsWith("tp.srgssr.ch") || hostname.endsWith("player.rts.ch") || hostname.endsWith("player.rsi.ch") || hostname.endsWith("player.rtr.ch") || hostname.endsWith("player.swissinfo.ch") || hostname.endsWith("player.srf.ch"): + return "tp"; + case hostname.includes("rts.ch") || hostname.includes("srgplayer-rts") || (hostname.includes("play-mmf") && pathname.startsWith("/rts/")): + return "rts"; + case hostname.includes("rsi.ch") || hostname.includes("srgplayer-rsi") || (hostname.includes("play-mmf") && pathname.startsWith("/rsi/")): + return "rsi"; + case hostname.includes("rtr.ch") || hostname.includes("srgplayer-rtr") || (hostname.includes("play-mmf") && pathname.startsWith("/rtr/")): + return "rtr"; + case hostname.includes("swissinfo.ch") || hostname.includes("srgplayer-swi") || (hostname.includes("play-mmf") && pathname.startsWith("/swi/")): + return "swi"; + case hostname.includes("srf.ch") || hostname.includes("srgplayer-srf") || (hostname.includes("play-mmf") && pathname.startsWith("/srf/")): + return "srf"; + case hostname.includes("play-mmf") && pathname.startsWith("/mmf/"): + return "mmf"; + case hostname.includes("radioswisspop.ch") || hostname.includes("radioswissclassic.ch") || hostname.includes("radioswissjazz.ch"): + return "radioswiss"; + } + return null; + } + +/** +* Build scheme://host[/path][?queryParams[0]&...&queryParams[n-1]] +* Sample: +* playrts://media/urn:xxx?position=0&server=mmf +*/ +function buildUri(scheme, host, path, queryParams) { + var uri = scheme + "://" + host; + if (path) { + uri = uri + "/" + path; + } + if (queryParams && queryParams !== {}) { + uri = uri + "?"; + var optionIndex = 0; + for (var option in queryParams) { + if(queryParams[option]) { + if(optionIndex > 0) { + uri = uri + "&"; + } + uri = uri + option + "=" + encodeURIComponent(queryParams[option]); + optionIndex++; + } + } + } + return uri; +} + +function buildBuUri(bu, host, path, queryParams) { + return buildUri(schemeForBu(bu), host, path, queryParams); +} 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 7203fd36f..c02124ff6 100755 --- a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt +++ b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt @@ -474,7 +474,7 @@ name: Mantle, nameSpecified: , owner: Mantle, version: 2.1.0 name: Masonry, nameSpecified: , owner: SRGSSR, version: v1.1.0_srg1 -name: srganalytics-ios, nameSpecified: , owner: SRGSSR, version: 3.7.4 +name: srganalytics-ios, nameSpecified: , owner: SRGSSR, version: 3.7.5 name: srgappearance-ios, nameSpecified: , owner: SRGSSR, version: 1.1.10 @@ -482,9 +482,9 @@ name: srgdataprovider-ios, nameSpecified: , owner: SRGSSR, version: 6.8 name: srgdiagnostics-ios, nameSpecified: , owner: SRGSSR, version: 1.0.1 -name: srgidentity-ios, nameSpecified: , owner: SRGSSR, version: 1.0.4 +name: srgidentity-ios, nameSpecified: , owner: SRGSSR, version: 1.0.5 -name: srgletterbox-ios, nameSpecified: , owner: SRGSSR, version: 1.14.4 +name: srgletterbox-ios, nameSpecified: , owner: SRGSSR, version: 1.14.5 name: srglogger-ios, nameSpecified: , owner: SRGSSR, version: 1.1 @@ -492,7 +492,7 @@ name: srgmediaplayer-ios, nameSpecified: , owner: SRGSSR, version: 2.8 name: srgnetwork-ios, nameSpecified: , owner: SRGSSR, version: 1.0.3 -name: srguserdata-ios, nameSpecified: , owner: SRGSSR, version: 1.1.3 +name: srguserdata-ios, nameSpecified: , owner: SRGSSR, version: 1.1.4 name: tagcommander-ios, nameSpecified: , owner: SRGSSR, version: 4.3.3_4.3.1 diff --git a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist index b3505ff29..224816c7e 100755 --- a/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist +++ b/Application/Resources/Settings.bundle/com.mono0926.LicensePlist.plist @@ -224,7 +224,7 @@ File com.mono0926.LicensePlist/srganalytics-ios Title - srganalytics-ios (3.7.4) + srganalytics-ios (3.7.5) Type PSChildPaneSpecifier @@ -256,7 +256,7 @@ File com.mono0926.LicensePlist/srgidentity-ios Title - srgidentity-ios (1.0.4) + srgidentity-ios (1.0.5) Type PSChildPaneSpecifier @@ -264,7 +264,7 @@ File com.mono0926.LicensePlist/srgletterbox-ios Title - srgletterbox-ios (1.14.4) + srgletterbox-ios (1.14.5) Type PSChildPaneSpecifier @@ -296,7 +296,7 @@ File com.mono0926.LicensePlist/srguserdata-ios Title - srguserdata-ios (1.1.3) + srguserdata-ios (1.1.4) Type PSChildPaneSpecifier diff --git a/Application/Sources/Application/PlayAppDelegate.m b/Application/Sources/Application/PlayAppDelegate.m index c285bca47..489762fd6 100755 --- a/Application/Sources/Application/PlayAppDelegate.m +++ b/Application/Sources/Application/PlayAppDelegate.m @@ -261,28 +261,34 @@ - (void)application:(UIApplication *)application performActionForShortcutItem:(U } // See URL_SCHEMES.md -// Open [scheme]://open?media=[media_urn] (optional query parameters: channel-id=[channel_id], start-time=[start_position_seconds]) -// Open [scheme]://open?show=[show_urn] (optional query parameter: channel-id=[channel_id]) -// Open [scheme]://open?topic=[topic_urn] -// Open [scheme]://open?module=[module_urn] -// Open [scheme]://open?page-id=[home] (optional query parameters: channel-id=[channel_id]) -// Open [scheme]://open?page-id=[az] (optional query parameters: channel-id=[channel_id], index=[index_letter]) -// Open [scheme]://open?page-id=[bydate] (optional query parameters: channel-id=[channel_id], date=[date] with format yyyy-MM-dd) -// Open [scheme]://open?page-id=[search] (optional query parameters: query=[query], mediaType=[audio|video]) -// Open [scheme]://open?url=[url] -// Open [scheme]://[play website url] (use "parsePlayUrl.js" to attempt transforming the URL) +// Open [scheme]://media/[media_urn] (optional query parameters: channel_id=[channel_id], start_time=[start_position_seconds]) +// Open [scheme]://show/[show_urn] (optional query parameter: channel_id=[channel_id]) +// Open [scheme]://topic/[topic_urn] +// Open [scheme]://module/[module_urn] +// Open [scheme]://home (optional query parameters: channel_id=[channel_id]) +// Open [scheme]://az (optional query parameters: channel_id=[channel_id], index=[index_letter]) +// Open [scheme]://bydate (optional query parameters: channel_id=[channel_id], date=[date] with format yyyy-MM-dd) +// Open [scheme]://search (optional query parameters: query=[query], media_type=[audio|video]) +// Open [scheme]://link?url=[url] +// Open [scheme]://[play_website_url] (use "parsePlayUrl.js" to attempt transforming the URL) - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(NSDictionary *)options { AnalyticsSource analyticsSource = ([URL.scheme isEqualToString:@"http"] || [URL.scheme isEqualToString:@"https"]) ? AnalyticsSourceDeepLink : AnalyticsSourceSchemeURL; - NSURLComponents *URLComponents = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:NO]; - if (! [URLComponents.host.lowercaseString isEqualToString:@"open"]) { + + NSArray *supportedActions = @[ DeeplinkActionMedia, DeeplinkActionShow, DeeplinkActionTopic, DeeplinkActionModule, + DeeplinkActionHome, DeeplinkActionAZ, DeeplinkActionByDate, DeeplinkActionSearch, + DeeplinkActionLink ]; + + NSURLComponents *URLComponents = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:YES]; + if (! [supportedActions containsObject:URLComponents.host.lowercaseString]) { NSURL *deepLinkURL = [DeepLinkService.sharedService schemeURLFromWebURL:URL]; if (deepLinkURL) { - URLComponents = [NSURLComponents componentsWithURL:deepLinkURL resolvingAgainstBaseURL:NO]; + URLComponents = [NSURLComponents componentsWithURL:deepLinkURL resolvingAgainstBaseURL:YES]; } } - if ([URLComponents.host.lowercaseString isEqualToString:@"open"]) { + if ([supportedActions containsObject:URLComponents.host.lowercaseString]) { + DeeplinkAction action = URLComponents.host.lowercaseString; #if defined(DEBUG) || defined(NIGHTLY) || defined(BETA) NSString *server = [self valueFromURLComponents:URLComponents withParameterName:@"server"]; @@ -300,10 +306,10 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(N } #endif - NSString *mediaURN = [self valueFromURLComponents:URLComponents withParameterName:@"media"]; - if (mediaURN) { - NSString *channelUid = [self valueFromURLComponents:URLComponents withParameterName:@"channel-id"]; - NSInteger startTime = [[self valueFromURLComponents:URLComponents withParameterName:@"start-time"] integerValue]; + NSString *mediaURN = URLComponents.path.lastPathComponent; + if ([action isEqualToString:DeeplinkActionMedia] && mediaURN) { + NSString *channelUid = [self valueFromURLComponents:URLComponents withParameterName:@"channel_id"]; + NSInteger startTime = [[self valueFromURLComponents:URLComponents withParameterName:@"start_time"] integerValue]; [self openMediaWithURN:mediaURN startTime:startTime channelUid:channelUid fromPushNotification:NO completionBlock:^{ SRGAnalyticsHiddenEventLabels *labels = [[SRGAnalyticsHiddenEventLabels alloc] init]; labels.source = analyticsSource; @@ -315,9 +321,9 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(N return YES; } - NSString *showURN = [self valueFromURLComponents:URLComponents withParameterName:@"show"]; - if (showURN) { - NSString *channelUid = [self valueFromURLComponents:URLComponents withParameterName:@"channel-id"]; + NSString *showURN = URLComponents.path.lastPathComponent; + if ([action isEqualToString:DeeplinkActionShow] && showURN) { + NSString *channelUid = [self valueFromURLComponents:URLComponents withParameterName:@"channel_id"]; [self openShowWithURN:showURN channelUid:channelUid fromPushNotification:NO completionBlock:^{ SRGAnalyticsHiddenEventLabels *labels = [[SRGAnalyticsHiddenEventLabels alloc] init]; labels.source = analyticsSource; @@ -329,8 +335,8 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(N return YES; } - NSString *topicURN = [self valueFromURLComponents:URLComponents withParameterName:@"topic"]; - if (topicURN) { + NSString *topicURN = URLComponents.path.lastPathComponent; + if ([action isEqualToString:DeeplinkActionTopic] && topicURN) { [self openTopicWithURN:topicURN completionBlock:^{ SRGAnalyticsHiddenEventLabels *labels = [[SRGAnalyticsHiddenEventLabels alloc] init]; labels.source = analyticsSource; @@ -342,8 +348,8 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(N return YES; } - NSString *moduleURN = [self valueFromURLComponents:URLComponents withParameterName:@"module"]; - if (moduleURN) { + NSString *moduleURN = URLComponents.path.lastPathComponent; + if ([action isEqualToString:DeeplinkActionModule] && moduleURN) { [self openModuleWithURN:moduleURN completionBlock:^{ SRGAnalyticsHiddenEventLabels *labels = [[SRGAnalyticsHiddenEventLabels alloc] init]; labels.source = analyticsSource; @@ -355,14 +361,14 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(N return YES; } - NSString *pageUid = [self valueFromURLComponents:URLComponents withParameterName:@"page-id"]; - if (pageUid) { - NSString *channelUid = [self valueFromURLComponents:URLComponents withParameterName:@"channel-id"]; - [self openPageWithUid:pageUid channelUid:channelUid URLComponents:URLComponents completionBlock:^{ + NSArray *pageActions = @[ DeeplinkActionHome, DeeplinkActionAZ, DeeplinkActionByDate, DeeplinkActionSearch ]; + if ([pageActions containsObject:action]) { + NSString *channelUid = [self valueFromURLComponents:URLComponents withParameterName:@"channel_id"]; + [self openPageWithAction:action channelUid:channelUid URLComponents:URLComponents completionBlock:^{ SRGAnalyticsHiddenEventLabels *labels = [[SRGAnalyticsHiddenEventLabels alloc] init]; labels.source = analyticsSource; labels.type = AnalyticsTypeActionDisplayPage; - labels.value = pageUid; + labels.value = action; labels.extraValue1 = options[UIApplicationOpenURLOptionsSourceApplicationKey]; [SRGAnalyticsTracker.sharedTracker trackHiddenEventWithName:AnalyticsTitleOpenURL labels:labels]; }]; @@ -371,7 +377,7 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(N NSString *URLString = [self valueFromURLComponents:URLComponents withParameterName:@"url"]; NSURL *URL = (URLString) ? [NSURL URLWithString:URLString] : nil; - if (URL) { + if ([action isEqualToString:DeeplinkActionLink] && URL) { [UIApplication.sharedApplication play_openURL:URL withCompletionHandler:^(BOOL success) { SRGAnalyticsHiddenEventLabels *labels = [[SRGAnalyticsHiddenEventLabels alloc] init]; labels.source = analyticsSource; @@ -501,20 +507,20 @@ - (void)openHomeWithChannelUid:(NSString *)channelUid completionBlock:(void (^)( [self resetWithMenuItemInfo:menuItemInfo completionBlock:completionBlock]; } -- (void)openPageWithUid:(NSString *)pageUid channelUid:(NSString *)channelUid URLComponents:(NSURLComponents *)URLComponents completionBlock:(void (^)(void))completionBlock +- (void)openPageWithAction:(DeeplinkAction)action channelUid:(NSString *)channelUid URLComponents:(NSURLComponents *)URLComponents completionBlock:(void (^)(void))completionBlock { - NSParameterAssert(pageUid); + NSParameterAssert(action); - if ([pageUid isEqualToString:@"az"]) { + if ([action isEqualToString:DeeplinkActionAZ]) { NSString *index = [self valueFromURLComponents:URLComponents withParameterName:@"index"]; [self openShowListAtIndex:index withChannelUid:channelUid completionBlock:completionBlock]; } - else if ([pageUid isEqualToString:@"bydate"]) { + else if ([action isEqualToString:DeeplinkActionByDate]) { NSString *dateString = [self valueFromURLComponents:URLComponents withParameterName:@"date"]; NSDate *date = dateString ? [NSDateFormatter.play_URLOptionDateFormatter dateFromString:dateString] : nil; [self openCalendarAtDate:date withChannelUid:channelUid completionBlock:completionBlock]; } - else if ([pageUid isEqualToString:@"search"]) { + else if ([action isEqualToString:DeeplinkActionSearch]) { NSString *query = [self valueFromURLComponents:URLComponents withParameterName:@"query"]; static NSDictionary *s_mediaTypes; @@ -524,12 +530,12 @@ - (void)openPageWithUid:(NSString *)pageUid channelUid:(NSString *)channelUid UR @"audio" : @(SRGMediaTypeAudio) }; }); - NSString *mediaTypeName = [self valueFromURLComponents:URLComponents withParameterName:@"mediaType"]; + NSString *mediaTypeName = [self valueFromURLComponents:URLComponents withParameterName:@"media_type"]; SRGMediaType mediaType = s_mediaTypes[mediaTypeName].integerValue; [self openSearchWithQuery:query mediaType:mediaType completionBlock:completionBlock]; } - else if ([pageUid isEqualToString:@"home"]) { + else if ([action isEqualToString:DeeplinkActionHome]) { [self openHomeWithChannelUid:channelUid completionBlock:completionBlock]; } } diff --git a/Application/Sources/Helpers/DeepLinkService.h b/Application/Sources/Helpers/DeepLinkService.h index d7b3370fd..af1a175c7 100755 --- a/Application/Sources/Helpers/DeepLinkService.h +++ b/Application/Sources/Helpers/DeepLinkService.h @@ -8,6 +8,21 @@ NS_ASSUME_NONNULL_BEGIN +typedef NSString * DeeplinkAction NS_STRING_ENUM; + +/** + * Actions + */ +OBJC_EXPORT DeeplinkAction const DeeplinkActionMedia; +OBJC_EXPORT DeeplinkAction const DeeplinkActionShow; +OBJC_EXPORT DeeplinkAction const DeeplinkActionTopic; +OBJC_EXPORT DeeplinkAction const DeeplinkActionModule; +OBJC_EXPORT DeeplinkAction const DeeplinkActionHome; +OBJC_EXPORT DeeplinkAction const DeeplinkActionAZ; +OBJC_EXPORT DeeplinkAction const DeeplinkActionByDate; +OBJC_EXPORT DeeplinkAction const DeeplinkActionSearch; +OBJC_EXPORT DeeplinkAction const DeeplinkActionLink; + /** * Service responsible for retrieving the deep link conversion file, and to convert web URLs into scheme URLs. */ diff --git a/Application/Sources/Helpers/DeepLinkService.m b/Application/Sources/Helpers/DeepLinkService.m index 58ef4ab0b..f0c9a0200 100755 --- a/Application/Sources/Helpers/DeepLinkService.m +++ b/Application/Sources/Helpers/DeepLinkService.m @@ -18,6 +18,16 @@ NSString * const DeepLinkDiagnosticsServiceName = @"DeepLinkDiagnosticsServiceName"; +DeeplinkAction const DeeplinkActionMedia = @"media"; +DeeplinkAction const DeeplinkActionShow = @"show"; +DeeplinkAction const DeeplinkActionTopic = @"topic"; +DeeplinkAction const DeeplinkActionModule = @"module"; +DeeplinkAction const DeeplinkActionHome = @"home"; +DeeplinkAction const DeeplinkActionAZ = @"az"; +DeeplinkAction const DeeplinkActionByDate = @"bydate"; +DeeplinkAction const DeeplinkActionSearch = @"search"; +DeeplinkAction const DeeplinkActionLink = @"link"; + @interface DeepLinkService () @property (nonatomic, weak) SRGRequest *request; @@ -135,7 +145,7 @@ - (void)updateDeepLinkScript { if ([FXReachability sharedInstance].reachable && !self.request.running) { NSURL *middlewareURL = ApplicationConfiguration.sharedApplicationConfiguration.middlewareURL; - NSURL *URL = [NSURL URLWithString:@"api/v1/deeplink/parsePlayUrl.js" relativeToURL:middlewareURL]; + NSURL *URL = [NSURL URLWithString:@"api/v2/deeplink/parsePlayUrl.js" relativeToURL:middlewareURL]; SRGRequest *request = [SRGRequest dataRequestWithURLRequest:[NSURLRequest requestWithURL:URL] session:NSURLSession.sharedSession completionBlock:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (data) { diff --git a/Application/Sources/Search/SearchViewController.h b/Application/Sources/Search/SearchViewController.h index 6af9b8e1b..bf6127992 100755 --- a/Application/Sources/Search/SearchViewController.h +++ b/Application/Sources/Search/SearchViewController.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN * Create a search view controller with optional query and settings. * * @param query The query. - * @param settings The search settings. Only set if the application can use them (@see `searchSettingsDisabled` in `ApplicationConfiguration`). + * @param settings The search settings. Only used if search settings is enabled (@see `searchSettingsDisabled` in `ApplicationConfiguration`). */ - (instancetype)initWithQuery:(nullable NSString *)query settings:(nullable SRGMediaSearchSettings *)settings; diff --git a/Application/Sources/Search/SearchViewController.m b/Application/Sources/Search/SearchViewController.m index c170edf5e..accbbe07f 100755 --- a/Application/Sources/Search/SearchViewController.m +++ b/Application/Sources/Search/SearchViewController.m @@ -47,7 +47,9 @@ @implementation SearchViewController + (BOOL)containsAdvancedSettings:(SRGMediaSearchSettings *)settings { - NSParameterAssert(settings); + if (! settings) { + return NO; + } SRGMediaSearchSettings *defaultSettings = SearchSettingsViewController.defaultSettings; defaultSettings.aggregationsEnabled = NO; @@ -61,11 +63,16 @@ - (instancetype)initWithQuery:(NSString *)query settings:(SRGMediaSearchSettings if (self = [super init]) { self.query = query; + // A BU supporting aggregation but not displaying search settings can lead to longer response times. + // (@see `-mediasForVendor:matchingQuery:withSettings:completionBlock:` in `SRGDataProvider`). ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; if (! applicationConfiguration.searchSettingsDisabled) { self.settings = settings ?: SearchSettingsViewController.defaultSettings; self.settings.aggregationsEnabled = NO; } + else { + self.settings = nil; + } } return self; } @@ -333,7 +340,8 @@ - (UIViewController *)previewContextViewController - (void)updateSearchSettingsButton { - if (self.settings) { + ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; + if (! applicationConfiguration.searchSettingsDisabled) { UIButton *filtersButton = [UIButton buttonWithType:UIButtonTypeCustom]; [filtersButton addTarget:self action:@selector(showSettings:) forControlEvents:UIControlEventTouchUpInside]; @@ -363,20 +371,6 @@ - (void)updateSearchSettingsButton } } -#pragma mark Settings management - -- (SRGMediaType)mediaTypeForScopeButtonIndex:(NSInteger)index -{ - static dispatch_once_t s_onceToken; - static NSDictionary *s_mediaTypes; - dispatch_once(&s_onceToken, ^{ - s_mediaTypes = @{ @0 : @(SRGMediaTypeNone), - @1 : @(SRGMediaTypeVideo), - @2 : @(SRGMediaTypeAudio) }; - }); - return [s_mediaTypes[@(index)] integerValue]; -} - #pragma mark Search - (void)search @@ -552,7 +546,12 @@ - (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementa if (self.items != 0) { ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; if (applicationConfiguration.searchSettingsDisabled) { - headerView.title = NSLocalizedString(@"Videos", @"Header for video search results"); + if (applicationConfiguration.radioChannels.count == 0) { + headerView.title = NSLocalizedString(@"Videos", @"Header for video search results"); + } + else { + headerView.title = NSLocalizedString(@"Videos and audios", @"Header for video and audio search results"); + } } else { static dispatch_once_t s_onceToken; @@ -673,7 +672,9 @@ - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NS - (void)showSettings:(id)sender { - SearchSettingsViewController *searchSettingsViewController = [[SearchSettingsViewController alloc] initWithQuery:self.query settings:self.settings]; + [self.searchController.searchBar resignFirstResponder]; + + SearchSettingsViewController *searchSettingsViewController = [[SearchSettingsViewController alloc] initWithQuery:self.query settings:self.settings ?: SearchSettingsViewController.defaultSettings]; searchSettingsViewController.delegate = self; UIColor *backgroundColor = (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) ? UIColor.play_popoverGrayColor : nil; @@ -721,10 +722,6 @@ - (void)updateSearchResultsForSearchController:(UISearchController *)searchContr NSTimeInterval delay = (searchBar.text.length == 0) ? 0. : kTypingSpeedThreshold; [self performSelector:@selector(search) withObject:nil afterDelay:delay inModes:@[ NSRunLoopCommonModes ]]; } - // Instantaneous search triggered when the selected scope button changed - else if ([self mediaTypeForScopeButtonIndex:searchBar.selectedScopeButtonIndex] != self.settings.mediaType) { - [self search]; - } } #pragma mark UIScrollViewDelegate protocol diff --git a/Application/Sources/UI/Controllers/SideMenuController.m b/Application/Sources/UI/Controllers/SideMenuController.m index 106585700..3709a174c 100755 --- a/Application/Sources/UI/Controllers/SideMenuController.m +++ b/Application/Sources/UI/Controllers/SideMenuController.m @@ -363,10 +363,9 @@ - (void)setSelectedMenuItemInfo:(MenuItemInfo *)selectedMenuItemInfo animated:(B switch (selectedMenuItemInfo.menuItem) { case MenuItemSearch: { NSString *query = selectedMenuItemInfo.options[MenuItemOptionSearchQueryKey]; - SRGMediaType mediaType = [selectedMenuItemInfo.options[MenuItemOptionSearchMediaTypeOptionKey] integerValue]; - SRGMediaSearchSettings *settings = [[SRGMediaSearchSettings alloc] init]; - settings.mediaType = mediaType; + settings.mediaType = [selectedMenuItemInfo.options[MenuItemOptionSearchMediaTypeOptionKey] integerValue]; + viewController = [[SearchViewController alloc] initWithQuery:query settings:settings]; break; } diff --git a/Cartfile b/Cartfile index 829114d5e..6b8ecb58d 100755 --- a/Cartfile +++ b/Cartfile @@ -2,5 +2,5 @@ github "defagos/CoconutKit" "3.3.8" github "Flipboard/FLEX" "6d489e72c52401839386ee41aeefe1f5105c212e" github "mapbox/Fingertips" "cdffabac5506103a2c7cc5aedeed4021df2501da" github "SRGSSR/DZNEmptyDataSet" "v1.8.1_srg1" -github "SRGSSR/srgletterbox-ios" "1.14.4" -github "SRGSSR/srguserdata-ios" "1.1.3" \ No newline at end of file +github "SRGSSR/srgletterbox-ios" "1.14.5" +github "SRGSSR/srguserdata-ios" "1.1.4" \ No newline at end of file diff --git a/Cartfile.resolved.proprietary b/Cartfile.resolved.proprietary index eb682b098..1b80c22a8 100755 --- a/Cartfile.resolved.proprietary +++ b/Cartfile.resolved.proprietary @@ -8,17 +8,17 @@ github "SRGSSR/Masonry" "v1.1.0_srg1" github "SRGSSR/UICKeyChainStore" "v2.1.2_srg1" github "SRGSSR/YYWebImage" "1.0.5_srg1" github "SRGSSR/libextobjc" "0.6_srg1" -github "SRGSSR/srganalytics-ios" "3.7.4" +github "SRGSSR/srganalytics-ios" "3.7.5" github "SRGSSR/srgappearance-ios" "1.1.10" github "SRGSSR/srgcontentprotection-ios" "1.2.3" github "SRGSSR/srgdataprovider-ios" "6.8" github "SRGSSR/srgdiagnostics-ios" "1.0.1" -github "SRGSSR/srgidentity-ios" "1.0.4" -github "SRGSSR/srgletterbox-ios" "1.14.4" +github "SRGSSR/srgidentity-ios" "1.0.5" +github "SRGSSR/srgletterbox-ios" "1.14.5" github "SRGSSR/srglogger-ios" "1.1" github "SRGSSR/srgmediaplayer-ios" "2.8" github "SRGSSR/srgnetwork-ios" "1.0.3" -github "SRGSSR/srguserdata-ios" "1.1.3" +github "SRGSSR/srguserdata-ios" "1.1.4" github "SRGSSR/tagcommander-ios" "4.3.3_4.3.1" github "defagos/CoconutKit" "3.3.8" github "mapbox/Fingertips" "cdffabac5506103a2c7cc5aedeed4021df2501da" diff --git a/Cartfile.resolved.public b/Cartfile.resolved.public index 807c6599f..0b28bf172 100755 --- a/Cartfile.resolved.public +++ b/Cartfile.resolved.public @@ -8,17 +8,17 @@ github "SRGSSR/Masonry" "v1.1.0_srg1" github "SRGSSR/UICKeyChainStore" "v2.1.2_srg1" github "SRGSSR/YYWebImage" "1.0.5_srg1" github "SRGSSR/libextobjc" "0.6_srg1" -github "SRGSSR/srganalytics-ios" "3.7.4" +github "SRGSSR/srganalytics-ios" "3.7.5" github "SRGSSR/srgappearance-ios" "1.1.10" github "SRGSSR/srgcontentprotection-fake-ios" "1.2.3" github "SRGSSR/srgdataprovider-ios" "6.8" github "SRGSSR/srgdiagnostics-ios" "1.0.1" -github "SRGSSR/srgidentity-ios" "1.0.4" -github "SRGSSR/srgletterbox-ios" "1.14.4" +github "SRGSSR/srgidentity-ios" "1.0.5" +github "SRGSSR/srgletterbox-ios" "1.14.5" github "SRGSSR/srglogger-ios" "1.1" github "SRGSSR/srgmediaplayer-ios" "2.8" github "SRGSSR/srgnetwork-ios" "1.0.3" -github "SRGSSR/srguserdata-ios" "1.1.3" +github "SRGSSR/srguserdata-ios" "1.1.4" github "SRGSSR/tagcommander-ios" "4.3.3_4.3.1" github "defagos/CoconutKit" "3.3.8" github "mapbox/Fingertips" "cdffabac5506103a2c7cc5aedeed4021df2501da" diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index a3a9d6a5a..a62109e07 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -2617,13 +2617,6 @@ path = Helpers; sourceTree = ""; }; - 0865F60F223C7506007DE03B /* Recovered References */ = { - isa = PBXGroup; - children = ( - ); - name = "Recovered References"; - sourceTree = ""; - }; 0865F616223C7684007DE03B /* Data */ = { isa = PBXGroup; children = ( @@ -2697,7 +2690,6 @@ E65311E31D3E6FD100B4B8BB /* Frameworks */, 45D67F958D75253E27BCDE9C /* Pods */, 08C68D501D38D49600BB8AAA /* Products */, - 0865F60F223C7506007DE03B /* Recovered References */, ); sourceTree = ""; }; @@ -5224,7 +5216,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes: string srfplayer${MARKETING_VERSION_SUFFIX}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Info.plist\""; + shellScript = "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes: string srfplayer${MARKETING_VERSION_SUFFIX}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Info.plist\"\n"; }; 6FE1B4A01DCBC8270094D5BA /* Embed Debugging Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -6695,7 +6687,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 313; + CURRENT_PROJECT_VERSION = 317; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -6714,7 +6706,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 2.9.5; + MARKETING_VERSION = 2.9.6; MARKETING_VERSION_SUFFIX = "-debug"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -6763,7 +6755,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 313; + CURRENT_PROJECT_VERSION = 317; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -6776,7 +6768,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 2.9.5; + MARKETING_VERSION = 2.9.6; MARKETING_VERSION_SUFFIX = ""; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -6829,7 +6821,7 @@ CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/Application/Application.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = JA876HZNN2; DOMAIN = "*.srf.ch"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6888,7 +6880,7 @@ CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/Application/Application.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = 8779C367VK; DOMAIN = "*.rts.ch"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6947,7 +6939,7 @@ CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/Application/Application.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = Y7342J76KH; DOMAIN = "*.rsi.ch"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7006,7 +6998,7 @@ CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/Application/Application.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = ABW92QGFZ7; DOMAIN = "*.rtr.ch"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7063,7 +7055,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/Application/Application.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = R36WF3S37T; DOMAIN = "*.swissinfo.ch"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7118,7 +7110,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 313; + CURRENT_PROJECT_VERSION = 317; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -7132,7 +7124,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 2.9.5; + MARKETING_VERSION = 2.9.6; MARKETING_VERSION_SUFFIX = "-beta"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -7325,7 +7317,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 313; + CURRENT_PROJECT_VERSION = 317; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -7339,7 +7331,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 2.9.5; + MARKETING_VERSION = 2.9.6; MARKETING_VERSION_SUFFIX = "-nightly"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -7569,7 +7561,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = JA876HZNN2; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -7796,7 +7788,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = 8779C367VK; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -8023,7 +8015,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = Y7342J76KH; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -8250,7 +8242,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = ABW92QGFZ7; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -8475,7 +8467,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = VMGRRW6SG7; + DEVELOPMENT_TEAM = R36WF3S37T; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( diff --git a/WhatsNew-beta.json b/WhatsNew-beta.json index c4d71cec6..ffd4d707e 100755 --- a/WhatsNew-beta.json +++ b/WhatsNew-beta.json @@ -89,5 +89,9 @@ "2.9.4-310": "- Fix filters incorrectly set when opening the search from the left menu.", "2.9.4-311": "- New search period filters in search.\n- Channel logo in TV livestream player page.\n- Application version in settings view and iOS Settings application.\n- Date and time spelling improvements for accessibility.", "2.9.4-312": "- The player track selection interface has been updated.\n- iOS 13 preliminary support.", - "2.9.5-313": "- New RTS TV logos." + "2.9.5-313": "- New RTS TV logos.", + "2.9.6-314": "- Search filters popup on iPad tiny update.\n- New Play scheme URL supported.", + "2.9.6-315": "- Dark mode support for iOS 13 (always).", + "2.9.6-316": "- AppStore release for iOS 13.", + "2.9.6-317": "- AppStore release for iOS 13 (Xcode GM Seed 2)" } \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index c010f7ea2..3affa7039 100755 --- a/docs/README.md +++ b/docs/README.md @@ -31,7 +31,7 @@ Depending on the business unit, some functionalities might not be available (e.g ## Compatibility -The project runs on iOS 9 and above and must be opened with the latest Xcode version (currently Xcode 10). +The project runs on iOS 9 and above and must be opened with the latest Xcode version. ## Contributing diff --git a/docs/URL_SCHEMES.md b/docs/URL_SCHEMES.md index 325ab64fb..f1d6a360f 100755 --- a/docs/URL_SCHEMES.md +++ b/docs/URL_SCHEMES.md @@ -2,7 +2,7 @@ Play applications can be opened with a custom URL scheme having the following format: `play(srf|rts|rsi|rtr|swi)(-beta|-nightly|-debug)`. -## Actions +## Actions (v2 seen 2.9.6 release) The application supports Apple universal links. Replacing the `http`or `https` scheme of a Play website URL with the corresponding application scheme yields a link which can be opened with the application: @@ -10,18 +10,17 @@ The application supports Apple universal links. Replacing the `http`or `https` s The available actions are: -* Open a media within the player: `[scheme]://open?media=[media_urn]`. An optional `&start-time=[start_time]` parameter can be added to start VOD / AOD playback at the specified position in second. -* Open a show page: `[scheme]://open?show=[show_urn]`. -* Open a topic page: `[scheme]://open?topic=[topic_urn]`. -* Open a module page: `[scheme]://open?module=[module_urn]`. -* Open a page: `[scheme]://open?page-id=[page_id]`. - * Home page: `home`. - * Search page: `search`. Optional `query` and `mediaType` (with `video` or `audio` values) parameters can be provided. - * Shows A to Z page: `az`. An optional `index` single character parameter can be provided to open the page at the specified index. - * Shows by date page: `bydate`. An optional `date` parameter with the `yyyy-MM-dd` format can be provided. -* Open a URL: `[scheme]://open?url=[url]`. +* Open a media within the player: `[scheme]://media/[media_urn]`. An optional `&start_time=[start_time]` parameter can be added to start VOD / AOD playback at the specified position in second. +* Open a show page: `[scheme]://show/[show_urn]`. +* Open a topic page: `[scheme]://topic/[topic_urn]`. +* Open a module page: `[scheme]://module/[module_urn]`. +* Open a home page: `[scheme]://home`. +* Open shows A to Z page: `[scheme]://az`. An optional `index` single character parameter can be provided to open the page at the specified index. +* Open shows by date page: `[scheme]://bydate`. An optional `date` parameter with the `yyyy-MM-dd` format can be provided. +* Open search page: `[scheme]://search`. Optional `query` and `media_type` (with `video` or `audio` values) parameters can be provided. +* Open a URL: `[scheme]://link?url=[url]`. -For media, show and page links, an optional `&channel-id=[channel_id]` parameter can be added, which resets the homepage to the specified radio channel homepage. If this parameter is not specified or does not match a valid channel, the homepage is reset to the TV one instead. +For media, show and page links, an optional `&channel_id=[channel_id]` parameter can be added, which resets the homepage to the specified radio channel homepage. If this parameter is not specified or does not match a valid channel, the homepage is reset to the TV one instead. For a `Debug`, `Nightly` or a `Beta` build, `&server=[server_title]` parameter can be added to force a server selection update. The available server list can be found under *Settings* > *Advanced features* > *Server*. @@ -37,32 +36,32 @@ Open one of the following links on a mobile device to open the corresponding ite | Item | Type | Production link | Beta link | Nightly link | Debug link | |:--:|:--:|:--:|:--:|:--:|:--:| -| Telegiornale (2018/10/03) | Video | `playrsi://open?media=urn:rsi:video:10889069` | `playrsi-beta://open?media=urn:rsi:video:10889069` | `playrsi-nightly://open?media=urn:rsi:video:10889069` | `playrsi-debug://open?media=urn:rsi:video:10889069` | -| Radiogiornale | Radio show | `playrsi://open?show=urn:rsi:show:radio:2100980` | `playrsi-beta://open?show=urn:rsi:show:radio:2100980` | `playrsi-nightly://open?show=urn:rsi:show:radio:2100980` | `playrsi-debug://open?show=urn:rsi:show:radio:2100980` | +| Telegiornale (2018/10/03) | Video | `playrsi://media/urn:rsi:video:10889069` | `playrsi-beta://media/urn:rsi:video:10889069` | `playrsi-nightly://media/urn:rsi:video:10889069` | `playrsi-debug://media/urn:rsi:video:10889069` | +| Radiogiornale | Radio show | `playrsi://show/urn:rsi:show:radio:2100980` | `playrsi-beta://show/urn:rsi:show:radio:2100980` | `playrsi-nightly://show/urn:rsi:show:radio:2100980` | `playrsi-debug://show/urn:rsi:show:radio:2100980` | ### RTR | Item | Type | Production link | Beta link | Nightly link | Debug link | |:--:|:--:|:--:|:--:|:--:|:--:| -| Telesguard (2018/10/03) | Video | `playrtr://open?media=urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | `playrtr-beta://open?media=urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | `playrtr-nightly://open?media=urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | `playrtr-debug://open?media=urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | -| Gratulaziuns | Radio show | `playrtr://open?show=urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | `playrtr-beta://open?show=urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | `playrtr-nightly://open?show=urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | `playrtr-debug://open?show=urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | +| Telesguard (2018/10/03) | Video | `playrtr://media/urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | `playrtr-beta://media/urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | `playrtr-nightly://media/urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | `playrtr-debug://media/urn:rtr:video:8e0c23b1-5ea6-463a-b48e-7d474158e992` | +| Gratulaziuns | Radio show | `playrtr://show/urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | `playrtr-beta://show/urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | `playrtr-nightly://show/urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | `playrtr-debug://show/urn:rtr:show:radio:a8b76055-1621-4d01-88c9-421e2ac14828` | ### RTS | Item | Type | Production link | Beta link | Nightly link | Debug link | |:--:|:--:|:--:|:--:|:--:|:--:| -| 19h30 (2018/10/03) | Video | `playrts://open?media=urn:rts:video:9890897` | `playrts-beta://open?media=urn:rts:video:9890897` | `playrts-nightly://open?media=urn:rts:video:9890897` | `playrts-debug://open?urn=urn:rts:video:9890897` | -| Sexomax | Radio show | `playrts://open?show=urn:rts:show:radio:8864883` | `playrts-beta://open?show=urn:rts:show:radio:8864883` | `playrts-nightly://open?show=urn:rts:show:radio:8864883` | `playrts-debug://open?show=urn:rts:show:radio:8864883` | +| 19h30 (2018/10/03) | Video | `playrts://media/urn:rts:video:9890897` | `playrts-beta://media/urn:rts:video:9890897` | `playrts-nightly://media/urn:rts:video:9890897` | `playrts-debug://open?urn=urn:rts:video:9890897` | +| Sexomax | Radio show | `playrts://show/urn:rts:show:radio:8864883` | `playrts-beta://show/urn:rts:show:radio:8864883` | `playrts-nightly://show/urn:rts:show:radio:8864883` | `playrts-debug://show/urn:rts:show:radio:8864883` | ### SRF | Item | Type | Production link | Beta link | Nightly link | Debug link | |:--:|:--:|:--:|:--:|:--:|:--:| -| 10vor10 (2018/10/03) | Video | `playsrf://open?media=urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | `playsrf-beta://open?media=urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | `playsrf-nightly://open?media=urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | `playsrf-debug://open?media=urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | -| Buchzeichen | Radio show | `playsrf://open?show=urn:srf:show:radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | `playsrf-beta://open?show=urn:srf:show: radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | `playsrf-nightly://open?show=urn:srf:show: radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | `playsrf-debug://open?show=urn:srf:show: radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | +| 10vor10 (2018/10/03) | Video | `playsrf://media/urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | `playsrf-beta://media/urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | `playsrf-nightly://media/urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | `playsrf-debug://media/urn:srf:video:da6fdf91-3e91-46fb-be50-914e47203e45` | +| Buchzeichen | Radio show | `playsrf://show/urn:srf:show:radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | `playsrf-beta://show/urn:srf:show: radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | `playsrf-nightly://show/urn:srf:show: radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | `playsrf-debug://show/urn:srf:show: radio:132857ed-76c6-4659-9e36-ab1e5bdf6e7f` | ### SWI | Item | Type | Production link | Beta link | Nightly link | Debug link | |:--:|:--:|:--:|:--:|:--:|:--:| -| Zermatt’s new tri-cable car system | Video | `playswi://open?media=urn:swi:video:44438410` | `playswi-beta://open?media=urn:swi:video:44438410` | `playswi-nightly://open?media=urn:swi:video:44438410` | `playswi-debug://open?media=urn:swi:video:44438410` | \ No newline at end of file +| Zermatt’s new tri-cable car system | Video | `playswi://media/urn:swi:video:44438410` | `playswi-beta://media/urn:swi:video:44438410` | `playswi-nightly://media/urn:swi:video:44438410` | `playswi-debug://media/urn:swi:video:44438410` | \ No newline at end of file