diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index f4d154de..60847e1f 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* RequestHandler.swift */; }; BF29012F2318F6B100D88A45 /* AppBannerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF29012E2318F6B100D88A45 /* AppBannerView.xib */; }; BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2901302318F7A800D88A45 /* AppBannerView.swift */; }; + BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; }; BF3432FB246B894F0052F4A1 /* BackupAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */; }; BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3BEFBE2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift */; }; BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3BEFC024086A1E00DE7D55 /* RefreshAppOperation.swift */; }; @@ -40,6 +41,7 @@ BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3F786322CAA41E008FBD20 /* ALTDeviceManager+Installation.swift */; }; BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF41B805233423AE00C593A3 /* TabBarController.swift */; }; BF41B808233433C100C593A3 /* LoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF41B807233433C100C593A3 /* LoadingState.swift */; }; + BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF42345825101C1D006D1EB2 /* WidgetView.swift */; }; BF42345C251024B0006D1EB2 /* AltSign-Static in Frameworks */ = {isa = PBXBuildFile; productRef = BF42345B251024B0006D1EB2 /* AltSign-Static */; }; BF42345D25102688006D1EB2 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; }; BF44CC6C232AEB90004DA9C3 /* LaunchAtLogin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */; }; @@ -193,6 +195,10 @@ BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */; }; BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; }; BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C322E662D300049BA1 /* AppViewController.swift */; }; + BF989171250AABF4002ACF50 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF989170250AABF4002ACF50 /* Assets.xcassets */; }; + BF989177250AABF4002ACF50 /* AltWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF98917C250AAC4F002ACF50 /* Countdown.swift */; }; + BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF98917D250AAC4F002ACF50 /* AltWidget.swift */; }; BF989184250AACFC002ACF50 /* Date+RelativeDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */; }; BF989185250AAD1D002ACF50 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; }; BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */; }; @@ -223,6 +229,7 @@ BFB6B21E231870160022A802 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21D231870160022A802 /* NewsViewController.swift */; }; BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21F231870B00022A802 /* NewsCollectionViewCell.swift */; }; BFB6B22423187A3D0022A802 /* NewsCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFB6B22323187A3D0022A802 /* NewsCollectionViewCell.xib */; }; + BFBE0004250ACFFB0080826E /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; }; BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */; }; BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */; }; BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; }; @@ -318,6 +325,7 @@ BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; }; BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */; }; BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */; }; + BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; }; BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; }; BFFCFA582488648D0077BFCE /* LocalConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF10EB3124870B3F0055E6DB /* LocalConnectionHandler.swift */; }; /* End PBXBuildFile section */ @@ -337,6 +345,13 @@ remoteGlobalIDString = BF66EE7D2501AE50007EE018; remoteInfo = AltStoreCore; }; + BF989175250AABF4002ACF50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFD247622284B9A500981D42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BF989166250AABF3002ACF50; + remoteInfo = AltWidgetExtension; + }; BFBFFB262380C72F00993A4A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFD247622284B9A500981D42 /* Project object */; @@ -344,6 +359,13 @@ remoteGlobalIDString = BF5C5FC4237DF5AE00EDD0C6; remoteInfo = AltPlugin; }; + BFF615AA2510042B00484D3B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFD247622284B9A500981D42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BF66EE7D2501AE50007EE018; + remoteInfo = AltStoreCore; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -382,6 +404,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF98917B250AABF4002ACF50 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + BF989177250AABF4002ACF50 /* AltWidgetExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -425,6 +458,7 @@ BF3F786322CAA41E008FBD20 /* ALTDeviceManager+Installation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTDeviceManager+Installation.swift"; sourceTree = ""; }; BF41B805233423AE00C593A3 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; BF41B807233433C100C593A3 /* LoadingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingState.swift; sourceTree = ""; }; + BF42345825101C1D006D1EB2 /* WidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetView.swift; sourceTree = ""; }; BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LaunchAtLogin.framework; path = Carthage/Build/Mac/LaunchAtLogin.framework; sourceTree = ""; }; BF44EEEF246B08BA002A52F2 /* BackupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupController.swift; sourceTree = ""; }; BF44EEF2246B3A17002A52F2 /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = ""; }; @@ -592,6 +626,7 @@ BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundTaskManager.swift; sourceTree = ""; }; BF770E6822BD57DD002A40FE /* Silence.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Silence.m4a; sourceTree = ""; }; BF88F97124F8727D00BB75DF /* AppManagerErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManagerErrors.swift; sourceTree = ""; }; + BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltWidgetExtension.entitlements; sourceTree = ""; }; BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnisetteDataManager.swift; sourceTree = ""; }; BF8CAE432489E772004D6CCE /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = ""; }; BF8CAE442489E772004D6CCE /* RequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestHandler.swift; sourceTree = ""; }; @@ -599,6 +634,12 @@ BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Jailbreak.swift"; sourceTree = ""; }; BF8F69C122E659F700049BA1 /* AppContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewController.swift; sourceTree = ""; }; BF8F69C322E662D300049BA1 /* AppViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = ""; }; + BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AltWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + BF989170250AABF4002ACF50 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BF989172250AABF4002ACF50 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BF98917C250AAC4F002ACF50 /* Countdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Countdown.swift; sourceTree = ""; }; + BF98917D250AAC4F002ACF50 /* AltWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AltWidget.swift; sourceTree = ""; }; + BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = ViewApp.intentdefinition; sourceTree = ""; }; BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseViewController.swift; sourceTree = ""; }; BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseCollectionViewCell.swift; sourceTree = ""; }; BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotCollectionViewCell.swift; sourceTree = ""; }; @@ -759,6 +800,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF989164250AABF3002ACF50 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BFD247672284B9A500981D42 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1054,6 +1103,7 @@ BF66EEE32501AED0007EE018 /* Extensions */, BF66EEAA2501AECA007EE018 /* Model */, BF66EE9F2501AEC5007EE018 /* Patreon */, + BF98918B250AAE18002ACF50 /* Intents */, BF66EE9A2501AEC1007EE018 /* Protocols */, BF66EE8D2501AEBC007EE018 /* Types */, BF66EE812501AE50007EE018 /* Info.plist */, @@ -1217,6 +1267,27 @@ name = "Supporting Files"; sourceTree = ""; }; + BF98916C250AABF3002ACF50 /* AltWidget */ = { + isa = PBXGroup; + children = ( + BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */, + BF98917D250AAC4F002ACF50 /* AltWidget.swift */, + BF42345825101C1D006D1EB2 /* WidgetView.swift */, + BF98917C250AAC4F002ACF50 /* Countdown.swift */, + BF989170250AABF4002ACF50 /* Assets.xcassets */, + BF989172250AABF4002ACF50 /* Info.plist */, + ); + path = AltWidget; + sourceTree = ""; + }; + BF98918B250AAE18002ACF50 /* Intents */ = { + isa = PBXGroup; + children = ( + BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */, + ); + path = Intents; + sourceTree = ""; + }; BF9ABA4322DCFF33008935CF /* Browse */ = { isa = PBXGroup; children = ( @@ -1288,6 +1359,7 @@ BF5C5FC6237DF5AE00EDD0C6 /* AltPlugin */, BF58047C246A28F7008AE704 /* AltBackup */, BF18BFE824857D7900DD5981 /* AltDaemon */, + BF98916C250AABF3002ACF50 /* AltWidget */, BFD247852284BB3300981D42 /* Frameworks */, BFD2476B2284B9A500981D42 /* Products */, 4460E048E3AC1C9708C4FA33 /* Pods */, @@ -1304,6 +1376,7 @@ BF58047B246A28F7008AE704 /* AltBackup.app */, BF18BFE724857D7900DD5981 /* AltDaemon */, BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */, + BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -1716,6 +1789,24 @@ productReference = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; productType = "com.apple.product-type.framework"; }; + BF989166250AABF3002ACF50 /* AltWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "AltWidgetExtension" */; + buildPhases = ( + BF989163250AABF3002ACF50 /* Sources */, + BF989164250AABF3002ACF50 /* Frameworks */, + BF989165250AABF3002ACF50 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BFF615AB2510042B00484D3B /* PBXTargetDependency */, + ); + name = AltWidgetExtension; + productName = AltWidgetExtension; + productReference = BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; BFD247692284B9A500981D42 /* AltStore */ = { isa = PBXNativeTarget; buildConfigurationList = BFD2477E2284B9A700981D42 /* Build configuration list for PBXNativeTarget "AltStore" */; @@ -1726,11 +1817,13 @@ BFD247682284B9A500981D42 /* Resources */, BF088D2B2501A087008082D9 /* Embed Frameworks */, 744AE3B03F6BF664FC5705C5 /* [CP] Embed Pods Frameworks */, + BF98917B250AABF4002ACF50 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( BF66EE842501AE50007EE018 /* PBXTargetDependency */, + BF989176250AABF4002ACF50 /* PBXTargetDependency */, ); name = AltStore; productName = AltStore; @@ -1776,6 +1869,10 @@ BF66EE7D2501AE50007EE018 = { CreatedOnToolsVersion = 12.0; }; + BF989166250AABF3002ACF50 = { + CreatedOnToolsVersion = 12.0; + LastSwiftMigration = 1200; + }; BFD247692284B9A500981D42 = { CreatedOnToolsVersion = 10.2.1; LastSwiftMigration = 1020; @@ -1810,6 +1907,7 @@ BF58047A246A28F7008AE704 /* AltBackup */, BF18BFE624857D7900DD5981 /* AltDaemon */, BF66EE7D2501AE50007EE018 /* AltStoreCore */, + BF989166250AABF3002ACF50 /* AltWidgetExtension */, ); }; /* End PBXProject section */ @@ -1848,6 +1946,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF989165250AABF3002ACF50 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF989171250AABF4002ACF50 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BFD247682284B9A500981D42 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2155,6 +2261,7 @@ BFAECC5B2501B0A400528F27 /* Bundle+AltStore.swift in Sources */, BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */, BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */, + BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */, BFAECC522501B0A400528F27 /* CodableServerError.swift in Sources */, BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */, BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */, @@ -2204,6 +2311,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF989163250AABF3002ACF50 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */, + BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */, + BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BFD247662284B9A500981D42 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2281,6 +2398,7 @@ BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */, BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */, BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */, + BFBE0004250ACFFB0080826E /* ViewApp.intentdefinition in Sources */, BF770E5622BC3C03002A40FE /* Server.swift in Sources */, BFA8172923C56042001B5953 /* ServerConnection.swift in Sources */, BF56D2AF23DF9E310006506D /* AppIDsViewController.swift in Sources */, @@ -2300,11 +2418,21 @@ target = BF66EE7D2501AE50007EE018 /* AltStoreCore */; targetProxy = BF66EE832501AE50007EE018 /* PBXContainerItemProxy */; }; + BF989176250AABF4002ACF50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BF989166250AABF3002ACF50 /* AltWidgetExtension */; + targetProxy = BF989175250AABF4002ACF50 /* PBXContainerItemProxy */; + }; BFBFFB272380C72F00993A4A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BF5C5FC4237DF5AE00EDD0C6 /* AltPlugin */; targetProxy = BFBFFB262380C72F00993A4A /* PBXContainerItemProxy */; }; + BFF615AB2510042B00484D3B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BF66EE7D2501AE50007EE018 /* AltStoreCore */; + targetProxy = BFF615AA2510042B00484D3B /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -2772,6 +2900,72 @@ }; name = Release; }; + BF989179250AABF4002ACF50 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 6XVY5G3U44; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/iossimulator", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/ios", + ); + INFOPLIST_FILE = AltWidget/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore.AltWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BF98917A250AABF4002ACF50 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 6XVY5G3U44; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/iossimulator", + "$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/ios", + ); + INFOPLIST_FILE = AltWidget/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore.AltWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; BFD2477C2284B9A700981D42 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3014,6 +3208,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "AltWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF989179250AABF4002ACF50 /* Debug */, + BF98917A250AABF4002ACF50 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; BFD247652284B9A500981D42 /* Build configuration list for PBXProject "AltStore" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/AltStore/Managing Apps/AppManager.swift b/AltStore/Managing Apps/AppManager.swift index e5950e12..ce905253 100644 --- a/AltStore/Managing Apps/AppManager.swift +++ b/AltStore/Managing Apps/AppManager.swift @@ -12,6 +12,7 @@ import UserNotifications import MobileCoreServices import Intents import Combine +import WidgetKit import AltStoreCore import AltSign @@ -1346,6 +1347,16 @@ private extension AppManager AnalyticsManager.shared.trackEvent(event) } + if #available(iOS 14, *) + { + WidgetCenter.shared.getCurrentConfigurations { (result) in + guard case .success(let widgets) = result else { return } + + guard let widget = widgets.first(where: { $0.configuration is ViewAppIntent }) else { return } + WidgetCenter.shared.reloadTimelines(ofKind: widget.kind) + } + } + do { try installedApp.managedObjectContext?.save() } catch { print("Error saving installed app.", error) } } diff --git a/AltStoreCore/Intents/ViewApp.intentdefinition b/AltStoreCore/Intents/ViewApp.intentdefinition new file mode 100644 index 00000000..0ef32fe2 --- /dev/null +++ b/AltStoreCore/Intents/ViewApp.intentdefinition @@ -0,0 +1,179 @@ + + + + + INEnums + + INIntentDefinitionModelVersion + 1.2 + INIntentDefinitionNamespace + TPucbY + INIntentDefinitionSystemVersion + 20A5354i + INIntentDefinitionToolsBuildVersion + 12A8189n + INIntentDefinitionToolsVersion + 12.0 + INIntents + + + INIntentCategory + information + INIntentConfigurable + + INIntentDescription + Select App + INIntentDescriptionID + sb9c7F + INIntentEligibleForWidgets + + INIntentIneligibleForSuggestions + + INIntentLastParameterTag + 2 + INIntentManagedParameterCombinations + + app + + INIntentParameterCombinationSupportsBackgroundExecution + + INIntentParameterCombinationUpdatesLinked + + + + INIntentName + ViewApp + INIntentParameters + + + INIntentParameterConfigurable + + INIntentParameterDisplayName + App + INIntentParameterDisplayNameID + QwLCXY + INIntentParameterDisplayPriority + 1 + INIntentParameterName + app + INIntentParameterObjectType + App + INIntentParameterObjectTypeNamespace + TPucbY + INIntentParameterPromptDialogs + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Configuration + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Primary + + + INIntentParameterSupportsDynamicEnumeration + + INIntentParameterTag + 2 + INIntentParameterType + Object + + + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseCodeName + failure + + + + INIntentTitle + View App + INIntentTitleID + 7aGoWn + INIntentType + Custom + INIntentVerb + View + + + INTypes + + + INTypeDisplayName + App + INTypeDisplayNameID + cUl1NZ + INTypeLastPropertyTag + 99 + INTypeName + App + INTypeProperties + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 1 + INTypePropertyName + identifier + INTypePropertyTag + 1 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 2 + INTypePropertyName + displayString + INTypePropertyTag + 2 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 3 + INTypePropertyName + pronunciationHint + INTypePropertyTag + 3 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 4 + INTypePropertyName + alternativeSpeakableMatches + INTypePropertySupportsMultipleValues + + INTypePropertyTag + 4 + INTypePropertyType + SpeakableString + + + + + + diff --git a/AltStoreCore/Model/DatabaseManager.swift b/AltStoreCore/Model/DatabaseManager.swift index cee2fa0b..26c44276 100644 --- a/AltStoreCore/Model/DatabaseManager.swift +++ b/AltStoreCore/Model/DatabaseManager.swift @@ -145,6 +145,8 @@ private extension DatabaseManager { func prepareDatabase(completionHandler: @escaping (Result) -> Void) { + guard !Bundle.isAppExtension() else { return completionHandler(.success(())) } + let context = self.persistentContainer.newBackgroundContext() context.performAndWait { guard let localApp = ALTApplication(fileURL: Bundle.main.bundleURL) else { return } diff --git a/AltWidget/AltWidget.swift b/AltWidget/AltWidget.swift new file mode 100644 index 00000000..4754993a --- /dev/null +++ b/AltWidget/AltWidget.swift @@ -0,0 +1,173 @@ +// +// AltWidget.swift +// AltWidget +// +// Created by Riley Testut on 6/26/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import SwiftUI +import WidgetKit +import UIKit +import CoreData + +import AltStoreCore +import AltSign + +struct AppEntry: TimelineEntry +{ + var date: Date + var relevance: TimelineEntryRelevance? + + var app: AppSnapshot? + var isPlaceholder: Bool = false +} + +struct AppSnapshot +{ + var name: String + var bundleIdentifier: String + var expirationDate: Date + var refreshedDate: Date + + var tintColor: UIColor? + var icon: UIImage? +} + +extension AppSnapshot +{ + // Declared in extension so we retain synthesized initializer. + init(installedApp: InstalledApp) + { + self.name = installedApp.name + self.bundleIdentifier = installedApp.bundleIdentifier + self.expirationDate = installedApp.expirationDate + self.refreshedDate = installedApp.refreshedDate + + self.tintColor = installedApp.storeApp?.tintColor + + let application = ALTApplication(fileURL: installedApp.fileURL) + self.icon = application?.icon?.resizing(toFill: CGSize(width: 180, height: 180)) + } +} + +struct Provider: IntentTimelineProvider +{ + typealias Intent = ViewAppIntent + typealias Entry = AppEntry + + func placeholder(in context: Context) -> AppEntry + { + return AppEntry(date: Date(), app: nil, isPlaceholder: true) + } + + func getSnapshot(for configuration: ViewAppIntent, in context: Context, completion: @escaping (AppEntry) -> Void) + { + self.prepare { (result) in + do + { + let context = try result.get() + let snapshot = InstalledApp.fetchAltStore(in: context).map(AppSnapshot.init) + + let entry = AppEntry(date: Date(), app: snapshot) + completion(entry) + } + catch + { + print("Error preparing widget snapshot:", error) + + let entry = AppEntry(date: Date(), app: nil) + completion(entry) + } + } + } + + func getTimeline(for configuration: ViewAppIntent, in context: Context, completion: @escaping (Timeline) -> Void) { + self.prepare { (result) in + autoreleasepool { + do + { + let context = try result.get() + + let installedApp: InstalledApp? + + if let identifier = configuration.app?.identifier + { + let app = InstalledApp.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(InstalledApp.bundleIdentifier), identifier), + in: context) + installedApp = app + } + else + { + installedApp = InstalledApp.fetchAltStore(in: context) + } + + let snapshot = installedApp.map(AppSnapshot.init) + + var entries: [AppEntry] = [] + + // Generate a timeline consisting of one entry per day. + + if let snapshot = snapshot + { + let currentDate = Calendar.current.startOfDay(for: Date()) + let numberOfDays = snapshot.expirationDate.numberOfCalendarDays(since: currentDate) + + for dayOffset in 0 ..< min(numberOfDays, 7) + { + guard let entryDate = Calendar.current.date(byAdding: .day, value: dayOffset, to: currentDate) else { continue } + + let score = Float(dayOffset + 1) / Float(numberOfDays) + let entry = AppEntry(date: entryDate, relevance: TimelineEntryRelevance(score: score), app: snapshot) + entries.append(entry) + } + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } + catch + { + print("Error preparing widget timeline:", error) + + let entry = AppEntry(date: Date(), app: nil) + let timeline = Timeline(entries: [entry], policy: .atEnd) + completion(timeline) + } + } + } + } + + private func prepare(completion: @escaping (Result) -> Void) + { + DatabaseManager.shared.start { (error) in + if let error = error + { + completion(.failure(error)) + } + else + { + DatabaseManager.shared.viewContext.perform { + completion(.success(DatabaseManager.shared.viewContext)) + } + } + } + } +} + +@main +struct AltWidget: Widget +{ + private let kind: String = "AppDetail" + + public var body: some WidgetConfiguration { + return IntentConfiguration(kind: kind, + intent: ViewAppIntent.self, + provider: Provider()) { (entry) in + WidgetView(entry: entry) + } + .supportedFamilies([.systemSmall]) + .configurationDisplayName("AltWidget") + .description("View remaining days until your sideloaded apps expire.") + } +} diff --git a/AltWidget/AltWidgetExtension.entitlements b/AltWidget/AltWidgetExtension.entitlements new file mode 100644 index 00000000..099f1e39 --- /dev/null +++ b/AltWidget/AltWidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.rileytestut.AltStore + + + diff --git a/AltWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/AltWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/AltWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Assets.xcassets/AltStore.imageset/Contents.json b/AltWidget/Assets.xcassets/AltStore.imageset/Contents.json new file mode 100644 index 00000000..c371f7ac --- /dev/null +++ b/AltWidget/Assets.xcassets/AltStore.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 23_120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 23_180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Assets.xcassets/AltStore.imageset/Group 23_120.png b/AltWidget/Assets.xcassets/AltStore.imageset/Group 23_120.png new file mode 100644 index 00000000..3626fdb7 Binary files /dev/null and b/AltWidget/Assets.xcassets/AltStore.imageset/Group 23_120.png differ diff --git a/AltWidget/Assets.xcassets/AltStore.imageset/Group 23_180.png b/AltWidget/Assets.xcassets/AltStore.imageset/Group 23_180.png new file mode 100644 index 00000000..245d2785 Binary files /dev/null and b/AltWidget/Assets.xcassets/AltStore.imageset/Group 23_180.png differ diff --git a/AltWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/AltWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/AltWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Assets.xcassets/Badge.imageset/Contents.json b/AltWidget/Assets.xcassets/Badge.imageset/Contents.json new file mode 100644 index 00000000..be20e8a1 --- /dev/null +++ b/AltWidget/Assets.xcassets/Badge.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "group16Copy2.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Assets.xcassets/Badge.imageset/group16Copy2.pdf b/AltWidget/Assets.xcassets/Badge.imageset/group16Copy2.pdf new file mode 100644 index 00000000..da2ea1e1 Binary files /dev/null and b/AltWidget/Assets.xcassets/Badge.imageset/group16Copy2.pdf differ diff --git a/AltWidget/Assets.xcassets/Contents.json b/AltWidget/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/AltWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Assets.xcassets/Delta.imageset/Contents.json b/AltWidget/Assets.xcassets/Delta.imageset/Contents.json new file mode 100644 index 00000000..543305d2 --- /dev/null +++ b/AltWidget/Assets.xcassets/Delta.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon-120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon-180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Assets.xcassets/Delta.imageset/icon-120.png b/AltWidget/Assets.xcassets/Delta.imageset/icon-120.png new file mode 100644 index 00000000..1f0e424c Binary files /dev/null and b/AltWidget/Assets.xcassets/Delta.imageset/icon-120.png differ diff --git a/AltWidget/Assets.xcassets/Delta.imageset/icon-180.png b/AltWidget/Assets.xcassets/Delta.imageset/icon-180.png new file mode 100644 index 00000000..7118e823 Binary files /dev/null and b/AltWidget/Assets.xcassets/Delta.imageset/icon-180.png differ diff --git a/AltWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/AltWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/AltWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AltWidget/Countdown.swift b/AltWidget/Countdown.swift new file mode 100644 index 00000000..cb705627 --- /dev/null +++ b/AltWidget/Countdown.swift @@ -0,0 +1,79 @@ +// +// Countdown.swift +// AltWidgetExtension +// +// Created by Riley Testut on 7/6/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import SwiftUI +import WidgetKit + +struct Countdown: View +{ + let startDate: Date? + let endDate: Date? + + @Environment(\.font) private var font + + private var numberOfDays: Int { + guard let date = self.endDate else { return 0 } + + let numberOfDays = date.numberOfCalendarDays(since: Date()) + return numberOfDays + } + + private var fractionComplete: CGFloat { + guard let startDate = self.startDate, let endDate = self.endDate else { return 1.0 } + + let totalNumberOfDays = endDate.numberOfCalendarDays(since: startDate) + let fractionComplete = CGFloat(self.numberOfDays) / CGFloat(totalNumberOfDays) + return fractionComplete + } + + @ViewBuilder + private func overlay(progress: CGFloat) -> some View + { + let strokeStyle = StrokeStyle(lineWidth: 4.0, lineCap: .round, lineJoin: .round) + + if self.numberOfDays > 9 || self.numberOfDays < 0 { + Capsule(style: .continuous) + .trim(from: 0.0, to: progress) + .stroke(style: strokeStyle) + } + else { + Circle() + .trim(from: 0.0, to: progress) + .rotation(Angle(degrees: -90), anchor: .center) + .stroke(style: strokeStyle) + } + } + + var body: some View { + Text("\(numberOfDays)") + .font((font ?? .title).monospacedDigit()) + .bold() + .opacity(endDate != nil ? 1 : 0) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .overlay( + ZStack { + overlay(progress: 1.0) + .opacity(0.3) + + overlay(progress: fractionComplete) + } + ) + } +} + +struct Countdown_Previews: PreviewProvider { + static var previews: some View { + let startDate = Calendar.current.date(byAdding: .day, value: -2, to: Date()) ?? Date() + Group { + Countdown(startDate: startDate, endDate: Calendar.current.date(byAdding: .day, value: 7, to: startDate)) + Countdown(startDate: startDate, endDate: Calendar.current.date(byAdding: .day, value: 365, to: startDate)) + } + .previewContext(WidgetPreviewContext(family: .systemSmall)) + } +} diff --git a/AltWidget/Info.plist b/AltWidget/Info.plist new file mode 100644 index 00000000..b0b95893 --- /dev/null +++ b/AltWidget/Info.plist @@ -0,0 +1,33 @@ + + + + + ALTAppGroups + + group.com.rileytestut.AltStore + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + AltWidget + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/AltWidget/WidgetView.swift b/AltWidget/WidgetView.swift new file mode 100644 index 00000000..451ac9f3 --- /dev/null +++ b/AltWidget/WidgetView.swift @@ -0,0 +1,170 @@ +// +// WidgetView.swift +// AltStore +// +// Created by Riley Testut on 9/14/20. +// Copyright © 2020 Riley Testut. All rights reserved. +// + +import WidgetKit +import SwiftUI + +import AltStoreCore +import AltSign +import CoreData + +struct WidgetView : View +{ + var entry: AppEntry + + var body: some View { + Group { + if let app = self.entry.app + { + let daysRemaining = app.expirationDate.numberOfCalendarDays(since: Date()) + + GeometryReader { (geometry) in + Group { + VStack(alignment: .leading) { + VStack(alignment: .leading, spacing: 5) { + let imageHeight = geometry.size.height * 0.45 + + app.icon.map { + Image(uiImage: $0) + .resizable() + .aspectRatio(CGSize(width: 1, height: 1), contentMode: .fit) + .frame(height: imageHeight) + .mask(RoundedRectangle(cornerRadius: imageHeight / 5.0, style: .continuous)) + } + + Text(app.name.uppercased()) + .font(.system(size: 12, weight: .semibold, design: .rounded)) + .foregroundColor(.white) + .lineLimit(1) + .minimumScaleFactor(0.5) + } + + HStack(alignment: .bottom) { + ( + Text("Expires in\n") + .font(.system(size: 13, weight: .semibold, design: .rounded)) + .foregroundColor(Color.white.opacity(0.45)) + + + Text(daysRemaining == 1 ? "1 day" : "\(daysRemaining) days") + .font(.system(size: 15, weight: .semibold, design: .rounded)) + .foregroundColor(.white) + ) + .lineLimit(2) + .lineSpacing(1.0) + .minimumScaleFactor(0.5) + + Spacer() + + Countdown(startDate: app.refreshedDate, endDate: app.expirationDate) + .font(.system(size: 20, weight: .semibold, design: .rounded)) + .foregroundColor(Color.white) + .opacity(0.8) + .fixedSize(horizontal: true, vertical: false) + .offset(x: 5) + } + .offset(y: 5) // Offset so we don't affect layout, but still leave space between app name and Countdown. + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .padding() + } + } + else + { + VStack { + // Put conditional inside VStack, or else an empty view will be returned + // if isPlaceholder == false, which messes up layout. + if !entry.isPlaceholder + { + Text("App Not Found") + .font(.system(.body, design: .rounded)) + .fontWeight(.semibold) + .foregroundColor(Color.white.opacity(0.4)) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .background(backgroundView(icon: entry.app?.icon, tintColor: entry.app?.tintColor)) + } +} + +private extension WidgetView +{ + func backgroundView(icon: UIImage? = nil, tintColor: UIColor? = nil) -> some View + { + let icon = icon ?? UIImage(named: "AltStore")! + let tintColor = tintColor ?? .altPrimary + + let imageHeight = 60 as CGFloat + let saturation = 1.8 + let blurRadius = 5 as CGFloat + let tintOpacity = 0.45 + + return ZStack(alignment: .topTrailing) { + // Blurred Image + GeometryReader { geometry in + ZStack { + Image(uiImage: icon) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: imageHeight, height: imageHeight, alignment: .center) + .saturation(saturation) + .blur(radius: blurRadius, opaque: true) + .scaleEffect(geometry.size.width / imageHeight, anchor: .center) + + Color(tintColor) + .opacity(tintOpacity) + } + } + + Image("Badge") + .resizable() + .frame(width: 26, height: 26) + .padding() + } + } +} + +struct WidgetView_Previews: PreviewProvider { + static var previews: some View { + let shortRefreshedDate = Calendar.current.date(byAdding: .day, value: -2, to: Date()) ?? Date() + let shortExpirationDate = Calendar.current.date(byAdding: .day, value: 7, to: shortRefreshedDate) ?? Date() + + let longRefreshedDate = Calendar.current.date(byAdding: .day, value: -100, to: Date()) ?? Date() + let longExpirationDate = Calendar.current.date(byAdding: .day, value: 365, to: longRefreshedDate) ?? Date() + + let altstore = AppSnapshot(name: "AltStore", + bundleIdentifier: "com.rileytestut.AltStore", + expirationDate: shortExpirationDate, + refreshedDate: shortRefreshedDate, + tintColor: .altPrimary, + icon: UIImage(named: "AltStore")) + + let delta = AppSnapshot(name: "Delta", + bundleIdentifier: "com.rileytestut.Delta", + expirationDate: longExpirationDate, + refreshedDate: longRefreshedDate, + tintColor: .deltaPrimary, + icon: UIImage(named: "Delta")) + + return Group { + WidgetView(entry: AppEntry(date: Date(), app: altstore)) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + + WidgetView(entry: AppEntry(date: Date(), app: delta)) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + + WidgetView(entry: AppEntry(date: Date(), app: nil)) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + + WidgetView(entry: AppEntry(date: Date(), app: nil, isPlaceholder: true)) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + } + } +}