diff options
author | Sylvain CECCHETTO <cecchetto.sylvain@me.com> | 2020-06-11 11:54:01 +0200 |
---|---|---|
committer | Sylvain CECCHETTO <cecchetto.sylvain@me.com> | 2020-08-26 17:11:38 +0200 |
commit | 3737c035232b31d0e9af7ea83e389d49e978cad3 (patch) | |
tree | 2c48167e470d5e32615a58fd155ba68b39e1583e | |
parent | e418ee4607c78d5012f27a0e8fd70cd1f0ab7c56 (diff) |
[tvOS][TopShelf] Refactor code
-rw-r--r-- | cmake/scripts/darwin_embedded/ExtraTargets.cmake | 6 | ||||
-rw-r--r-- | xbmc/platform/darwin/DarwinUtils.h | 2 | ||||
-rw-r--r-- | xbmc/platform/darwin/DarwinUtils.mm | 52 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/DarwinEmbedUtils.h | 16 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/DarwinEmbedUtils.mm | 52 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/TVOSTopShelf.h | 9 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/TVOSTopShelf.mm | 150 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.m | 124 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.mm | 123 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/tvosShared.h | 1 | ||||
-rw-r--r-- | xbmc/platform/darwin/tvos/tvosShared.mm (renamed from xbmc/platform/darwin/tvos/tvosShared.m) | 30 | ||||
-rw-r--r-- | xbmc/settings/SettingsComponent.cpp | 13 | ||||
-rw-r--r-- | xbmc/utils/RecentlyAddedJob.cpp | 5 |
15 files changed, 311 insertions, 276 deletions
diff --git a/cmake/scripts/darwin_embedded/ExtraTargets.cmake b/cmake/scripts/darwin_embedded/ExtraTargets.cmake index 2b9980a184..1b984f36b8 100644 --- a/cmake/scripts/darwin_embedded/ExtraTargets.cmake +++ b/cmake/scripts/darwin_embedded/ExtraTargets.cmake @@ -7,9 +7,11 @@ if(CORE_PLATFORM_NAME_LC STREQUAL tvos) set(ENTITLEMENTS_OUT_PATH "${CMAKE_BINARY_DIR}/CMakeFiles/${TOPSHELF_EXTENSION_NAME}.dir/TopShelf.entitlements") set(SOURCES - ${TOPSHELF_DIR}/ServiceProvider.m - ${TOPSHELF_DIR}/../tvosShared.m) + ${TOPSHELF_DIR}/../../ios-common/DarwinEmbedUtils.mm + ${TOPSHELF_DIR}/ServiceProvider.mm + ${TOPSHELF_DIR}/../tvosShared.mm) set(HEADERS + ${TOPSHELF_DIR}/../../ios-common/DarwinEmbedUtils.h ${TOPSHELF_DIR}/ServiceProvider.h ${TOPSHELF_DIR}/../tvosShared.h) add_executable(${TOPSHELF_EXTENSION_NAME} MACOSX_BUNDLE ${SOURCES} ${HEADERS}) diff --git a/xbmc/platform/darwin/DarwinUtils.h b/xbmc/platform/darwin/DarwinUtils.h index 4f3db3557d..be2cb51dc0 100644 --- a/xbmc/platform/darwin/DarwinUtils.h +++ b/xbmc/platform/darwin/DarwinUtils.h @@ -24,8 +24,6 @@ public: static const char* GetVersionString(); static std::string GetFrameworkPath(bool forPython); static int GetExecutablePath(char* path, size_t *pathsize); - static const char *GetAppRootFolder(void); - static bool IsIosSandboxed(void); static void SetScheduling(bool realtime); static bool CFStringRefToString(CFStringRef source, std::string& destination); static bool CFStringRefToUTF8String(CFStringRef source, std::string& destination); diff --git a/xbmc/platform/darwin/DarwinUtils.mm b/xbmc/platform/darwin/DarwinUtils.mm index 8f9cd57e19..0b4178ba8d 100644 --- a/xbmc/platform/darwin/DarwinUtils.mm +++ b/xbmc/platform/darwin/DarwinUtils.mm @@ -140,58 +140,6 @@ int CDarwinUtils::GetExecutablePath(char* path, size_t *pathsize) return 0; } -const char* CDarwinUtils::GetAppRootFolder(void) -{ - static std::string rootFolder; - static std::once_flag flag; - std::call_once(flag, [] - { - if (IsIosSandboxed()) - { -#ifdef TARGET_DARWIN_TVOS - // writing to Documents is prohibited, more info: - // https://developer.apple.com/library/archive/documentation/General/Conceptual/AppleTV_PG/index.html#//apple_ref/doc/uid/TP40015241-CH12-SW5 - // https://forums.developer.apple.com/thread/89008 - rootFolder = "Library/Caches"; -#else - // when we are sandbox make documents our root - // so that user can access everything he needs - // via itunes sharing - rootFolder = "Documents"; -#endif - } - else - { - rootFolder = "Library/Preferences"; - } - }); - return rootFolder.c_str(); -} - -bool CDarwinUtils::IsIosSandboxed(void) -{ - static bool ret = false; - static std::once_flag flag; - std::call_once(flag, [] { - auto executablePath = getExecutablePath(); - auto sandboxPrefixPaths = { - // since iOS later than 9.0.2 but before 9.3.5 - @"/var/containers/Bundle/", - // since iOS 13 - @"/private/var/containers/Bundle/", - }; - for (auto prefixPath : sandboxPrefixPaths) - { - if ([executablePath hasPrefix:prefixPath]) - { - ret = true; - break; - } - } - }); - return ret; -} - void CDarwinUtils::SetScheduling(bool realtime) { int policy; diff --git a/xbmc/platform/darwin/ios-common/CMakeLists.txt b/xbmc/platform/darwin/ios-common/CMakeLists.txt index 49ce23c93f..4e6241198b 100644 --- a/xbmc/platform/darwin/ios-common/CMakeLists.txt +++ b/xbmc/platform/darwin/ios-common/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES AnnounceReceiver.mm DarwinEmbedKeyboard.mm DarwinEmbedKeyboardView.mm DarwinEmbedNowPlayingInfoManager.mm + DarwinEmbedUtils.mm DarwinNSUserDefaults.mm NSData+GZIP.m) @@ -11,6 +12,7 @@ set(HEADERS AnnounceReceiver.h DarwinEmbedKeyboard.h DarwinEmbedKeyboardView.h DarwinEmbedNowPlayingInfoManager.h + DarwinEmbedUtils.h DarwinNSUserDefaults.h NSData+GZIP.h) diff --git a/xbmc/platform/darwin/ios-common/DarwinEmbedUtils.h b/xbmc/platform/darwin/ios-common/DarwinEmbedUtils.h new file mode 100644 index 0000000000..7402970d5f --- /dev/null +++ b/xbmc/platform/darwin/ios-common/DarwinEmbedUtils.h @@ -0,0 +1,16 @@ +/* +* Copyright (C) 2010-2020 Team Kodi +* This file is part of Kodi - https://kodi.tv +* +* SPDX-License-Identifier: GPL-2.0-or-later +* See LICENSES/README.md for more information. +*/ + +#pragma once + +class CDarwinEmbedUtils +{ +public: + static const char* GetAppRootFolder(void); + static bool IsIosSandboxed(void); +}; diff --git a/xbmc/platform/darwin/ios-common/DarwinEmbedUtils.mm b/xbmc/platform/darwin/ios-common/DarwinEmbedUtils.mm new file mode 100644 index 0000000000..f2fa674e22 --- /dev/null +++ b/xbmc/platform/darwin/ios-common/DarwinEmbedUtils.mm @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2010-2020 Team Kodi +* This file is part of Kodi - https://kodi.tv +* +* SPDX-License-Identifier: GPL-2.0-or-later +* See LICENSES/README.md for more information. +*/ + +#import "DarwinEmbedUtils.h" + +#include <mutex> +#include <string> + +#import <Foundation/Foundation.h> + +const char* CDarwinEmbedUtils::GetAppRootFolder(void) +{ + static std::string rootFolder; + static std::once_flag flag; + std::call_once(flag, [] { + if (IsIosSandboxed()) + { +#ifdef TARGET_DARWIN_TVOS + // writing to Documents is prohibited, more info: + // https://developer.apple.com/library/archive/documentation/General/Conceptual/AppleTV_PG/index.html#//apple_ref/doc/uid/TP40015241-CH12-SW5 + // https://forums.developer.apple.com/thread/89008 + rootFolder = "Library/Caches"; +#else + // when we are sandbox make documents our root + // so that user can access everything he needs + // via itunes sharing + rootFolder = "Documents"; +#endif + } + else + { + rootFolder = "Library/Preferences"; + } + }); + return rootFolder.c_str(); +} + +bool CDarwinEmbedUtils::IsIosSandboxed(void) +{ + static bool ret; + static std::once_flag flag; + std::call_once(flag, [] { + auto const sandboxPrefixPath = @"/private/var/containers/Bundle/"; + ret = [NSBundle.mainBundle.executablePath hasPrefix:sandboxPrefixPath]; + }); + return ret; +} diff --git a/xbmc/platform/darwin/tvos/CMakeLists.txt b/xbmc/platform/darwin/tvos/CMakeLists.txt index 099511dd07..8500d04c06 100644 --- a/xbmc/platform/darwin/tvos/CMakeLists.txt +++ b/xbmc/platform/darwin/tvos/CMakeLists.txt @@ -3,7 +3,7 @@ set(SOURCES PreflightHandler.mm TVOSEAGLView.mm TVOSKeyboardView.mm TVOSSettingsHandler.mm - tvosShared.m + tvosShared.mm TVOSTopShelf.mm XBMCController.mm) diff --git a/xbmc/platform/darwin/tvos/TVOSTopShelf.h b/xbmc/platform/darwin/tvos/TVOSTopShelf.h index ee5c244a5e..06f8643571 100644 --- a/xbmc/platform/darwin/tvos/TVOSTopShelf.h +++ b/xbmc/platform/darwin/tvos/TVOSTopShelf.h @@ -11,12 +11,19 @@ #include "FileItem.h" #include "threads/CriticalSection.h" +typedef enum +{ + MOVIES = 0, + TV_SHOWS = 1 +} TVOSTopShelfItemsCategory; + + class CTVOSTopShelf { public: static CTVOSTopShelf& GetInstance(); void RunTopShelf(); - void SetTopShelfItems(CFileItemList& movies, CFileItemList& tv); + void SetTopShelfItems(CFileItemList& items, TVOSTopShelfItemsCategory category); void HandleTopShelfUrl(const std::string& url, const bool run); private: diff --git a/xbmc/platform/darwin/tvos/TVOSTopShelf.mm b/xbmc/platform/darwin/tvos/TVOSTopShelf.mm index 852f40d2e9..50dae8f508 100644 --- a/xbmc/platform/darwin/tvos/TVOSTopShelf.mm +++ b/xbmc/platform/darwin/tvos/TVOSTopShelf.mm @@ -28,6 +28,8 @@ #include "video/windows/GUIWindowVideoBase.h" #include "video/windows/GUIWindowVideoNav.h" +#include "platform/darwin/ios-common/DarwinEmbedUtils.h" + #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <mach/mach_host.h> @@ -46,119 +48,137 @@ CTVOSTopShelf& CTVOSTopShelf::GetInstance() return sTopShelf; } -void CTVOSTopShelf::SetTopShelfItems(CFileItemList& movies, CFileItemList& tv) +void CTVOSTopShelf::SetTopShelfItems(CFileItemList& items, TVOSTopShelfItemsCategory category) { @autoreleasepool { + // Retrieve store URL auto storeUrl = [tvosShared getSharedURL]; if (!storeUrl) return; - storeUrl = [storeUrl URLByAppendingPathComponent:@"RA" isDirectory:YES]; - const BOOL isJailbroken = [tvosShared isJailbroken]; - CLog::Log(LOGDEBUG, "TopShelf: using shared path {} (jailbroken: {})", storeUrl.path.UTF8String, - isJailbroken ? "yes" : "no"); - auto sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:[tvosShared getSharedID]]; - auto sharedDictJailbreak = isJailbroken ? [[NSMutableDictionary alloc] initWithCapacity:2 + 2] - : nil; // for jailbroken devices + const auto isSandboxed = CDarwinEmbedUtils::IsIosSandboxed(); + CLog::Log(LOGDEBUG, "[TopShelf] (sandboxed: {}) Use storeUrl: {}", isSandboxed ? "yes" : "no", + storeUrl.path.UTF8String); // store all old thumbs in array auto fileManager = NSFileManager.defaultManager; auto filePaths = [NSMutableSet setWithArray:[fileManager contentsOfDirectoryAtPath:storeUrl.path error:nil]]; std::string raPath = storeUrl.path.UTF8String; - CVideoThumbLoader thumbLoader; + // Shared dicts (if we are sandboxed we use sharedDefaults, else we use sharedJailbreak) + auto sharedDefaults = + isSandboxed ? [[NSUserDefaults alloc] initWithSuiteName:[tvosShared getSharedID]] : nil; + auto sharedJailbreak = isSandboxed ? nil : [[NSMutableDictionary alloc] initWithCapacity:2]; + + // Function used to add category items in TopShelf shared dict + CVideoThumbLoader thumbLoader; auto fillSharedDicts = - [&](CFileItemList& videoItems, NSString* videosKey, NSString* videosTitleKey, - uint32_t titleStringCode, + [&](CFileItemList& items, NSString* categoryKey, uint32_t categoryTitleCode, std::function<std::string(CFileItemPtr videoItem)> getThumbnailForItem, std::function<std::string(CFileItemPtr videoItem)> getTitleForItem) { - if (videoItems.Size() <= 0) + if (items.Size() <= 0) { - // cleanup if there is no RA - [sharedDefaults removeObjectForKey:videosKey]; - [sharedDefaults removeObjectForKey:videosTitleKey]; + // If there is no item in this category, remove this dict from sharedDefaults + [sharedDefaults removeObjectForKey:categoryKey]; return; } - const int topShelfSize = std::min(videoItems.Size(), MaxItems); - auto videosArray = [NSMutableArray arrayWithCapacity:topShelfSize]; - for (int i = 0; i < topShelfSize; ++i) + // Create dict for this category + auto categoryDict = [NSMutableDictionary dictionaryWithCapacity:2]; + + // Save category title in dict + categoryDict[@"categoryTitle"] = @(g_localizeStrings.Get(categoryTitleCode).c_str()); + + // Create an array to store each category item + const int categorySize = std::min(items.Size(), MaxItems); + auto categoryItems = [NSMutableArray arrayWithCapacity:categorySize]; + for (int i = 0; i < categorySize; ++i) { @autoreleasepool { - auto videoItem = videoItems.Get(i); - if (!videoItem->HasArt("thumb")) - thumbLoader.LoadItem(videoItem.get()); + auto item = items.Get(i); + if (!item->HasArt("thumb")) + thumbLoader.LoadItem(item.get()); - auto thumbnailPath = getThumbnailForItem(videoItem); - auto fileName = std::to_string(videoItem->GetVideoInfoTag()->m_iDbId) + + auto thumbnailPath = getThumbnailForItem(item); + auto fileName = std::to_string(item->GetVideoInfoTag()->m_iDbId) + URIUtils::GetFileName(thumbnailPath); auto destPath = URIUtils::AddFileToFolder(raPath, fileName); if (!XFILE::CFile::Exists(destPath)) XFILE::CFile::Copy(thumbnailPath, destPath); else { - // remove from array so it doesnt get deleted at the end + // Remove from array so it doesn't get deleted at the end [filePaths removeObject:@(fileName.c_str())]; } - auto title = getTitleForItem(videoItem); - CLog::Log(LOGDEBUG, "TopShelf: - adding video to '{}' array: {}", - videosKey.UTF8String, title.c_str()); - [videosArray addObject:@{ - @"title" : @(title.c_str()), + auto itemTitle = getTitleForItem(item); + CLog::Log(LOGDEBUG, "[TopShelf] Adding item '{}' in category '{}'", itemTitle.c_str(), + categoryKey.UTF8String); + [categoryItems addObject:@{ + @"title" : @(itemTitle.c_str()), @"thumb" : @(fileName.c_str()), - @"url" : @(Base64::Encode(videoItem->GetVideoInfoTag()->GetPath()).c_str()) + @"url" : @(Base64::Encode(item->GetVideoInfoTag()->GetPath()).c_str()) }]; } } - [sharedDefaults setObject:videosArray forKey:videosKey]; - sharedDictJailbreak[videosKey] = videosArray; - auto tvTitle = @(g_localizeStrings.Get(titleStringCode).c_str()); - [sharedDefaults setObject:tvTitle forKey:videosTitleKey]; - sharedDictJailbreak[videosTitleKey] = tvTitle; + // Store category items array in category dict + categoryDict[@"categoryItems"] = categoryItems; + + // Store category dict in shared dict + [sharedDefaults setObject:categoryDict forKey:categoryKey]; + sharedJailbreak[categoryKey] = categoryDict; }; - fillSharedDicts(movies, @"movies", @"moviesTitle", 20386, - [](CFileItemPtr videoItem) { - if (videoItem->HasArt("poster")) - { - return videoItem->GetArt("poster"); - } - else - return videoItem->GetArt("thumb"); - }, - [](CFileItemPtr videoItem) { return videoItem->GetLabel(); }); - - CVideoDatabase videoDb; - videoDb.Open(); - fillSharedDicts(tv, @"tv", @"tvTitle", 20387, - [&videoDb](CFileItemPtr videoItem) { - int season = videoItem->GetVideoInfoTag()->m_iIdSeason; - return season > 0 ? videoDb.GetArtForItem(season, MediaTypeSeason, "poster") - : std::string{}; - }, - [](CFileItemPtr videoItem) { - return StringUtils::Format( - "%s s%02de%02d", videoItem->GetVideoInfoTag()->m_strShowTitle.c_str(), - videoItem->GetVideoInfoTag()->m_iSeason, - videoItem->GetVideoInfoTag()->m_iEpisode); - }); - videoDb.Close(); - - // remove unused thumbs from cache folder + + // Based on category type, add items in TopShelf shared dict + switch (category) + { + case TVOSTopShelfItemsCategory::MOVIES: + fillSharedDicts( + items, @"movies", 20386, + [](CFileItemPtr videoItem) { + if (videoItem->HasArt("poster")) + return videoItem->GetArt("poster"); + else + return videoItem->GetArt("thumb"); + }, + [](CFileItemPtr videoItem) { return videoItem->GetLabel(); }); + break; + case TVOSTopShelfItemsCategory::TV_SHOWS: + CVideoDatabase videoDb; + videoDb.Open(); + fillSharedDicts( + items, @"tvshows", 20387, + [&videoDb](CFileItemPtr videoItem) { + int season = videoItem->GetVideoInfoTag()->m_iIdSeason; + return season > 0 ? videoDb.GetArtForItem(season, MediaTypeSeason, "poster") + : std::string{}; + }, + [](CFileItemPtr videoItem) { + return StringUtils::Format("%s s%02de%02d", + videoItem->GetVideoInfoTag()->m_strShowTitle.c_str(), + videoItem->GetVideoInfoTag()->m_iSeason, + videoItem->GetVideoInfoTag()->m_iEpisode); + }); + videoDb.Close(); + break; + } + + // Remove unused thumbs from cache folder NSString* strFiles; for (strFiles in filePaths) [fileManager removeItemAtURL:[storeUrl URLByAppendingPathComponent:strFiles isDirectory:NO] error:nil]; - [sharedDictJailbreak writeToURL:[storeUrl URLByAppendingPathComponent:@"shared.dict"] - atomically:YES]; + // Synchronize shared dict [sharedDefaults synchronize]; + [sharedJailbreak writeToURL:[storeUrl URLByAppendingPathComponent:@"shared.dict"] + atomically:YES]; } } diff --git a/xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.m b/xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.m deleted file mode 100644 index 3277db4e49..0000000000 --- a/xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.m +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2015 Team MrMC - * https://github.com/MrMC - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MrMC; see the file COPYING. If not, see - * <http://www.gnu.org/licenses/>. - * - */ - -#import "ServiceProvider.h" - -#import "../tvosShared.h" - -@implementation ServiceProvider - -#pragma mark - TVTopShelfProvider protocol - -- (TVTopShelfContentStyle)topShelfStyle -{ - return TVTopShelfContentStyleSectioned; -} - -- (NSArray<TVContentItem*>*)topShelfItems -{ - __auto_type storeUrl = [tvosShared getSharedURL]; - if (!storeUrl) - return @[]; - - __auto_type const sharedID = [tvosShared getSharedID]; - __auto_type const shared = [[NSUserDefaults alloc] initWithSuiteName:sharedID]; - __auto_type topShelfItems = [[NSMutableArray alloc] init]; - __auto_type wrapperIdentifier = - [[TVContentIdentifier alloc] initWithIdentifier:@"shelf-wrapper" container:nil]; - - NSArray* movieArray = nil; - NSArray* tvArray = nil; - NSDictionary* sharedDict = nil; - - if ([tvosShared isJailbroken]) - { - __auto_type sharedDictUrl = - [storeUrl URLByAppendingPathComponent:@"shared.dict" isDirectory:NO]; - sharedDict = [NSDictionary dictionaryWithContentsOfFile:[sharedDictUrl path]]; - - movieArray = [sharedDict valueForKey:@"movies"]; - tvArray = [sharedDict valueForKey:@"tv"]; - } - else - { - movieArray = [shared objectForKey:@"movies"]; - tvArray = [shared valueForKey:@"tv"]; - } - - __auto_type mainAppBundle = [tvosShared mainAppBundle]; - __auto_type kodiUrlScheme = @"kodi"; // fallback value - NSDictionary* dic; - for (dic in [mainAppBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) - { - if ([dic[@"CFBundleURLName"] isEqualToString:mainAppBundle.bundleIdentifier]) - { - kodiUrlScheme = dic[@"CFBundleURLSchemes"][0]; - break; - } - } - - storeUrl = [storeUrl URLByAppendingPathComponent:@"RA" isDirectory:YES]; - __auto_type contentItemsFrom = ^NSArray<TVContentItem*>*(NSArray* videosArray) - { - NSMutableArray<TVContentItem*>* contentItems = - [[NSMutableArray alloc] initWithCapacity:videosArray.count]; - NSDictionary* videoDict; - for (videoDict in videosArray) - { - __auto_type identifier = - [[TVContentIdentifier alloc] initWithIdentifier:@"VOD" container:wrapperIdentifier]; - __auto_type contentItem = [[TVContentItem alloc] initWithContentIdentifier:identifier]; - - [contentItem - setImageURL:[storeUrl URLByAppendingPathComponent:[videoDict valueForKey:@"thumb"] - isDirectory:NO] - forTraits:TVContentItemImageTraitScreenScale1x]; - contentItem.imageShape = TVContentItemImageShapePoster; - contentItem.title = [videoDict valueForKey:@"title"]; - NSString* url = [videoDict valueForKey:@"url"]; - contentItem.displayURL = [NSURL - URLWithString:[NSString stringWithFormat:@"%@://display/movie/%@", kodiUrlScheme, url]]; - contentItem.playURL = [NSURL - URLWithString:[NSString stringWithFormat:@"%@://play/movie/%@", kodiUrlScheme, url]]; - [contentItems addObject:contentItem]; - } - return contentItems; - }; - - if ([movieArray count] > 0) - { - __auto_type itemMovie = [[TVContentItem alloc] initWithContentIdentifier:wrapperIdentifier]; - itemMovie.title = [(sharedDict ?: shared) valueForKey:@"moviesTitle"]; - itemMovie.topShelfItems = contentItemsFrom(movieArray); - [topShelfItems addObject:itemMovie]; - } - - if ([tvArray count] > 0) - { - __auto_type itemTv = [[TVContentItem alloc] initWithContentIdentifier:wrapperIdentifier]; - itemTv.title = [(sharedDict ?: shared) valueForKey:@"tvTitle"]; - itemTv.topShelfItems = contentItemsFrom(tvArray); - [topShelfItems addObject:itemTv]; - } - - return topShelfItems; -} - -@end diff --git a/xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.mm b/xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.mm new file mode 100644 index 0000000000..fef201dfda --- /dev/null +++ b/xbmc/platform/darwin/tvos/TopShelf/ServiceProvider.mm @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2015 Team MrMC + * https://github.com/MrMC + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MrMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#import "ServiceProvider.h" + +#import "../tvosShared.h" + +#include "platform/darwin/ios-common/DarwinEmbedUtils.h" + +@implementation ServiceProvider + +#pragma mark - TVTopShelfProvider protocol + +- (TVTopShelfContentStyle)topShelfStyle +{ + return TVTopShelfContentStyleSectioned; +} + +- (NSArray<TVContentItem*>*)topShelfItems +{ + // Retrieve store URL + auto storeUrl = [tvosShared getSharedURL]; + if (!storeUrl) + return @[]; + storeUrl = [storeUrl URLByAppendingPathComponent:@"RA" isDirectory:YES]; + + + // Retrieve shared dict + NSDictionary* sharedDict; + if (CDarwinEmbedUtils::IsIosSandboxed()) + { + auto const sharedID = [tvosShared getSharedID]; + auto const shared = [[NSUserDefaults alloc] initWithSuiteName:sharedID]; + sharedDict = shared.dictionaryRepresentation; + } + else + { + auto sharedDictUrl = [storeUrl URLByAppendingPathComponent:@"shared.dict" + isDirectory:NO]; + sharedDict = [NSDictionary dictionaryWithContentsOfFile:[sharedDictUrl path]]; + } + + + // Retrieve Kodi URL Scheme + auto const mainAppBundle = [tvosShared mainAppBundle]; + auto kodiUrlScheme = @"kodi"; // fallback value + for (NSDictionary* dic in [mainAppBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) + { + if ([dic[@"CFBundleURLName"] isEqualToString:mainAppBundle.bundleIdentifier]) + { + kodiUrlScheme = dic[@"CFBundleURLSchemes"][0]; + break; + } + } + + auto wrapperIdentifier = [[TVContentIdentifier alloc] initWithIdentifier:@"shelf-wrapper" + container:nil]; + + + // Function to create a TVContentItem array from an array of items (a category) + auto contentItemsFrom = ^NSArray<TVContentItem*>*(NSArray* categoryItems) + { + NSMutableArray<TVContentItem*>* contentItems = + [[NSMutableArray alloc] initWithCapacity:categoryItems.count]; + for (NSDictionary* item in categoryItems) + { + auto identifier = [[TVContentIdentifier alloc] initWithIdentifier:@"VOD" + container:wrapperIdentifier]; + auto contentItem = [[TVContentItem alloc] initWithContentIdentifier:identifier]; + + [contentItem setImageURL:[storeUrl URLByAppendingPathComponent:item[@"thumb"] isDirectory:NO] + forTraits:TVContentItemImageTraitScreenScale1x]; + contentItem.imageShape = TVContentItemImageShapePoster; + contentItem.title = item[@"title"]; + NSString* url = item[@"url"]; + contentItem.displayURL = [NSURL + URLWithString:[NSString stringWithFormat:@"%@://display/movie/%@", kodiUrlScheme, url]]; + contentItem.playURL = [NSURL + URLWithString:[NSString stringWithFormat:@"%@://play/movie/%@", kodiUrlScheme, url]]; + [contentItems addObject:contentItem]; + } + return contentItems; + }; + + // Add each category to TopShelf + auto topShelfItems = [[NSMutableArray alloc] init]; + + [sharedDict enumerateKeysAndObjectsUsingBlock:^(NSString* categoryKey, id categoryDict, BOOL *stop) { + if (![categoryDict isKindOfClass:[NSDictionary class]]) + return; + NSArray* categoryItems = categoryDict[@"categoryItems"]; + if (!categoryItems) + return; + if (categoryItems.count == 0) + return; + + auto categoryContent = [[TVContentItem alloc] initWithContentIdentifier:wrapperIdentifier]; + categoryContent.title = categoryDict[@"categoryTitle"]; + categoryContent.topShelfItems = contentItemsFrom(categoryItems); + [topShelfItems addObject:categoryContent]; + }]; + + return topShelfItems; +} + +@end diff --git a/xbmc/platform/darwin/tvos/tvosShared.h b/xbmc/platform/darwin/tvos/tvosShared.h index dd9bbc5279..093ed184e0 100644 --- a/xbmc/platform/darwin/tvos/tvosShared.h +++ b/xbmc/platform/darwin/tvos/tvosShared.h @@ -11,6 +11,5 @@ @interface tvosShared : NSObject + (NSString*)getSharedID; + (NSURL*)getSharedURL; -+ (BOOL)isJailbroken; + (NSBundle*)mainAppBundle; @end diff --git a/xbmc/platform/darwin/tvos/tvosShared.m b/xbmc/platform/darwin/tvos/tvosShared.mm index b194dd9f48..14accc944a 100644 --- a/xbmc/platform/darwin/tvos/tvosShared.m +++ b/xbmc/platform/darwin/tvos/tvosShared.mm @@ -8,6 +8,8 @@ #import "tvosShared.h" +#include "platform/darwin/ios-common/DarwinEmbedUtils.h" + @implementation tvosShared + (NSString*)getSharedID @@ -18,34 +20,20 @@ + (NSURL*)getSharedURL { NSString* sharedID = [self getSharedID]; - if ([self isJailbroken]) - return [[NSURL fileURLWithPath:@"/var/mobile/Library/Caches"] - URLByAppendingPathComponent:sharedID]; - else + if (CDarwinEmbedUtils::IsIosSandboxed()) { NSFileManager* fileManager = [NSFileManager defaultManager]; NSURL* sharedUrl = [fileManager containerURLForSecurityApplicationGroupIdentifier:sharedID]; + // e.g. /private/var/mobile/Containers/Shared/AppGroup/32B9DA1F-3B1F-4DBC-8326-ABB08BF16EC9/ sharedUrl = [sharedUrl URLByAppendingPathComponent:@"Library" isDirectory:YES]; sharedUrl = [sharedUrl URLByAppendingPathComponent:@"Caches" isDirectory:YES]; return sharedUrl; } -} - -+ (BOOL)IsTVOSSandboxed -{ - // @todo merge with CDarwinUtils::IsIosSandboxed - static BOOL ret; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // we re NOT sandboxed if we are installed in /var/mobile/Applications with greeng0blin jailbreak - ret = ![[self mainAppBundle].bundlePath containsString:@"/var/mobile/Applications/"]; - }); - return ret; -} - -+ (BOOL)isJailbroken -{ - return ![self IsTVOSSandboxed]; + else + { + return [[NSURL fileURLWithPath:@"/var/mobile/Library/Caches"] + URLByAppendingPathComponent:sharedID]; + } } + (NSBundle*)mainAppBundle diff --git a/xbmc/settings/SettingsComponent.cpp b/xbmc/settings/SettingsComponent.cpp index 5d1a4d186c..4fb51ca521 100644 --- a/xbmc/settings/SettingsComponent.cpp +++ b/xbmc/settings/SettingsComponent.cpp @@ -17,7 +17,7 @@ #include "filesystem/Directory.h" #include "filesystem/SpecialProtocol.h" #ifdef TARGET_DARWIN_EMBEDDED -#include "platform/darwin/DarwinUtils.h" +#include "platform/darwin/ios-common/DarwinEmbedUtils.h" #endif #ifdef TARGET_WINDOWS #include "platform/Environment.h" @@ -304,8 +304,10 @@ bool CSettingsComponent::InitDirectoriesOSX(bool bPlatformDirectories) CSpecialProtocol::SetXBMCPath(appPath); #if defined(TARGET_DARWIN_EMBEDDED) std::string appName = CCompileInfo::GetAppName(); - CSpecialProtocol::SetHomePath(userHome + "/" + CDarwinUtils::GetAppRootFolder() + "/" + appName); - CSpecialProtocol::SetMasterProfilePath(userHome + "/" + CDarwinUtils::GetAppRootFolder() + "/" + appName + "/userdata"); + CSpecialProtocol::SetHomePath(userHome + "/" + CDarwinEmbedUtils::GetAppRootFolder() + "/" + + appName); + CSpecialProtocol::SetMasterProfilePath(userHome + "/" + CDarwinEmbedUtils::GetAppRootFolder() + + "/" + appName + "/userdata"); #else std::string appName = CCompileInfo::GetAppName(); CSpecialProtocol::SetHomePath(userHome + "/Library/Application Support/" + appName); @@ -316,7 +318,8 @@ bool CSettingsComponent::InitDirectoriesOSX(bool bPlatformDirectories) StringUtils::ToLower(dotLowerAppName); // location for temp files #if defined(TARGET_DARWIN_EMBEDDED) - std::string strTempPath = URIUtils::AddFileToFolder(userHome, std::string(CDarwinUtils::GetAppRootFolder()) + "/" + appName + "/temp"); + std::string strTempPath = URIUtils::AddFileToFolder( + userHome, std::string(CDarwinEmbedUtils::GetAppRootFolder()) + "/" + appName + "/temp"); #else std::string strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/"); XFILE::CDirectory::Create(strTempPath); @@ -326,7 +329,7 @@ bool CSettingsComponent::InitDirectoriesOSX(bool bPlatformDirectories) // xbmc.log file location #if defined(TARGET_DARWIN_EMBEDDED) - strTempPath = userHome + "/" + std::string(CDarwinUtils::GetAppRootFolder()); + strTempPath = userHome + "/" + std::string(CDarwinEmbedUtils::GetAppRootFolder()); #else strTempPath = userHome + "/Library/Logs"; #endif diff --git a/xbmc/utils/RecentlyAddedJob.cpp b/xbmc/utils/RecentlyAddedJob.cpp index b45fadeb46..b6374fc6a8 100644 --- a/xbmc/utils/RecentlyAddedJob.cpp +++ b/xbmc/utils/RecentlyAddedJob.cpp @@ -145,8 +145,9 @@ bool CRecentlyAddedJob::UpdateVideo() } #if defined(TARGET_DARWIN_TVOS) - // send recently added Movies and TvShows to TopShelf - CTVOSTopShelf::GetInstance().SetTopShelfItems(items, TVShowItems); + // Add recently added Movies and TvShows items on tvOS Kodi TopShelf + CTVOSTopShelf::GetInstance().SetTopShelfItems(items, TVOSTopShelfItemsCategory::MOVIES); + CTVOSTopShelf::GetInstance().SetTopShelfItems(TVShowItems, TVOSTopShelfItemsCategory::TV_SHOWS); #endif i = 0; |