From 5abf7a5a11256d08d9a84a3ccc567e9c2e1e0485 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 15 Sep 2020 13:51:29 -0700 Subject: [PATCH] [AltWidget] Initial version --- AltStore.xcodeproj/project.pbxproj | 203 ++++++++++++++++++ AltStore/Managing Apps/AppManager.swift | 11 + AltStoreCore/Intents/ViewApp.intentdefinition | 179 +++++++++++++++ AltStoreCore/Model/DatabaseManager.swift | 2 + AltWidget/AltWidget.swift | 173 +++++++++++++++ AltWidget/AltWidgetExtension.entitlements | 10 + .../AccentColor.colorset/Contents.json | 11 + .../AltStore.imageset/Contents.json | 22 ++ .../AltStore.imageset/Group 23_120.png | Bin 0 -> 11798 bytes .../AltStore.imageset/Group 23_180.png | Bin 0 -> 21552 bytes .../AppIcon.appiconset/Contents.json | 98 +++++++++ .../Badge.imageset/Contents.json | 12 ++ .../Badge.imageset/group16Copy2.pdf | Bin 0 -> 3213 bytes AltWidget/Assets.xcassets/Contents.json | 6 + .../Delta.imageset/Contents.json | 22 ++ .../Delta.imageset/icon-120.png | Bin 0 -> 5267 bytes .../Delta.imageset/icon-180.png | Bin 0 -> 8235 bytes .../WidgetBackground.colorset/Contents.json | 11 + AltWidget/Countdown.swift | 79 +++++++ AltWidget/Info.plist | 33 +++ AltWidget/WidgetView.swift | 170 +++++++++++++++ 21 files changed, 1042 insertions(+) create mode 100644 AltStoreCore/Intents/ViewApp.intentdefinition create mode 100644 AltWidget/AltWidget.swift create mode 100644 AltWidget/AltWidgetExtension.entitlements create mode 100644 AltWidget/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 AltWidget/Assets.xcassets/AltStore.imageset/Contents.json create mode 100644 AltWidget/Assets.xcassets/AltStore.imageset/Group 23_120.png create mode 100644 AltWidget/Assets.xcassets/AltStore.imageset/Group 23_180.png create mode 100644 AltWidget/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 AltWidget/Assets.xcassets/Badge.imageset/Contents.json create mode 100644 AltWidget/Assets.xcassets/Badge.imageset/group16Copy2.pdf create mode 100644 AltWidget/Assets.xcassets/Contents.json create mode 100644 AltWidget/Assets.xcassets/Delta.imageset/Contents.json create mode 100644 AltWidget/Assets.xcassets/Delta.imageset/icon-120.png create mode 100644 AltWidget/Assets.xcassets/Delta.imageset/icon-180.png create mode 100644 AltWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 AltWidget/Countdown.swift create mode 100644 AltWidget/Info.plist create mode 100644 AltWidget/WidgetView.swift 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 0000000000000000000000000000000000000000..3626fdb785ad6343cb3944527399263dfd00a6c6 GIT binary patch literal 11798 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..da2ea1e10b3ca225c9c02d9c1d1440e52f44a138 GIT binary patch literal 3213 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 literal 0 HcmV?d00001 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)) + } + } +}