diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100755 index e7a29b2..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build - -on: - push: - workflow_dispatch: - -jobs: - build: - name: Build for iOS - runs-on: J316sAP - steps: - - name: Remove work folders - run: | - echo "before" - ls -lah ./ - rm -rf ./* || true - rm -rf ./.??* || true - echo "after" - ls -lah ./ - - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Build - env: - password: ${{ secrets.KEYCHAIN_PASSWORD }} - run: | - mv Malachite/Codesigning.example.xcconfig Malachite/Codesigning.xcconfig - xcodebuild -project Malachite.xcodeproj -target Malachite -configuration Internal CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - mkdir build/Payload && mv build/Internal-iphoneos/Malachite.app build/Payload/Malachite.app - cd build && zip -r Payload.ipa Payload/ - cd .. - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: Malachite.ipa - path: build/Payload.ipa - - - diff --git a/CHANGELOG.md b/CHANGELOG.md index 451aaf7..f1be902 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,21 @@ # 1.0.0 (build xxx) - +**NOTE:** The build number is unrelated to previous build numbers because of an Apple Developer Program account change. +- Bump Xcode project version to Xcode 26.3 + - Xcode 26.3 runs on macOS Sequoia 15.6 and later +- Add support for the front camera! +- Introduce Enhanced Security capability for iOS and iPadOS 26 +- Fixed an issue where HDR would show as enabled in Settings on cameras that do not support it +- Fixed an issue where symbols on buttons in the viewfinder would be hard to see + - Addressed by configuring the color on iOS and iPadOS 17 + - Addressed by using labels on iOS and iPadOS 18 +- Fixed Liquid Glass issues on iOS and iPadOS 26.1 beta 2 and later + - Addressed by removing corner radius and masking +- **Renamed Malachite to mlchtCamera** + - Malachite Remote also gets a rename to mlchtRemote + - Issues with App Store Connect and bundle IDs, I love Apple... # 1.0.0 (build 54) +**NOTE:** The build number is unrelated to previous build numbers because of an Apple Developer Program account change. - **Increased the minimum version requirement from iOS 14.1 to iOS 15** - Supporting the few users on iOS 14 is no longer worth the extra complexity for my workflow diff --git a/Malachite.xcodeproj/project.pbxproj b/Malachite.xcodeproj/project.pbxproj deleted file mode 100755 index ff0b8f5..0000000 --- a/Malachite.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1708 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 780067E12CBDCEA0004BB595 /* ControlCenterWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780067CF2CBDCB94004BB595 /* ControlCenterWidget.swift */; }; - 7806973A2B27F66E00B4942B /* MalachiteTooltipUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780697392B27F66E00B4942B /* MalachiteTooltipUtils.swift */; }; - 78163D8E2CCB7BB300146126 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 78163D8D2CCB7BAE00146126 /* CHANGELOG.md */; }; - 782FC7762B2DAFAB007709C1 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 782FC7752B2DAFAB007709C1 /* README.md */; }; - 7837C1062E34C45B009396B0 /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A9DE2C2CD57F4D002C131D /* MalachiteIntentUtils.swift */; }; - 7837C1072E34C45B009396B0 /* ControlCenterWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780067CF2CBDCB94004BB595 /* ControlCenterWidget.swift */; }; - 7837C1082E34C45B009396B0 /* LockScreenWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78917F622B99AE72005E10FA /* LockScreenWidget.swift */; }; - 7837C1092E34C45B009396B0 /* MalachiteWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78917F602B99AE72005E10FA /* MalachiteWidgetBundle.swift */; }; - 7837C10B2E34C45B009396B0 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5D2B99AE72005E10FA /* SwiftUI.framework */; }; - 7837C10C2E34C45B009396B0 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5B2B99AE72005E10FA /* WidgetKit.framework */; }; - 7837C10E2E34C45B009396B0 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; - 7837C1102E34C45B009396B0 /* Codesigning.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; }; - 7837C1172E34C47D009396B0 /* WidgetBundleWatch.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7837C1152E34C45B009396B0 /* WidgetBundleWatch.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 78562BC32B450A7600920160 /* PRIVACY_POLICY.md in Resources */ = {isa = PBXBuildFile; fileRef = 78562BC22B450A7600920160 /* PRIVACY_POLICY.md */; }; - 7859DE1A2B8308C000D1A998 /* MalachiteAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7859DE192B8308C000D1A998 /* MalachiteAboutView.swift */; }; - 785F084D2B12D41100244EB4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F084C2B12D41100244EB4 /* AppDelegate.swift */; }; - 785F084F2B12D41100244EB4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F084E2B12D41100244EB4 /* SceneDelegate.swift */; }; - 785F08512B12D41100244EB4 /* MalachiteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F08502B12D41100244EB4 /* MalachiteView.swift */; }; - 785F08562B12D41300244EB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 785F08552B12D41300244EB4 /* Assets.xcassets */; }; - 785F08592B12D41300244EB4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 785F08572B12D41300244EB4 /* LaunchScreen.storyboard */; }; - 7860DC002B897A3E00450BF8 /* MalachiteGameUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7860DBFF2B897A3E00450BF8 /* MalachiteGameUtils.swift */; }; - 78789C742E3494D500862C3D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 785F08552B12D41300244EB4 /* Assets.xcassets */; }; - 787B1CAA2B8B095E000AFECC /* Malachite.docc in Sources */ = {isa = PBXBuildFile; fileRef = 787B1CA92B8B095E000AFECC /* Malachite.docc */; }; - 7881A98C2C1F77AB00B1F83B /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; - 7886C6C22E45E2E10095460C /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 78B72BA12E332744002E2D4E /* AppIcon.icon */; }; - 788848B52CC74B4200B02E37 /* MalachiteCompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788848B42CC74B3B00B02E37 /* MalachiteCompatibilityView.swift */; }; - 788E74B32B13D8200024B586 /* MalachiteSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788E74B22B13D8200024B586 /* MalachiteSettingsView.swift */; }; - 78917F5C2B99AE72005E10FA /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5B2B99AE72005E10FA /* WidgetKit.framework */; }; - 78917F5E2B99AE72005E10FA /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5D2B99AE72005E10FA /* SwiftUI.framework */; }; - 78917F612B99AE72005E10FA /* MalachiteWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78917F602B99AE72005E10FA /* MalachiteWidgetBundle.swift */; }; - 78917F632B99AE72005E10FA /* LockScreenWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78917F622B99AE72005E10FA /* LockScreenWidget.swift */; }; - 78917F692B99AE73005E10FA /* WidgetBundle.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 78917F592B99AE72005E10FA /* WidgetBundle.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 7897ECA62BA2B87100662EA0 /* MalachiteSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7897ECA52BA2B87100662EA0 /* MalachiteSettingsDetailView.swift */; }; - 78A9DD6A2CD55551002C131D /* CaptureBundle.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 78A9DD612CD55551002C131D /* CaptureBundle.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 78A9DD762CD55558002C131D /* MalachiteCaptureBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A9DD722CD55558002C131D /* MalachiteCaptureBundle.swift */; }; - 78A9DD7C2CD5562B002C131D /* MalachiteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F08502B12D41100244EB4 /* MalachiteView.swift */; }; - 78A9DD7D2CD55631002C131D /* MalachiteAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7859DE192B8308C000D1A998 /* MalachiteAboutView.swift */; }; - 78A9DD7E2CD55631002C131D /* MalachiteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDE2B27E07300812A17 /* MalachiteUtils.swift */; }; - 78A9DD7F2CD55631002C131D /* MalachiteSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788E74B22B13D8200024B586 /* MalachiteSettingsView.swift */; }; - 78A9DD812CD55631002C131D /* MalachiteFunctionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EE42B27E0A200812A17 /* MalachiteFunctionUtils.swift */; }; - 78A9DD822CD55631002C131D /* MalachitePhotoPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E5EFBF2B15748400F15250 /* MalachitePhotoPreview.swift */; }; - 78A9DD832CD55631002C131D /* MalachiteCompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788848B42CC74B3B00B02E37 /* MalachiteCompatibilityView.swift */; }; - 78A9DD842CD55631002C131D /* MalachiteViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDD2B27E07300812A17 /* MalachiteViewUtils.swift */; }; - 78A9DD852CD55631002C131D /* MalachiteSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7897ECA52BA2B87100662EA0 /* MalachiteSettingsDetailView.swift */; }; - 78A9DD862CD55631002C131D /* MalachiteTooltipUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780697392B27F66E00B4942B /* MalachiteTooltipUtils.swift */; }; - 78A9DD872CD55631002C131D /* MalachiteGameUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7860DBFF2B897A3E00450BF8 /* MalachiteGameUtils.swift */; }; - 78A9DD882CD55631002C131D /* MalachiteHapticUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDC2B27E07300812A17 /* MalachiteHapticUtils.swift */; }; - 78A9DE2D2CD57F51002C131D /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A9DE2C2CD57F4D002C131D /* MalachiteIntentUtils.swift */; }; - 78A9DE2F2CD57F51002C131D /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A9DE2C2CD57F4D002C131D /* MalachiteIntentUtils.swift */; }; - 78A9DE312CD581F1002C131D /* LockedCameraCapture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78A9DE302CD581F1002C131D /* LockedCameraCapture.framework */; }; - 78A9DE752CD5AE5F002C131D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; - 78B72BA22E332744002E2D4E /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 78B72BA12E332744002E2D4E /* AppIcon.icon */; }; - 78B72BB12E332830002E2D4E /* MalachiteWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 78B72BA72E33282E002E2D4E /* MalachiteWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 78B72BD42E3452A2002E2D4E /* build.yml in Resources */ = {isa = PBXBuildFile; fileRef = 78B72BD12E3452A2002E2D4E /* build.yml */; }; - 78B72BDA2E345301002E2D4E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B72BD62E345301002E2D4E /* ContentView.swift */; }; - 78B72BDB2E345301002E2D4E /* MalachiteWatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B72BD72E345301002E2D4E /* MalachiteWatchApp.swift */; }; - 78B72BE32E3461DA002E2D4E /* MalachiteIconMasked_Darwin25.png in Resources */ = {isa = PBXBuildFile; fileRef = 78B72BE22E3461DA002E2D4E /* MalachiteIconMasked_Darwin25.png */; }; - 78B72BE42E3461DA002E2D4E /* MalachiteIconMasked_Darwin24.png in Resources */ = {isa = PBXBuildFile; fileRef = 78B72BE12E3461DA002E2D4E /* MalachiteIconMasked_Darwin24.png */; }; - 78C7EF9D2D1ECBD1001E332B /* MalachitePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C7EF9C2D1ECBCB001E332B /* MalachitePreferences.swift */; }; - 78C7EF9E2D1ECBD1001E332B /* MalachitePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C7EF9C2D1ECBCB001E332B /* MalachitePreferences.swift */; }; - 78C80BF52CDABE7800F6E8A7 /* MalachitePreferencesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C80BF32CDABE7200F6E8A7 /* MalachitePreferencesUtils.swift */; }; - 78C80BF62CDABE7800F6E8A7 /* MalachitePreferencesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C80BF32CDABE7200F6E8A7 /* MalachitePreferencesUtils.swift */; }; - 78CAC36A2CCD99B600A35AE8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; - 78CD7DB62E359B87003814B5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 785F08552B12D41300244EB4 /* Assets.xcassets */; }; - 78CD7DB72E359B87003814B5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 785F08552B12D41300244EB4 /* Assets.xcassets */; }; - 78D570762E3C7C5E0025B9B3 /* MalachiteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F08502B12D41100244EB4 /* MalachiteView.swift */; }; - 78D570772E3C7CB20025B9B3 /* MalachiteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDE2B27E07300812A17 /* MalachiteUtils.swift */; }; - 78D570782E3C7CB80025B9B3 /* MalachiteTooltipUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780697392B27F66E00B4942B /* MalachiteTooltipUtils.swift */; }; - 78D570792E3C7CB80025B9B3 /* MalachiteViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDD2B27E07300812A17 /* MalachiteViewUtils.swift */; }; - 78D5707A2E3C7CB80025B9B3 /* MalachiteHapticUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDC2B27E07300812A17 /* MalachiteHapticUtils.swift */; }; - 78D5707B2E3C7CB80025B9B3 /* MalachiteFunctionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EE42B27E0A200812A17 /* MalachiteFunctionUtils.swift */; }; - 78D5707C2E3C7CB80025B9B3 /* MalachiteGameUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7860DBFF2B897A3E00450BF8 /* MalachiteGameUtils.swift */; }; - 78D5707D2E3C7CC60025B9B3 /* MalachitePreferencesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C80BF32CDABE7200F6E8A7 /* MalachitePreferencesUtils.swift */; }; - 78D5707E2E3C7CC60025B9B3 /* MalachitePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C7EF9C2D1ECBCB001E332B /* MalachitePreferences.swift */; }; - 78D5707F2E3C7CF50025B9B3 /* MalachitePhotoPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E5EFBF2B15748400F15250 /* MalachitePhotoPreview.swift */; }; - 78D570802E3C7D050025B9B3 /* MalachiteSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788E74B22B13D8200024B586 /* MalachiteSettingsView.swift */; }; - 78D570812E3C7D050025B9B3 /* MalachiteCompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788848B42CC74B3B00B02E37 /* MalachiteCompatibilityView.swift */; }; - 78D570822E3C7D0B0025B9B3 /* MalachiteSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7897ECA52BA2B87100662EA0 /* MalachiteSettingsDetailView.swift */; }; - 78D570832E3C7D0B0025B9B3 /* MalachiteAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7859DE192B8308C000D1A998 /* MalachiteAboutView.swift */; }; - 78D570842E3C7E530025B9B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 785F08552B12D41300244EB4 /* Assets.xcassets */; }; - 78E142CA2DD4262F0016B3DB /* Codesigning.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; }; - 78E142CB2DD4262F0016B3DB /* Codesigning.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; }; - 78E142CC2DD4262F0016B3DB /* Codesigning.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; }; - 78E5EFC02B15748400F15250 /* MalachitePhotoPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E5EFBF2B15748400F15250 /* MalachitePhotoPreview.swift */; }; - 78E67EE02B27E07300812A17 /* MalachiteHapticUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDC2B27E07300812A17 /* MalachiteHapticUtils.swift */; }; - 78E67EE12B27E07300812A17 /* MalachiteViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDD2B27E07300812A17 /* MalachiteViewUtils.swift */; }; - 78E67EE22B27E07300812A17 /* MalachiteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EDE2B27E07300812A17 /* MalachiteUtils.swift */; }; - 78E67EE52B27E0A200812A17 /* MalachiteFunctionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E67EE42B27E0A200812A17 /* MalachiteFunctionUtils.swift */; }; - 78E8265B2E349FEC00A8DB50 /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A9DE2C2CD57F4D002C131D /* MalachiteIntentUtils.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 7837C1012E34BD60009396B0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 785F08412B12D41100244EB4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 78917F582B99AE72005E10FA; - remoteInfo = WidgetBundle; - }; - 7837C1182E34C47D009396B0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 785F08412B12D41100244EB4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 7837C1042E34C45B009396B0; - remoteInfo = WidgetBundle_Watch; - }; - 78917F672B99AE73005E10FA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 785F08412B12D41100244EB4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 78917F582B99AE72005E10FA; - remoteInfo = WidgetBundle; - }; - 78A9DD682CD55551002C131D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 785F08412B12D41100244EB4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 78A9DD602CD55551002C131D; - remoteInfo = CaptureBundle; - }; - 78B72BAF2E332830002E2D4E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 785F08412B12D41100244EB4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 78B72BA62E33282E002E2D4E; - remoteInfo = "MalachiteWatch Watch App"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 7837C11A2E34C47D009396B0 /* Embed Foundation Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - 7837C1172E34C47D009396B0 /* WidgetBundleWatch.appex in Embed Foundation Extensions */, - ); - name = "Embed Foundation Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - 78917F6A2B99AE73005E10FA /* Embed Foundation Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - 78917F692B99AE73005E10FA /* WidgetBundle.appex in Embed Foundation Extensions */, - ); - name = "Embed Foundation Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - 78A9DD702CD55551002C131D /* Embed ExtensionKit Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(EXTENSIONS_FOLDER_PATH)"; - dstSubfolderSpec = 16; - files = ( - 78A9DD6A2CD55551002C131D /* CaptureBundle.appex in Embed ExtensionKit Extensions */, - ); - name = "Embed ExtensionKit Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - 78B72BB62E332830002E2D4E /* Embed Watch Content */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; - dstSubfolderSpec = 16; - files = ( - 78B72BB12E332830002E2D4E /* MalachiteWatch.app in Embed Watch Content */, - ); - name = "Embed Watch Content"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 780067CF2CBDCB94004BB595 /* ControlCenterWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlCenterWidget.swift; sourceTree = ""; }; - 780697392B27F66E00B4942B /* MalachiteTooltipUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteTooltipUtils.swift; sourceTree = ""; }; - 78163D8D2CCB7BAE00146126 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; - 782FC7752B2DAFAB007709C1 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; - 7837C0FF2E34A5B7009396B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 7837C1152E34C45B009396B0 /* WidgetBundleWatch.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetBundleWatch.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 78474D2B2D7AC879006FBB96 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; - 78562BC22B450A7600920160 /* PRIVACY_POLICY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = PRIVACY_POLICY.md; sourceTree = SOURCE_ROOT; }; - 7859DE192B8308C000D1A998 /* MalachiteAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteAboutView.swift; sourceTree = ""; }; - 785F08492B12D41100244EB4 /* Malachite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Malachite.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 785F084C2B12D41100244EB4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 785F084E2B12D41100244EB4 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 785F08502B12D41100244EB4 /* MalachiteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteView.swift; sourceTree = ""; }; - 785F08552B12D41300244EB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 785F08582B12D41300244EB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 785F085A2B12D41300244EB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7860DBFF2B897A3E00450BF8 /* MalachiteGameUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteGameUtils.swift; sourceTree = ""; }; - 787B1CA92B8B095E000AFECC /* Malachite.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Malachite.docc; sourceTree = ""; }; - 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = Malachite/Localizable.xcstrings; sourceTree = SOURCE_ROOT; }; - 788589912B8974680018E2DA /* Malachite.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Malachite.entitlements; sourceTree = ""; }; - 788848B42CC74B3B00B02E37 /* MalachiteCompatibilityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteCompatibilityView.swift; sourceTree = ""; }; - 788E74B22B13D8200024B586 /* MalachiteSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteSettingsView.swift; sourceTree = ""; }; - 78917F592B99AE72005E10FA /* WidgetBundle.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetBundle.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 78917F5B2B99AE72005E10FA /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; - 78917F5D2B99AE72005E10FA /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; - 78917F602B99AE72005E10FA /* MalachiteWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteWidgetBundle.swift; sourceTree = ""; }; - 78917F622B99AE72005E10FA /* LockScreenWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenWidget.swift; sourceTree = ""; }; - 78917F662B99AE73005E10FA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7897ECA52BA2B87100662EA0 /* MalachiteSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteSettingsDetailView.swift; sourceTree = ""; }; - 78A9DD612CD55551002C131D /* CaptureBundle.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = CaptureBundle.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 78A9DD712CD55558002C131D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 78A9DD722CD55558002C131D /* MalachiteCaptureBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteCaptureBundle.swift; sourceTree = ""; }; - 78A9DE2C2CD57F4D002C131D /* MalachiteIntentUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteIntentUtils.swift; sourceTree = ""; }; - 78A9DE302CD581F1002C131D /* LockedCameraCapture.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LockedCameraCapture.framework; path = System/Library/Frameworks/LockedCameraCapture.framework; sourceTree = SDKROOT; }; - 78B72BA12E332744002E2D4E /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = ""; }; - 78B72BA72E33282E002E2D4E /* MalachiteWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MalachiteWatch.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 78B72BCE2E3452A0002E2D4E /* ci_post_clone.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = ""; }; - 78B72BCF2E3452A0002E2D4E /* ci_post_xcodebuild.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_post_xcodebuild.sh; sourceTree = ""; }; - 78B72BD12E3452A2002E2D4E /* build.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = build.yml; sourceTree = ""; }; - 78B72BD62E345301002E2D4E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - 78B72BD72E345301002E2D4E /* MalachiteWatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteWatchApp.swift; sourceTree = ""; }; - 78B72BE12E3461DA002E2D4E /* MalachiteIconMasked_Darwin24.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MalachiteIconMasked_Darwin24.png; sourceTree = ""; }; - 78B72BE22E3461DA002E2D4E /* MalachiteIconMasked_Darwin25.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MalachiteIconMasked_Darwin25.png; sourceTree = ""; }; - 78C7EF9C2D1ECBCB001E332B /* MalachitePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachitePreferences.swift; sourceTree = ""; }; - 78C80BF32CDABE7200F6E8A7 /* MalachitePreferencesUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachitePreferencesUtils.swift; sourceTree = ""; }; - 78E142C92DD426260016B3DB /* Codesigning.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Codesigning.xcconfig; sourceTree = ""; }; - 78E5EFBF2B15748400F15250 /* MalachitePhotoPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachitePhotoPreview.swift; sourceTree = ""; }; - 78E67EDC2B27E07300812A17 /* MalachiteHapticUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MalachiteHapticUtils.swift; sourceTree = ""; }; - 78E67EDD2B27E07300812A17 /* MalachiteViewUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MalachiteViewUtils.swift; sourceTree = ""; }; - 78E67EDE2B27E07300812A17 /* MalachiteUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MalachiteUtils.swift; sourceTree = ""; }; - 78E67EE42B27E0A200812A17 /* MalachiteFunctionUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteFunctionUtils.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 7837C10A2E34C45B009396B0 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7837C10B2E34C45B009396B0 /* SwiftUI.framework in Frameworks */, - 7837C10C2E34C45B009396B0 /* WidgetKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78917F562B99AE72005E10FA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 78917F5E2B99AE72005E10FA /* SwiftUI.framework in Frameworks */, - 78917F5C2B99AE72005E10FA /* WidgetKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78A9DD5E2CD55551002C131D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 78A9DE312CD581F1002C131D /* LockedCameraCapture.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78B72BA42E33282E002E2D4E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 7859DE182B83089500D1A998 /* Views */ = { - isa = PBXGroup; - children = ( - 788848B42CC74B3B00B02E37 /* MalachiteCompatibilityView.swift */, - 785F08502B12D41100244EB4 /* MalachiteView.swift */, - 78E5EFBF2B15748400F15250 /* MalachitePhotoPreview.swift */, - 788E74B22B13D8200024B586 /* MalachiteSettingsView.swift */, - 7859DE192B8308C000D1A998 /* MalachiteAboutView.swift */, - 7897ECA52BA2B87100662EA0 /* MalachiteSettingsDetailView.swift */, - ); - name = Views; - path = Malachite/Views; - sourceTree = SOURCE_ROOT; - }; - 785F08402B12D41100244EB4 = { - isa = PBXGroup; - children = ( - 78474D2B2D7AC879006FBB96 /* .gitignore */, - 78163D8D2CCB7BAE00146126 /* CHANGELOG.md */, - 782FC7752B2DAFAB007709C1 /* README.md */, - 78562BC22B450A7600920160 /* PRIVACY_POLICY.md */, - 78B72BD32E3452A2002E2D4E /* .github */, - 78B72BD02E3452A0002E2D4E /* ci_scripts */, - 785F084B2B12D41100244EB4 /* Malachite */, - 78B72BD82E345301002E2D4E /* MalachiteWatch */, - 78917F5A2B99AE72005E10FA /* Frameworks */, - 785F084A2B12D41100244EB4 /* Products */, - ); - sourceTree = ""; - }; - 785F084A2B12D41100244EB4 /* Products */ = { - isa = PBXGroup; - children = ( - 785F08492B12D41100244EB4 /* Malachite.app */, - 78917F592B99AE72005E10FA /* WidgetBundle.appex */, - 78A9DD612CD55551002C131D /* CaptureBundle.appex */, - 78B72BA72E33282E002E2D4E /* MalachiteWatch.app */, - 7837C1152E34C45B009396B0 /* WidgetBundleWatch.appex */, - ); - name = Products; - sourceTree = ""; - }; - 785F084B2B12D41100244EB4 /* Malachite */ = { - isa = PBXGroup; - children = ( - 78B72BCD2E34529A002E2D4E /* Assets */, - 78E142C92DD426260016B3DB /* Codesigning.xcconfig */, - 787B1CA92B8B095E000AFECC /* Malachite.docc */, - 788589912B8974680018E2DA /* Malachite.entitlements */, - 785F085A2B12D41300244EB4 /* Info.plist */, - 7859DE182B83089500D1A998 /* Views */, - 78E67EDB2B27E05C00812A17 /* Utilities */, - 78A9DD742CD55558002C131D /* CaptureBundle */, - 78917F5F2B99AE72005E10FA /* WidgetBundle */, - 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */, - 785F08572B12D41300244EB4 /* LaunchScreen.storyboard */, - 785F084C2B12D41100244EB4 /* AppDelegate.swift */, - 785F084E2B12D41100244EB4 /* SceneDelegate.swift */, - ); - path = Malachite; - sourceTree = ""; - }; - 78917F5A2B99AE72005E10FA /* Frameworks */ = { - isa = PBXGroup; - children = ( - 78A9DE302CD581F1002C131D /* LockedCameraCapture.framework */, - 78917F5B2B99AE72005E10FA /* WidgetKit.framework */, - 78917F5D2B99AE72005E10FA /* SwiftUI.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 78917F5F2B99AE72005E10FA /* WidgetBundle */ = { - isa = PBXGroup; - children = ( - 78917F602B99AE72005E10FA /* MalachiteWidgetBundle.swift */, - 78917F622B99AE72005E10FA /* LockScreenWidget.swift */, - 780067CF2CBDCB94004BB595 /* ControlCenterWidget.swift */, - 78917F662B99AE73005E10FA /* Info.plist */, - ); - path = WidgetBundle; - sourceTree = ""; - }; - 78A8FEEE2D2B218400BCA9C5 /* Preferences */ = { - isa = PBXGroup; - children = ( - 78C7EF9C2D1ECBCB001E332B /* MalachitePreferences.swift */, - 78C80BF32CDABE7200F6E8A7 /* MalachitePreferencesUtils.swift */, - ); - path = Preferences; - sourceTree = ""; - }; - 78A9DD742CD55558002C131D /* CaptureBundle */ = { - isa = PBXGroup; - children = ( - 78A9DD712CD55558002C131D /* Info.plist */, - 78A9DD722CD55558002C131D /* MalachiteCaptureBundle.swift */, - ); - path = CaptureBundle; - sourceTree = ""; - }; - 78B72BCD2E34529A002E2D4E /* Assets */ = { - isa = PBXGroup; - children = ( - 785F08552B12D41300244EB4 /* Assets.xcassets */, - 78B72BA12E332744002E2D4E /* AppIcon.icon */, - 78B72BE12E3461DA002E2D4E /* MalachiteIconMasked_Darwin24.png */, - 78B72BE22E3461DA002E2D4E /* MalachiteIconMasked_Darwin25.png */, - ); - path = Assets; - sourceTree = ""; - }; - 78B72BD02E3452A0002E2D4E /* ci_scripts */ = { - isa = PBXGroup; - children = ( - 78B72BCE2E3452A0002E2D4E /* ci_post_clone.sh */, - 78B72BCF2E3452A0002E2D4E /* ci_post_xcodebuild.sh */, - ); - path = ci_scripts; - sourceTree = ""; - }; - 78B72BD22E3452A2002E2D4E /* workflows */ = { - isa = PBXGroup; - children = ( - 78B72BD12E3452A2002E2D4E /* build.yml */, - ); - path = workflows; - sourceTree = ""; - }; - 78B72BD32E3452A2002E2D4E /* .github */ = { - isa = PBXGroup; - children = ( - 78B72BD22E3452A2002E2D4E /* workflows */, - ); - path = .github; - sourceTree = ""; - }; - 78B72BD82E345301002E2D4E /* MalachiteWatch */ = { - isa = PBXGroup; - children = ( - 7837C0FF2E34A5B7009396B0 /* Info.plist */, - 78B72BD62E345301002E2D4E /* ContentView.swift */, - 78B72BD72E345301002E2D4E /* MalachiteWatchApp.swift */, - ); - path = MalachiteWatch; - sourceTree = ""; - }; - 78E67EDB2B27E05C00812A17 /* Utilities */ = { - isa = PBXGroup; - children = ( - 78A8FEEE2D2B218400BCA9C5 /* Preferences */, - 78A9DE2C2CD57F4D002C131D /* MalachiteIntentUtils.swift */, - 78E67EDE2B27E07300812A17 /* MalachiteUtils.swift */, - 78E67EDC2B27E07300812A17 /* MalachiteHapticUtils.swift */, - 78E67EDD2B27E07300812A17 /* MalachiteViewUtils.swift */, - 78E67EE42B27E0A200812A17 /* MalachiteFunctionUtils.swift */, - 780697392B27F66E00B4942B /* MalachiteTooltipUtils.swift */, - 7860DBFF2B897A3E00450BF8 /* MalachiteGameUtils.swift */, - ); - path = Utilities; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 7837C1042E34C45B009396B0 /* WidgetBundleWatch */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7837C1112E34C45B009396B0 /* Build configuration list for PBXNativeTarget "WidgetBundleWatch" */; - buildPhases = ( - 7837C1052E34C45B009396B0 /* Sources */, - 7837C10A2E34C45B009396B0 /* Frameworks */, - 7837C10D2E34C45B009396B0 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = WidgetBundleWatch; - productName = MalachiteWidgetBundle; - productReference = 7837C1152E34C45B009396B0 /* WidgetBundleWatch.appex */; - productType = "com.apple.product-type.app-extension"; - }; - 785F08482B12D41100244EB4 /* Malachite */ = { - isa = PBXNativeTarget; - buildConfigurationList = 785F085D2B12D41300244EB4 /* Build configuration list for PBXNativeTarget "Malachite" */; - buildPhases = ( - 785F08452B12D41100244EB4 /* Sources */, - 785F08472B12D41100244EB4 /* Resources */, - 78917F6A2B99AE73005E10FA /* Embed Foundation Extensions */, - 78A9DD702CD55551002C131D /* Embed ExtensionKit Extensions */, - 78B72BB62E332830002E2D4E /* Embed Watch Content */, - 78329B0F2C22699A006D326F /* Embed build information */, - ); - buildRules = ( - ); - dependencies = ( - 78917F682B99AE73005E10FA /* PBXTargetDependency */, - 78A9DD692CD55551002C131D /* PBXTargetDependency */, - 78B72BB02E332830002E2D4E /* PBXTargetDependency */, - ); - name = Malachite; - productName = Malachite; - productReference = 785F08492B12D41100244EB4 /* Malachite.app */; - productType = "com.apple.product-type.application"; - }; - 78917F582B99AE72005E10FA /* WidgetBundle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 78917F6D2B99AE73005E10FA /* Build configuration list for PBXNativeTarget "WidgetBundle" */; - buildPhases = ( - 78917F552B99AE72005E10FA /* Sources */, - 78917F562B99AE72005E10FA /* Frameworks */, - 78917F572B99AE72005E10FA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = WidgetBundle; - productName = MalachiteWidgetBundle; - productReference = 78917F592B99AE72005E10FA /* WidgetBundle.appex */; - productType = "com.apple.product-type.app-extension"; - }; - 78A9DD602CD55551002C131D /* CaptureBundle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 78A9DD6C2CD55551002C131D /* Build configuration list for PBXNativeTarget "CaptureBundle" */; - buildPhases = ( - 78A9DD5D2CD55551002C131D /* Sources */, - 78A9DD5E2CD55551002C131D /* Frameworks */, - 78A9DD5F2CD55551002C131D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = CaptureBundle; - packageProductDependencies = ( - ); - productName = MalachiteCaptureBundle; - productReference = 78A9DD612CD55551002C131D /* CaptureBundle.appex */; - productType = "com.apple.product-type.extensionkit-extension"; - }; - 78B72BA62E33282E002E2D4E /* MalachiteWatch */ = { - isa = PBXNativeTarget; - buildConfigurationList = 78B72BB22E332830002E2D4E /* Build configuration list for PBXNativeTarget "MalachiteWatch" */; - buildPhases = ( - 78B72BA32E33282E002E2D4E /* Sources */, - 78B72BA42E33282E002E2D4E /* Frameworks */, - 78B72BA52E33282E002E2D4E /* Resources */, - 7837C11A2E34C47D009396B0 /* Embed Foundation Extensions */, - ); - buildRules = ( - ); - dependencies = ( - 7837C1022E34BD60009396B0 /* PBXTargetDependency */, - 7837C1192E34C47D009396B0 /* PBXTargetDependency */, - ); - name = MalachiteWatch; - packageProductDependencies = ( - ); - productName = "MalachiteWatch Watch App"; - productReference = 78B72BA72E33282E002E2D4E /* MalachiteWatch.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 785F08412B12D41100244EB4 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 2600; - LastUpgradeCheck = 1630; - TargetAttributes = { - 785F08482B12D41100244EB4 = { - CreatedOnToolsVersion = 15.1; - }; - 78917F582B99AE72005E10FA = { - CreatedOnToolsVersion = 15.3; - }; - 78A9DD602CD55551002C131D = { - CreatedOnToolsVersion = 16.2; - }; - 78B72BA62E33282E002E2D4E = { - CreatedOnToolsVersion = 26.0; - }; - }; - }; - buildConfigurationList = 785F08442B12D41100244EB4 /* Build configuration list for PBXProject "Malachite" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 785F08402B12D41100244EB4; - preferredProjectObjectVersion = 77; - productRefGroup = 785F084A2B12D41100244EB4 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 785F08482B12D41100244EB4 /* Malachite */, - 78B72BA62E33282E002E2D4E /* MalachiteWatch */, - 78917F582B99AE72005E10FA /* WidgetBundle */, - 7837C1042E34C45B009396B0 /* WidgetBundleWatch */, - 78A9DD602CD55551002C131D /* CaptureBundle */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 7837C10D2E34C45B009396B0 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7837C10E2E34C45B009396B0 /* Localizable.xcstrings in Resources */, - 78CD7DB72E359B87003814B5 /* Assets.xcassets in Resources */, - 7837C1102E34C45B009396B0 /* Codesigning.xcconfig in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 785F08472B12D41100244EB4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 782FC7762B2DAFAB007709C1 /* README.md in Resources */, - 7881A98C2C1F77AB00B1F83B /* Localizable.xcstrings in Resources */, - 78562BC32B450A7600920160 /* PRIVACY_POLICY.md in Resources */, - 78E142CB2DD4262F0016B3DB /* Codesigning.xcconfig in Resources */, - 78B72BE32E3461DA002E2D4E /* MalachiteIconMasked_Darwin25.png in Resources */, - 78B72BE42E3461DA002E2D4E /* MalachiteIconMasked_Darwin24.png in Resources */, - 78B72BD42E3452A2002E2D4E /* build.yml in Resources */, - 78B72BA22E332744002E2D4E /* AppIcon.icon in Resources */, - 785F08592B12D41300244EB4 /* LaunchScreen.storyboard in Resources */, - 78163D8E2CCB7BB300146126 /* CHANGELOG.md in Resources */, - 785F08562B12D41300244EB4 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78917F572B99AE72005E10FA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78CAC36A2CCD99B600A35AE8 /* Localizable.xcstrings in Resources */, - 78CD7DB62E359B87003814B5 /* Assets.xcassets in Resources */, - 78E142CA2DD4262F0016B3DB /* Codesigning.xcconfig in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78A9DD5F2CD55551002C131D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78E142CC2DD4262F0016B3DB /* Codesigning.xcconfig in Resources */, - 78D570842E3C7E530025B9B3 /* Assets.xcassets in Resources */, - 78A9DE752CD5AE5F002C131D /* Localizable.xcstrings in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78B72BA52E33282E002E2D4E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78789C742E3494D500862C3D /* Assets.xcassets in Resources */, - 7886C6C22E45E2E10095460C /* AppIcon.icon in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 78329B0F2C22699A006D326F /* Embed build information */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}", - ); - name = "Embed build information "; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "eval \"${GCC_PREPROCESSOR_DEFINITIONS}\"\n\nPATH=/opt/homebrew/bin:/usr/local/bin:$PATH\nPLISTBUDDY=\"/usr/libexec/PlistBuddy\"\nINFOPLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n\n# If there is no git repo to pull this info from, this is set to undefined later.\nCFBUILDHASH=$(git --git-dir=\"${PROJECT_DIR}/.git\" --work-tree=\"${PROJECT_DIR}\" rev-parse --short HEAD)\nCFBUILDDATE=$(date +'%Y-%m-%d-%H.%M.%S')\nif [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && -z $(system_profiler SPHardwareDataType | grep 'MacVM1,1') ]]; then\n # If the build is not INTERNAL, these are set to redacted later.\n CFBUILDUSER=$(whoami)\n CFBUILDHOST=$(hostname)\nelif [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && !(-z $(system_profiler SPHardwareDataType | grep 'MacVM1,1')) ]]; then\n # Xcode Cloud built this, but the hostname string is atrocious. Replace with these.\n CFBUILDUSER=\"evaluna (App Store Connect)\"\n CFBUILDHOST=\"Xcode Cloud\"\nfi\n\nif [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && -z $(gpg --list-secret-keys | grep 4A5B8C2A065654E24DE50FF03716ACDC524F1879) ]]; then\n echo \"Error: INTERNAL build type detected but Eva's GPG key was not found.\"\n echo \"Error: See 'Embed build information' in Build Phases for a solution\"\n # Be aware that I use the INTERNAL target to gate off any unstable/unready features. By removing this line, \n # you are acknowledging that you will not receive support for any issues encountered with Malachite until you \n # are running DEBUG or RELEASE. If you just want a DEBUG build, change the Build Configuration to Debug in \n # Product > Scheme > Edit Scheme...\n if [[ -z $(system_profiler SPHardwareDataType | grep 'MacVM1,1') ]]; then\n exit\n else\n # This line of code should keep internal TestFlight builds as INTERNAL, and external ones as DEBUG\n if [[ $(git --git-dir=\"${PROJECT_DIR}/.git\" rev-parse --abbrev-ref HEAD) != *\"dev\"* ]]; then\n CFBUILDTYPE=\"DEBUG\"\n fi\n fi\nfi\n\n\n\n# Print all of the variables in the build log. Useful for debugging\necho \"\"\necho \"CFBUILDHASH = ${CFBUILDHASH}\"\necho \"CFBUILDDATE = ${CFBUILDDATE}\"\necho \"CFBUILDTYPE = ${CFBUILDTYPE}\"\nif [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" ]]; then\n echo \"CFBUILDUSER = ${CFBUILDUSER}\"\n echo \"CFBUILDHOST = ${CFBUILDHOST}\"\nfi\necho \"INFOPLIST = ${INFOPLIST}\"\n\n# Make sure the key is present in the plist before trying to set it.\nTMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildHash' \"${INFOPLIST}\" 2>/dev/null)\nif [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildHash string\" \"${INFOPLIST}\"; fi\nTMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildDate' \"${INFOPLIST}\" 2>/dev/null)\nif [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildDate string\" \"${INFOPLIST}\"; fi\nTMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildType' \"${INFOPLIST}\" 2>/dev/null)\nif [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildType string\" \"${INFOPLIST}\"; fi\nTMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildUser' \"${INFOPLIST}\" 2>/dev/null)\nif [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildUser string\" \"${INFOPLIST}\"; fi\nTMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildHost' \"${INFOPLIST}\" 2>/dev/null)\nif [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildHost string\" \"${INFOPLIST}\"; fi\n\n# Fallback values, either to fill in the gaps or prevent app crashes\nif [ -z \"$CFBUILDHASH\" ]; then CFBUILDHASH=undefined; fi\nif [ -z \"$CFBUILDTYPE\" ]; then CFBUILDTYPE=debug; fi\nif [ -z \"$CFBUILDUSER\" ]; then CFBUILDUSER=redacted; fi\nif [ -z \"$CFBUILDHOST\" ]; then CFBUILDHOST=redacted; fi\n\n# Set the values in the Info.plist\n$PLISTBUDDY -c \"Set :CFBuildHash $CFBUILDHASH\" \"${INFOPLIST}\"\n$PLISTBUDDY -c \"Set :CFBuildDate $CFBUILDDATE\" \"${INFOPLIST}\"\n$PLISTBUDDY -c \"Set :CFBuildType $CFBUILDTYPE\" \"${INFOPLIST}\"\n$PLISTBUDDY -c \"Set :CFBuildUser $CFBUILDUSER\" \"${INFOPLIST}\"\n$PLISTBUDDY -c \"Set :CFBuildHost $CFBUILDHOST\" \"${INFOPLIST}\"\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 7837C1052E34C45B009396B0 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7837C1062E34C45B009396B0 /* MalachiteIntentUtils.swift in Sources */, - 7837C1072E34C45B009396B0 /* ControlCenterWidget.swift in Sources */, - 7837C1082E34C45B009396B0 /* LockScreenWidget.swift in Sources */, - 7837C1092E34C45B009396B0 /* MalachiteWidgetBundle.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 785F08452B12D41100244EB4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78E67EE22B27E07300812A17 /* MalachiteUtils.swift in Sources */, - 785F08512B12D41100244EB4 /* MalachiteView.swift in Sources */, - 78C80BF62CDABE7800F6E8A7 /* MalachitePreferencesUtils.swift in Sources */, - 78E67EE02B27E07300812A17 /* MalachiteHapticUtils.swift in Sources */, - 788E74B32B13D8200024B586 /* MalachiteSettingsView.swift in Sources */, - 7806973A2B27F66E00B4942B /* MalachiteTooltipUtils.swift in Sources */, - 7859DE1A2B8308C000D1A998 /* MalachiteAboutView.swift in Sources */, - 7860DC002B897A3E00450BF8 /* MalachiteGameUtils.swift in Sources */, - 7897ECA62BA2B87100662EA0 /* MalachiteSettingsDetailView.swift in Sources */, - 785F084D2B12D41100244EB4 /* AppDelegate.swift in Sources */, - 788848B52CC74B4200B02E37 /* MalachiteCompatibilityView.swift in Sources */, - 78A9DE2F2CD57F51002C131D /* MalachiteIntentUtils.swift in Sources */, - 78C7EF9D2D1ECBD1001E332B /* MalachitePreferences.swift in Sources */, - 787B1CAA2B8B095E000AFECC /* Malachite.docc in Sources */, - 78E67EE12B27E07300812A17 /* MalachiteViewUtils.swift in Sources */, - 78E5EFC02B15748400F15250 /* MalachitePhotoPreview.swift in Sources */, - 785F084F2B12D41100244EB4 /* SceneDelegate.swift in Sources */, - 78E67EE52B27E0A200812A17 /* MalachiteFunctionUtils.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78917F552B99AE72005E10FA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78D5707D2E3C7CC60025B9B3 /* MalachitePreferencesUtils.swift in Sources */, - 78D5707E2E3C7CC60025B9B3 /* MalachitePreferences.swift in Sources */, - 78D570762E3C7C5E0025B9B3 /* MalachiteView.swift in Sources */, - 78E8265B2E349FEC00A8DB50 /* MalachiteIntentUtils.swift in Sources */, - 780067E12CBDCEA0004BB595 /* ControlCenterWidget.swift in Sources */, - 78D570782E3C7CB80025B9B3 /* MalachiteTooltipUtils.swift in Sources */, - 78D570792E3C7CB80025B9B3 /* MalachiteViewUtils.swift in Sources */, - 78D5707A2E3C7CB80025B9B3 /* MalachiteHapticUtils.swift in Sources */, - 78D5707B2E3C7CB80025B9B3 /* MalachiteFunctionUtils.swift in Sources */, - 78D570802E3C7D050025B9B3 /* MalachiteSettingsView.swift in Sources */, - 78D570812E3C7D050025B9B3 /* MalachiteCompatibilityView.swift in Sources */, - 78D5707C2E3C7CB80025B9B3 /* MalachiteGameUtils.swift in Sources */, - 78917F632B99AE72005E10FA /* LockScreenWidget.swift in Sources */, - 78D570822E3C7D0B0025B9B3 /* MalachiteSettingsDetailView.swift in Sources */, - 78D570832E3C7D0B0025B9B3 /* MalachiteAboutView.swift in Sources */, - 78D570772E3C7CB20025B9B3 /* MalachiteUtils.swift in Sources */, - 78D5707F2E3C7CF50025B9B3 /* MalachitePhotoPreview.swift in Sources */, - 78917F612B99AE72005E10FA /* MalachiteWidgetBundle.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78A9DD5D2CD55551002C131D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78A9DE2D2CD57F51002C131D /* MalachiteIntentUtils.swift in Sources */, - 78A9DD762CD55558002C131D /* MalachiteCaptureBundle.swift in Sources */, - 78A9DD7C2CD5562B002C131D /* MalachiteView.swift in Sources */, - 78A9DD7D2CD55631002C131D /* MalachiteAboutView.swift in Sources */, - 78A9DD7E2CD55631002C131D /* MalachiteUtils.swift in Sources */, - 78A9DD7F2CD55631002C131D /* MalachiteSettingsView.swift in Sources */, - 78C80BF52CDABE7800F6E8A7 /* MalachitePreferencesUtils.swift in Sources */, - 78A9DD812CD55631002C131D /* MalachiteFunctionUtils.swift in Sources */, - 78A9DD822CD55631002C131D /* MalachitePhotoPreview.swift in Sources */, - 78A9DD832CD55631002C131D /* MalachiteCompatibilityView.swift in Sources */, - 78A9DD842CD55631002C131D /* MalachiteViewUtils.swift in Sources */, - 78A9DD852CD55631002C131D /* MalachiteSettingsDetailView.swift in Sources */, - 78A9DD862CD55631002C131D /* MalachiteTooltipUtils.swift in Sources */, - 78A9DD872CD55631002C131D /* MalachiteGameUtils.swift in Sources */, - 78C7EF9E2D1ECBD1001E332B /* MalachitePreferences.swift in Sources */, - 78A9DD882CD55631002C131D /* MalachiteHapticUtils.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78B72BA32E33282E002E2D4E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78B72BDA2E345301002E2D4E /* ContentView.swift in Sources */, - 78B72BDB2E345301002E2D4E /* MalachiteWatchApp.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 7837C1022E34BD60009396B0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 78917F582B99AE72005E10FA /* WidgetBundle */; - targetProxy = 7837C1012E34BD60009396B0 /* PBXContainerItemProxy */; - }; - 7837C1192E34C47D009396B0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 7837C1042E34C45B009396B0 /* WidgetBundleWatch */; - targetProxy = 7837C1182E34C47D009396B0 /* PBXContainerItemProxy */; - }; - 78917F682B99AE73005E10FA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 78917F582B99AE72005E10FA /* WidgetBundle */; - targetProxy = 78917F672B99AE73005E10FA /* PBXContainerItemProxy */; - }; - 78A9DD692CD55551002C131D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 78A9DD602CD55551002C131D /* CaptureBundle */; - targetProxy = 78A9DD682CD55551002C131D /* PBXContainerItemProxy */; - }; - 78B72BB02E332830002E2D4E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 78B72BA62E33282E002E2D4E /* MalachiteWatch */; - targetProxy = 78B72BAF2E332830002E2D4E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 785F08572B12D41300244EB4 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 785F08582B12D41300244EB4 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 7837C1122E34C45B009396B0 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_ENHANCED_SECURITY = NO; - ENABLE_POINTER_AUTHENTICATION = NO; - EXCLUDED_ARCHS = arm64e; - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/WidgetBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteWidgetBundle; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = "-DAPP_EXTENSION"; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WatchRemote.WidgetBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "watchos watchsimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Debug; - }; - 7837C1132E34C45B009396B0 /* Internal */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_ENHANCED_SECURITY = NO; - ENABLE_POINTER_AUTHENTICATION = NO; - EXCLUDED_ARCHS = arm64e; - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/WidgetBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteWidgetBundle; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = "-DAPP_EXTENSION"; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WatchRemote.WidgetBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "watchos watchsimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Internal; - }; - 7837C1142E34C45B009396B0 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_ENHANCED_SECURITY = NO; - ENABLE_POINTER_AUTHENTICATION = NO; - EXCLUDED_ARCHS = arm64e; - GCC_PREPROCESSOR_DEFINITIONS = ""; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/WidgetBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteWidgetBundle; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = "-DAPP_EXTENSION"; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WatchRemote.WidgetBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "watchos watchsimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Release; - }; - 785F085B2B12D41300244EB4 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 95J8WZ4TN8; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 785F085C2B12D41300244EB4 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = 95J8WZ4TN8; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_EMIT_LOC_STRINGS = YES; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 785F085E2B12D41300244EB4 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "asb_approved thatsinceguy"; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; - CODE_SIGN_ENTITLEMENTS = Malachite/Malachite.entitlements; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREPROCESSOR_DEFINITIONS = ( - "CFBUILDTYPE=DEBUG", - "$(inherited)", - ); - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Malachite; - INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography"; - INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "Malachite uses the rear camera system on iPhone to present your magnified view."; - INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Malachite uses write access to your library in order to save photos."; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = ""; - INFOPLIST_KEY_UIRequiresFullScreen = NO; - INFOPLIST_KEY_UIStatusBarHidden = YES; - INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_OBJC_INTERFACE_HEADER_NAME = "Malachite-Swift.h"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 785F085F2B12D41300244EB4 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "asb_approved thatsinceguy"; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; - CODE_SIGN_ENTITLEMENTS = Malachite/Malachite.entitlements; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREPROCESSOR_DEFINITIONS = "CFBUILDTYPE=RELEASE"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Malachite; - INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography"; - INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "Malachite uses the rear camera system on iPhone to present your magnified view."; - INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Malachite uses write access to your library in order to save photos."; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = ""; - INFOPLIST_KEY_UIRequiresFullScreen = NO; - INFOPLIST_KEY_UIStatusBarHidden = YES; - INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = MAIN_APP; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_OBJC_INTERFACE_HEADER_NAME = "Malachite-Swift.h"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; - 78917F6B2B99AE73005E10FA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/WidgetBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteWidgetBundle; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WidgetBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 26.0; - }; - name = Debug; - }; - 78917F6C2B99AE73005E10FA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - GCC_PREPROCESSOR_DEFINITIONS = ""; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/WidgetBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteWidgetBundle; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WidgetBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 26.0; - }; - name = Release; - }; - 78A9DD6D2CD55551002C131D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/CaptureBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteCaptureBundle; - INFOPLIST_KEY_NSCameraUsageDescription = "Malachite uses the rear camera system on iPhone to present your magnified view."; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Malachite uses write access to your library in order to save photos."; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.CaptureBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - 78A9DD6E2CD55551002C131D /* Internal */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/CaptureBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteCaptureBundle; - INFOPLIST_KEY_NSCameraUsageDescription = "Malachite uses the rear camera system on iPhone to present your magnified view."; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Malachite uses write access to your library in order to save photos."; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.CaptureBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Internal; - }; - 78A9DD6F2CD55551002C131D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/CaptureBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteCaptureBundle; - INFOPLIST_KEY_NSCameraUsageDescription = "Malachite uses the rear camera system on iPhone to present your magnified view."; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Malachite uses write access to your library in order to save photos."; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.CaptureBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - 78B72BB32E332830002E2D4E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_ENHANCED_SECURITY = NO; - ENABLE_POINTER_AUTHENTICATION = NO; - ENABLE_PREVIEWS = YES; - EXCLUDED_ARCHS = arm64e; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MalachiteWatch/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Malachite Remote"; - INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - INFOPLIST_KEY_WKCompanionAppBundleIdentifier = dev.crystll1ne.Malachite; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WatchRemote"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 8.0; - }; - name = Debug; - }; - 78B72BB42E332830002E2D4E /* Internal */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_ENHANCED_SECURITY = NO; - ENABLE_POINTER_AUTHENTICATION = NO; - ENABLE_PREVIEWS = YES; - EXCLUDED_ARCHS = arm64e; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MalachiteWatch/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Malachite Remote"; - INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - INFOPLIST_KEY_WKCompanionAppBundleIdentifier = dev.crystll1ne.Malachite; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WatchRemote"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 8.0; - }; - name = Internal; - }; - 78B72BB52E332830002E2D4E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_ENHANCED_SECURITY = NO; - ENABLE_POINTER_AUTHENTICATION = NO; - ENABLE_PREVIEWS = YES; - EXCLUDED_ARCHS = arm64e; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MalachiteWatch/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Malachite Remote"; - INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - INFOPLIST_KEY_WKCompanionAppBundleIdentifier = dev.crystll1ne.Malachite; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WatchRemote"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = MAIN_APP; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 8.0; - }; - name = Release; - }; - 78F9DA642CBCDDB900A99638 /* Internal */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 95J8WZ4TN8; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_COMPILATION_MODE = singlefile; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Internal; - }; - 78F9DA652CBCDDB900A99638 /* Internal */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "asb_approved thatsinceguy"; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; - CODE_SIGN_ENTITLEMENTS = Malachite/Malachite.entitlements; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREPROCESSOR_DEFINITIONS = ( - "CFBUILDTYPE=INTERNAL", - "$(inherited)", - ); - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Malachite; - INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography"; - INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "Malachite uses the rear camera system on iPhone to present your magnified view."; - INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Malachite uses write access to your library in order to save photos."; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = ""; - INFOPLIST_KEY_UIRequiresFullScreen = NO; - INFOPLIST_KEY_UIStatusBarHidden = YES; - INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_OBJC_INTERFACE_HEADER_NAME = "Malachite-Swift.h"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Internal; - }; - 78F9DA662CBCDDB900A99638 /* Internal */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; - ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; - ENABLE_C_BOUNDS_SAFETY = YES; - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Malachite/WidgetBundle/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MalachiteWidgetBundle; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0.0; - OTHER_SWIFT_FLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).Malachite.WidgetBundle"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 26.0; - }; - name = Internal; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 7837C1112E34C45B009396B0 /* Build configuration list for PBXNativeTarget "WidgetBundleWatch" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7837C1122E34C45B009396B0 /* Debug */, - 7837C1132E34C45B009396B0 /* Internal */, - 7837C1142E34C45B009396B0 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Internal; - }; - 785F08442B12D41100244EB4 /* Build configuration list for PBXProject "Malachite" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 785F085B2B12D41300244EB4 /* Debug */, - 78F9DA642CBCDDB900A99638 /* Internal */, - 785F085C2B12D41300244EB4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Internal; - }; - 785F085D2B12D41300244EB4 /* Build configuration list for PBXNativeTarget "Malachite" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 785F085E2B12D41300244EB4 /* Debug */, - 78F9DA652CBCDDB900A99638 /* Internal */, - 785F085F2B12D41300244EB4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Internal; - }; - 78917F6D2B99AE73005E10FA /* Build configuration list for PBXNativeTarget "WidgetBundle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 78917F6B2B99AE73005E10FA /* Debug */, - 78F9DA662CBCDDB900A99638 /* Internal */, - 78917F6C2B99AE73005E10FA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Internal; - }; - 78A9DD6C2CD55551002C131D /* Build configuration list for PBXNativeTarget "CaptureBundle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 78A9DD6D2CD55551002C131D /* Debug */, - 78A9DD6E2CD55551002C131D /* Internal */, - 78A9DD6F2CD55551002C131D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Internal; - }; - 78B72BB22E332830002E2D4E /* Build configuration list for PBXNativeTarget "MalachiteWatch" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 78B72BB32E332830002E2D4E /* Debug */, - 78B72BB42E332830002E2D4E /* Internal */, - 78B72BB52E332830002E2D4E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Internal; - }; -/* End XCConfigurationList section */ - }; - rootObject = 785F08412B12D41100244EB4 /* Project object */; -} diff --git a/Malachite/Base.lproj/LaunchScreen.storyboard b/Malachite/Base.lproj/LaunchScreen.storyboard deleted file mode 100755 index 865e932..0000000 --- a/Malachite/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Malachite/Malachite.docc/Malachite.md b/Malachite/Malachite.docc/Malachite.md deleted file mode 100755 index 754b17a..0000000 --- a/Malachite/Malachite.docc/Malachite.md +++ /dev/null @@ -1,9 +0,0 @@ -# ``Malachite`` - -Malachite is an app designed to bring more control to users for macro photography, while offering playful elements to enjoy. - -## Overview - -Welcome to the documentation browser for Malachite! While this application is not designed to by used as a library inside of other projects, making its code easy to reference is paramount for code readibility, learning how to create your own Swift applications, or contributing your own fixes and features to Malachite itself. - - diff --git a/Malachite/SceneDelegate.swift b/Malachite/SceneDelegate.swift deleted file mode 100755 index c391cd8..0000000 --- a/Malachite/SceneDelegate.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// SceneDelegate.swift -// Malachite -// -// Created by Eva Isabella Luna on 11/25/23. -// - -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - window?.rootViewController = MalachiteView() - window?.makeKeyAndVisible() - } -} diff --git a/Malachite/Views/MalachiteAboutView.swift b/Malachite/Views/MalachiteAboutView.swift deleted file mode 100755 index dfd64e8..0000000 --- a/Malachite/Views/MalachiteAboutView.swift +++ /dev/null @@ -1,263 +0,0 @@ -// -// MalachiteAboutView.swift -// Malachite -// -// Created by Eva Isabella Luna on 2/18/24. -// - -import SwiftUI -import GameKit - -private struct AppIcon { - let id = UUID() - let name: String - let description: String - let image: String - let symbol: Bool - let icon: String? - let achievement: String? -} - -struct MalachiteAboutView: View { - /// A State variable used for determining whether or not this view is being presented as a modal. - var dismissAction: (() -> Void) - /// A State variable used for determining whether or not to enable Game Center integration. - @State private var gamekitSwitch = false - /// A State variable used for determining whether or not to uncap the exposure slider. - @State private var exposureUnlimiterSwitch = false - - /// A variable used to determine the currently available app icons. - private let appIcons = [ - AppIcon(name: "crystall1nedev", description: "about.credits.crystall1nedev", image: "crystall1nedev", symbol: false, icon: nil, achievement: "icon.default"), - AppIcon(name: "ThatStella7922", description: "about.credits.thatstella7922", image: "thatstella7922", symbol: false, icon: "thatsniceguy", achievement: "icon.wifey"), - AppIcon(name: "ASentientBot", description: "about.credits.asentientbot", image: "asentientbot", symbol: false, icon: "asb_approved", achievement: "icon.marimo"), - AppIcon(name: "The Sanctuary Discord", description: "about.credits.discord", image: "", symbol: true, icon: nil, achievement: nil), - AppIcon(name: "Apple", description: "about.credits.apple", image: "applelogo", symbol: true, icon: nil, achievement: nil) - ] - - /// A variable to hold the existing instance of ``MalachiteClassesObject``. - var utilities = MalachiteClassesObject() - /// A variable used to hold the function for dismissing with the toolbar item. - ///var dismissAction: (() -> Void) - - /** - A variable used to hold the entire view. - - SwiftUI is weird... - Currently holds: - - Other variables to avoid type counting time issues. - - Handles initialization of variables required to show current settings. - - Navigation title of "About Malachite" - - Toolbar item for dismissing the view - */ - var body: some View { - MalachiteNagivationViewUtils() { guts } - } - - var guts: some View { - Form { - aboutSection - storySection - creditsSection - funniesSection - - } - .onAppear() { - gamekitSwitch = utilities.preferences.general.gamekit.enabled - exposureUnlimiterSwitch = utilities.preferences.capture.unlimitedISO - } - .onDisappear() { - utilities.preferences.general.gamekit.enabled = gamekitSwitch - utilities.preferences.capture.unlimitedISO = exposureUnlimiterSwitch - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.exposureLimitNotification.name, object: nil) - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.gameCenterEnabledNotification.name, object: nil) - } - .navigationTitle("view.title.about") - .toolbar(content: { - ToolbarItemGroup(placement: .topBarTrailing) { - if #available(iOS 26.0, *) { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark") - .tint(.primary) - } - .buttonStyle(.glassProminent) - } else { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark.circle") - } - } - } - }) - } - - /// A variable for the about section. - var aboutSection: some View { - Section(footer: aboutSectionFooter){ - HStack { - VStack { - HStack { - Text("appname") - .font(.largeTitle) - .bold() - Spacer() - } - HStack { - Text("\(utilities.versionMajor).\(utilities.versionMinor).\(utilities.versionMinor)") - .font(.footnote) - .frame(alignment: .leading) - Spacer() - } - } - Spacer() - Button { - if utilities.versionType == "INTERNAL" { - utilities.preferences.ext.showGameKitOptionInAbout(in: &utilities.preferences) - } - } label: { - if #available(iOS 26.0, *) { - Image("icon26") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 80, alignment: .trailing) - .clipShape(RoundedRectangle(cornerRadius: 17)) - } else { - Image("icon") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 80, alignment: .trailing) - .clipShape(RoundedRectangle(cornerRadius: 17)) - } - } - } - Text("about.description") - Text("about.author_note") - .bold() - } - } - - var aboutSectionFooter: some View { - VStack { - if utilities.versionType == "DEBUG" || utilities.versionType == "INTERNAL" { - HStack { - Text("\(utilities.versionType) - \(utilities.versionHash) - \(utilities.versionDate)") - .font(.footnote) - .frame(alignment: .leading) - Spacer() - } - } - if utilities.versionType == "INTERNAL" { - HStack { - Text("Built by \(utilities.versionUser) on \(utilities.versionHost)") - .font(.footnote) - .frame(alignment: .leading) - Spacer() - } - } - } - } - - /// A variable for the story section. - var storySection: some View { - Section(header: Text("about.header.story")) { - Text("about.story") - } - } - - /// A variable for the credits section. - var creditsSection : some View { - Section(header: Text("about.header.credits")) { - ForEach(appIcons, id: \.id) {appIcon in - HStack { - VStack { - HStack { - Text(appIcon.name) - .font(.title2) - .bold() - Spacer() - } - HStack { - Text(LocalizedStringKey(appIcon.description)) - Spacer() - } - } - Spacer() - Button { - utilities.debugNSLog("[App Icon] Changing to \(appIcon.icon ?? "default")") - #if MAIN_APP - UIApplication.shared.setAlternateIconName(appIcon.icon) { (error) in - if let error = error { - print("Failed request to update the app’s icon: \(error)") - } - } - #endif - if utilities.games.gameCenterEnabled && appIcon.achievement != nil { - DispatchQueue.global(qos: .background).async { [self] in - let iconAchievement = utilities.games.achievements.pullAchievement(achievementName: appIcon.achievement!) - iconAchievement.percentComplete = 100 - utilities.games.achievements.pushAchievement(achievementBody: iconAchievement) - } - } - } label: { - Text("") - } - if !appIcon.symbol { - Image(appIcon.image) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 40, alignment: .trailing) - .clipShape(Circle()) - } else { - if #available(iOS 16.0, *) { - Image(systemName: appIcon.image) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 30, alignment: .trailing) - .padding(.trailing, 5) - } else { - Image(systemName: appIcon.image) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 30, alignment: .trailing) - .padding(.trailing, 5) - } - } - } - } - } - } - - /// A variable for the funnies section. - var funniesSection : some View { - Section(header: Text("about.header.special")) { - MalachiteCellViewUtils( - icon: "sun.max", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.photo.max_exposure", isOn: $exposureUnlimiterSwitch) - } - if utilities.preferences.general.gamekit.found { - MalachiteCellViewUtils( - icon: "gamecontroller", - disabled: nil, - dangerous: true) - { - Toggle("", isOn: $gamekitSwitch) - } - } - } - .onChange(of: gamekitSwitch) {_ in - utilities.preferences.general.gamekit.enabled = gamekitSwitch - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.gameCenterEnabledNotification.name, object: nil) - } - .onChange(of: exposureUnlimiterSwitch) { _ in - utilities.debugNSLog("[Settings View] Lol") - utilities.preferences.capture.unlimitedISO = exposureUnlimiterSwitch - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.exposureLimitNotification.name, object: nil) - } - } -} diff --git a/Malachite/Views/MalachiteCompatibilityView.swift b/Malachite/Views/MalachiteCompatibilityView.swift deleted file mode 100755 index c3db225..0000000 --- a/Malachite/Views/MalachiteCompatibilityView.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// MalachiteCompatibilityView.swift -// Malachite -// -// Created by Eva Isabella Luna on 10/21/24. -// - -import SwiftUI - -public struct MalachiteCompatibilityView: View { - /// A State variable used for determining whether or not this view is being presented as a modal. - var dismissAction: (() -> Void) - /// A variable to hold the existing instance of ``MalachiteClassesObject``. - var utilities = MalachiteClassesObject() - - public var body: some View { - MalachiteNagivationViewUtils() { - Form { - Section { - Text("compatibility.note") - } - Section { - if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.ultrawide) { - // Ultra wide megapixel capabilities - MalachiteCompatibilityViewUtils(title: "compatibility.title.12mp.ultrawide", available: utilities.preferences.compatibility.ultrawide["12"] ?? false) - MalachiteCompatibilityViewUtils(title: "compatibility.title.48mp.ultrawide", available: utilities.preferences.compatibility.ultrawide["48"] ?? false) - } else { - MalachiteCompatibilityViewUtils(title: "compatibility.title.no.ultrawide", available: false) - } - if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.wideangle) { - // Wide angle megapixel capabilities - MalachiteCompatibilityViewUtils(title: "compatibility.title.8mp.wide", available: utilities.preferences.compatibility.wideangle["8"] ?? false) - MalachiteCompatibilityViewUtils(title: "compatibility.title.12mp.wide", available: utilities.preferences.compatibility.wideangle["12"] ?? false) - MalachiteCompatibilityViewUtils(title: "compatibility.title.48mp.wide", available: utilities.preferences.compatibility.wideangle["48"] ?? false) - } else { - MalachiteCompatibilityViewUtils(title: "compatibility.title.no.wide", available: false) - } - if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.telephoto) { - // Telephoto megapixel capabilities - MalachiteCompatibilityViewUtils(title: "compatibility.title.12mp.telephoto", available: utilities.preferences.compatibility.telephoto["12"] ?? false) - } else { - MalachiteCompatibilityViewUtils(title: "compatibility.title.no.telephoto", available: false) - } - - // JPEG, HEIF - MalachiteCompatibilityViewUtils(title: "compatibility.title.jpeg", available: utilities.preferences.compatibility.jpeg) - MalachiteCompatibilityViewUtils(title: "compatibility.title.heif", available: utilities.preferences.compatibility.heic) - - // HDR - MalachiteCompatibilityViewUtils(title: "compatibility.title.hdr", available: utilities.preferences.compatibility.hdr) - } - } - .navigationTitle("view.title.compatibility") - .toolbar(content: { - ToolbarItemGroup(placement: .topBarTrailing) { - if #available(iOS 26.0, *) { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark") - .tint(.primary) - } - .buttonStyle(.glassProminent) - } else { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark.circle") - } - } - } - }) - } - } -} diff --git a/Malachite/Views/MalachiteSettingsDetailView.swift b/Malachite/Views/MalachiteSettingsDetailView.swift deleted file mode 100755 index 9a70da2..0000000 --- a/Malachite/Views/MalachiteSettingsDetailView.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// MalachiteSettingsDetailView.swift -// Malachite -// -// Created by Eva Isabella Luna on 3/14/24. -// - -import SwiftUI - -struct MalachiteSettingsDetailView: View { - - /// A variable used to hold the function for dismissing with the toolbar item. - var dismissAction: (() -> Void) - - /// A variable used to hold the entire view. - var body: some View { - - Form { - aboutSection - previewSettingsSection - resolutionSettingsSection - photoSettingsSection - watermarkSettingsSection - uiSettingsSection - debugSettingsSection - } - .navigationTitle("view.title.help") - .toolbar(content: { - ToolbarItemGroup(placement: .topBarTrailing) { - if #available(iOS 26.0, *) { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark") - .tint(.primary) - } - .buttonStyle(.glassProminent) - } else { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark.circle") - } - } - } - }) - } - - /// A variable to hold the about section. - var aboutSection: some View { - Section { - MalachiteSettingsDetailViewUtils(title: Text("view.title.about"), subtitle: Text("view.detail.about")) {} - } - } - - /// A variable to hold the preview settings section. - var previewSettingsSection: some View { - Section(header: Text("settings.header.preview"), footer: Text("settings.footer.preview")) { - MalachiteSettingsDetailViewUtils(title: Text("settings.option.preview.aspect_ratio"), subtitle: Text("settings.detail.preview.aspect_ratio")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.preview.sbtlz"), subtitle: Text("settings.detail.preview.sbtlz")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.preview.zoom_maximum"), subtitle: Text("settings.detail.preview.zoom_maximum")) {} - } - } - - /// A variable to hold the image resolution section. - var resolutionSettingsSection: some View { - Section(header: Text("settings.header.resolution"), footer: Text("settings.footer.resolution")) { - MalachiteSettingsDetailViewUtils(title: Text("settings.option.resolution.ultrawide"), subtitle: Text("settings.detail.resolution.ultrawide")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.resolution.wide"), subtitle: Text("settings.detail.resolution.wide")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.resolution.telephoto"), subtitle: Text("settings.detail.resolution.telephoto")) {} - } - } - - /// A variable to hold the photo settings section. - var photoSettingsSection: some View { - Section(header: Text("settings.header.photo"), footer: Text("settings.footer.photo")) { - MalachiteSettingsDetailViewUtils(title: Text("settings.option.photo.fileformat"), subtitle: Text("settings.detail.photo.fileformat")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.photo.hdr"), subtitle: Text("settings.detail.photo.hdr")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.photo.continuous"), subtitle: Text("settings.detail.photo.continuous")) {} - } - } - - var uiSettingsSection: some View { - Section(header: Text("settings.header.ui"), footer: Text("settings.footer.ui")) { - MalachiteSettingsDetailViewUtils(title: Text("settings.option.ui.tapgesture"), subtitle: Text("settings.detail.ui.tapgesture")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.ui.hiddengestures"), subtitle: Text("settings.detail.ui.hiddengestures")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.ui.idletimer"), subtitle: Text("settings.detail.ui.idletimer")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.ui.haptics"), subtitle: Text("settings.detail.ui.haptics")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.ui.hiddenonlaunch"), subtitle: Text("settings.detail.ui.hiddenonlaunch")) {} - } - } - - /// A variable to hold the watermark settings section. - var watermarkSettingsSection: some View { - Section(header: Text("settings.header.watermark"), footer: Text("settings.footer.watermark")) { - MalachiteSettingsDetailViewUtils(title: Text("settings.option.watermark.enable"), subtitle: Text("settings.detail.watermark.enable")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.watermark.text"), subtitle: Text("settings.detail.watermark.text")) {} - } - } - - /// A variable to hold the debug settings section. Only available with debug builds. - var debugSettingsSection: some View { - Section(header: Text("settings.header.debug"), footer: Text("settings.footer.debug")) { - MalachiteSettingsDetailViewUtils(title: Text("settings.option.debug.logging.userdefaults"), subtitle: Text("settings.detail.debug.logging.userdefaults")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.debug.erase.userdefaults"), subtitle: Text("settings.detail.debug.erase.userdefaults")) {} - MalachiteSettingsDetailViewUtils(title: Text("settings.option.debug.erase.gamekit"), subtitle: Text("settings.detail.debug.erase.gamekit")) {} - } - } -} - -struct MalachiteSettingsDetailUtils: View { - var icon: String - var title: String - var subtitle: String - var dangerous: Bool - - init( - icon: String, - title: String, - subtitle: String?, - dangerous: Bool - ) { - self.icon = icon - self.title = title - self.subtitle = subtitle ?? "" - self.dangerous = dangerous - } - - var body: some View { - HStack() { - Image(systemName: icon) - .frame(maxWidth: 30) - .foregroundStyle(dangerous ? .red : Color.accentColor) - .symbolRenderingMode(.hierarchical) - VStack { - HStack { - if dangerous { - if #available(iOS 17.0, *) { - Text(LocalizedStringKey(title)) - .foregroundStyle(.red) - } else { - Text(LocalizedStringKey(title)) - .foregroundColor(.red) - } - } else { - Text(LocalizedStringKey(title)) - } - Spacer() - } - if subtitle != "" { - HStack { - if dangerous { - if #available(iOS 17.0, *) { - Text(LocalizedStringKey(subtitle)) - .foregroundStyle(.red) - .font(.footnote) - } else { - Text(LocalizedStringKey(subtitle)) - .foregroundColor(.red) - .font(.footnote) - } - } else { - Text(LocalizedStringKey(subtitle)) - .font(.footnote) - } - Spacer() - } - } - } - } - } -} diff --git a/Malachite/Views/MalachiteSettingsView.swift b/Malachite/Views/MalachiteSettingsView.swift deleted file mode 100755 index b2d2095..0000000 --- a/Malachite/Views/MalachiteSettingsView.swift +++ /dev/null @@ -1,759 +0,0 @@ -// -// MalachiteSettingsView.swift -// Malachite -// -// Created by Eva Isabella Luna on 11/26/23. -// - -import SwiftUI - -struct MalachiteSettingsView: View { - /// A State variable used for determining whether or not watermarking is enabled. - @State private var watermarkSwitch = false - /// A State variable used for determining the current watermark string. - @State private var watermarkText = String() - /// A State variable used for determining the active photo format. - @State private var photoFormat = Int() - /// A State variable used for determining the current aspect ratio for the ``cameraPreview``. - @State private var previewAspect = Int() - /// A State variable used for determining whether or not to stabilize the ``cameraPreview``. - @State private var shouldStabilize = Bool() - /// A State variable used for determining whether or not to skip opening ``MalachitePhotoPreview`` and save the photo. - @State private var shouldUseFastPath = Bool() - /// A State variable used for determining what the maximum zoom level for each camera should be. - @State private var zoomMaximum = Int() - /// A State variable used for determining whether or not to capture in HDR. - @State private var hdrSwitch = false - /// A State variable used for determining whether or not to enable continuous auto exposure. - @State private var continuousAEAF = Int() - /// A State variable used for determining how many fingers are used for the settings gesture. - @State private var settingsGestureFingers = Int() - /// A State variable used for determining whether or not to enable exposure and focus POI on tap and hold. - @State private var poiTapAndHold = Int() - /// A State variable used for determining whether or not to enable the system's auto locking APIs. - @State private var idleTimerDisabled = Bool() - /// A State variable used for determining whether or not to enabel haptics. - @State private var hapticsDisabled = Bool() - /// A State variable used for determining whether or not to start the app with the UI hidden. - @State private var appStartsUIHidden = Bool() - /// A State vairable used for determining whether or not to enable pinch to zoom and the tap gesture while the UI is hidden. - @State private var uiHiderGestures = Int() - /// A State variable used for determining whether or not the device supports HDR capture in its current mode. - @State private var supportsHDR = Bool() - /// A State variable used for determining whether or not the device supports HEIC capture. - @State private var supportsHEIC = Bool() - /// A State variable used for presenting the user with a footer based on capabilities. - @State private var formatFooterText: String? - /// A State variable used for determining whether or not debug logging UserDefaults is enabled. - @State private var debugLoggingUserDefaults = false - /// A State variable used for determining whether or not to literally break the app. - @State private var breakApp = false - /// A State variable used for determining what megapixel count the ultrawide camera should shoot in. - @State private var ultrawideMegapixelCount = Int() - /// A State variable used for determining what megapixel count the wide angle camera should shoot in. - @State private var wideMegapixelCount = Int() - /// A State variable used for determining what megapixel count the telephoto camera should shoot in. - @State private var telephotoMegapixelCount = Int() - /// A State variable used for determining whether or not a view is being presented. - @State var presentingAboutModal = false - /// A State variable used for determining whether or not a view is being presented. - @State var presentingCompatibilityModal = false - /// A variable to hold the existing instance of ``MalachiteClassesObject``. - var utilities = MalachiteClassesObject() - /// A variable used to hold the function for dismissing with the toolbar item. - var dismissAction: (() -> Void) - - /** - A variable used to hold the entire view. - - SwiftUI is weird... - Currently holds: - - Other variables to avoid type counting time issues. - - Handles initialization of variables required to show current settings. - - Navigation title of "Settings" - - Toolbar item for dismissing the view. - */ - var body: some View { - MalachiteNagivationViewUtils() { guts } - } - - var guts: some View { - Form { - aboutSection - previewSettingsSection - resolutionSettingsSection - photoSettingsSection - watermarkSettingsSection - uiSettingsSection - if utilities.versionType == "DEBUG" || utilities.versionType == "INTERNAL" { - debugSettingsSection - } - } - .onAppear { onAppear() } - .onDisappear { onDisappear() } - .navigationTitle("view.title.settings") - .toolbar(content: { - ToolbarItemGroup(placement: .topBarLeading) { - NavigationLink(destination: MalachiteSettingsDetailView(dismissAction: dismissAction)) { - if #available(iOS 26.0, *) { - Image(systemName: "questionmark.circle") - } else { - Image(systemName: "questionmark.circle").tint(.primary) - } - } - } - ToolbarItemGroup(placement: .topBarTrailing) { - if #available(iOS 26.0, *) { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark") - .tint(.primary) - } - .buttonStyle(.glassProminent) - } else { - Button { - self.dismissAction() - } label: { - Image(systemName: "checkmark.circle") - } - } - } - }) - } - - /// A variable to hold the about section. - var aboutSection: some View { - Section { - MalachiteCellViewUtils( - icon: "info.circle", - disabled: nil, - dangerous: false) - { - NavigationLink(destination: MalachiteAboutView(dismissAction: dismissAction)) { - Text("view.title.about") - } - } - if utilities.versionType == "INTERNAL" { - MalachiteCellViewUtils( - icon: "checkmark.seal", - disabled: nil, - dangerous: false) - { - NavigationLink(destination: MalachiteCompatibilityView(dismissAction: dismissAction, utilities: utilities)) { - Text("view.title.compatibility") - } - } - } - } - } - - /// A variable to hold the preview settings section. - var previewSettingsSection: some View { - Section(header: Text("settings.header.preview")) { - MalachiteCellViewUtils( - icon: "aspectratio", - disabled: false, - dangerous: false) - { - Picker("settings.option.preview.aspect_ratio", selection: $previewAspect) { - Text("settings.option.preview.aspect_ratio.fit") - .tag(0) - Text("settings.option.preview.aspect_ratio.fill") - .tag(1) - } - } - MalachiteCellViewUtils( - icon: "level", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.preview.sbtlz", isOn: $shouldStabilize) - } - // Testing things out - if utilities.versionType == "INTERNAL" { - MalachiteCellViewUtils( - icon: "forward", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.preview.fastpath", isOn: $shouldUseFastPath) - } - } - MalachiteCellViewUtils( - icon: "plus.magnifyingglass", - disabled: nil, - dangerous: false) - { - Stepper { - Text("settings.option.preview.zoom_maximum") - } onIncrement: { - if zoomMaximum < 10 { zoomMaximum += 1 } - } onDecrement: { - if zoomMaximum > 5 { zoomMaximum -= 1 } - } - Text(String(format: "%lldx", Int(zoomMaximum))) - .font(.footnote) - .foregroundColor(.gray) - } - } - .onChange(of: previewAspect) {_ in - utilities.preferences.preview.aspect = (previewAspect == 1) - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aspectFillNotification.name, object: nil) - } - .onChange(of: shouldStabilize) {_ in - utilities.preferences.preview.stablize = shouldStabilize - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.stabilizerNotification.name, object: nil) - } - .onChange(of: shouldUseFastPath) {_ in - utilities.preferences.preview.fastPath = shouldUseFastPath - } - .onChange(of: zoomMaximum) {_ in - utilities.preferences.capture.maximumZoom = zoomMaximum - } - } - - /// A variable to hold the image resolution section. - var resolutionSettingsSection: some View { - Section(header: Text("settings.header.resolution")) { - if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.ultrawide) { - MalachiteCellViewUtils( - icon: "camera.aperture", - disabled: utilities.preferences.ext.dictionary.getCount(dictionary: utilities.preferences.compatibility.ultrawide) == 1, - dangerous: false) - { - Picker("settings.option.resolution.ultrawide", selection: $ultrawideMegapixelCount) { - if let mp = utilities.preferences.compatibility.ultrawide["8"] { if mp { - Text("settings.option.resolution.8") - .tag(0) - } } - if let mp = utilities.preferences.compatibility.ultrawide["12"] { if mp { - Text("settings.option.resolution.12") - .tag(1) - } } - if let mp = utilities.preferences.compatibility.ultrawide["48"] { if mp { - Text("settings.option.resolution.48") - .tag(2) - } } - } - } - } - if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.wideangle) { - MalachiteCellViewUtils( - icon: "camera.aperture", - disabled: utilities.preferences.ext.dictionary.getCount(dictionary: utilities.preferences.compatibility.wideangle) == 1, - dangerous: false) - { - Picker("settings.option.resolution.wide", selection: $wideMegapixelCount) { - if let mp = utilities.preferences.compatibility.wideangle["8"] { if mp { - Text("settings.option.resolution.8") - .tag(0) - } } - if let mp = utilities.preferences.compatibility.wideangle["12"] { if mp { - Text("settings.option.resolution.12") - .tag(1) - } } - if let mp = utilities.preferences.compatibility.wideangle["48"] { if mp { - Text("settings.option.resolution.48") - .tag(2) - } } - } - } - } - if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.telephoto) { - MalachiteCellViewUtils( - icon: "camera.aperture", - disabled: utilities.preferences.ext.dictionary.getCount(dictionary: utilities.preferences.compatibility.telephoto) == 1, - dangerous: false) - { - Picker("settings.option.resolution.telephoto", selection: $telephotoMegapixelCount) { - if let mp = utilities.preferences.compatibility.wideangle["48"] { if mp { - Text("settings.option.resolution.12") - .tag(0) - } } - } - } - } - } - .onChange(of: ultrawideMegapixelCount) { _ in - switch ultrawideMegapixelCount { - case 1: - utilities.preferences.capture.mp.ultrawide = 12 - case 2: - utilities.preferences.capture.mp.ultrawide = 48 - default: - utilities.preferences.capture.mp.ultrawide = 8 - } - } - .onChange(of: wideMegapixelCount) { _ in - switch wideMegapixelCount { - case 1: - utilities.preferences.capture.mp.wideangle = 12 - case 2: - utilities.preferences.capture.mp.wideangle = 48 - default: - utilities.preferences.capture.mp.wideangle = 8 - } - } - .onChange(of: telephotoMegapixelCount) { _ in - switch telephotoMegapixelCount { - default: - utilities.preferences.capture.mp.telephoto = 12 - } - } - } - - /// A variable to hold the photo settings section. - var photoSettingsSection: some View { - Section(header: Text("settings.header.photo"), footer: (formatFooterText != nil) ? Text(formatFooterText!) : nil) { - MalachiteCellViewUtils( - icon: "square.and.arrow.down", - disabled: !supportsHEIC, - dangerous: false) - { - Picker("settings.option.photo.fileformat", selection: $photoFormat) { - Text("settings.option.photo.fileformat.jpeg") - .tag(0) - Text("settings.option.photo.fileformat.heif") - .tag(1) - } - } - - MalachiteCellViewUtils( - icon: "camera.filters", - disabled: !supportsHDR, - dangerous: false) - { - Toggle("settings.option.photo.hdr", isOn: $hdrSwitch) - } - MalachiteCellViewUtils( - icon: "plus.viewfinder", - disabled: nil, - dangerous: false) - { - Picker("settings.option.photo.continuous", selection: $continuousAEAF) { - Text("settings.option.reusable.aeaf") - .tag(0) - Text("settings.option.reusable.af") - .tag(1) - Text("settings.option.reusable.ae") - .tag(2) - Text("settings.option.reusable.off") - .tag(3) - } - } - } - .onChange(of: photoFormat) {_ in - utilities.preferences.capture.format.jpeg = photoFormat == 0 ? true : false - utilities.preferences.capture.format.heic = photoFormat == 1 ? true : false - } - .onChange(of: hdrSwitch) { _ in - utilities.preferences.capture.hdr = hdrSwitch - } - .onChange(of: continuousAEAF) { _ in - switch continuousAEAF { - case 0: - utilities.preferences.capture.continuous = ["ae", "af"] - case 1: - utilities.preferences.capture.continuous = ["af"] - case 2: - utilities.preferences.capture.continuous = ["ae"] - default: - utilities.preferences.capture.continuous = ["off"] - } - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.continousAEAFNotification.name, object: nil) - } - } - - /// A variable to hold the watermark settings section. - var watermarkSettingsSection: some View { - Section(header: Text("settings.header.watermark")) { - MalachiteCellViewUtils( - icon: "textformat", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.watermark.enable", isOn: $watermarkSwitch) - } - - MalachiteCellViewUtils( - icon: "signature", - disabled: nil, - dangerous: false) - { - Text("settings.option.watermark.text") - TextField("settings.option.watermark.text.placeholder", text: $watermarkText) - .multilineTextAlignment(.trailing) - .autocorrectionDisabled() - .keyboardType(.twitter) - } - } - .onChange(of: watermarkSwitch) {_ in - utilities.preferences.watermark.enabled = watermarkSwitch - } - .onChange(of: watermarkText) {_ in - utilities.preferences.watermark.text = watermarkText.isEmpty ? "Shot with Malachite" : String(watermarkText.prefix(65)) - } - } - - /// A variable to hold settings related to the user interface - var uiSettingsSection: some View { - Section(header: Text("settings.header.ui")) { - MalachiteCellViewUtils( - icon: "hand.tap", - disabled: nil, - dangerous: false) - { - Picker("settings.option.ui.tapgesture", selection: $poiTapAndHold) { - Text("settings.option.reusable.aeaf") - .tag(0) - Text("settings.option.reusable.af") - .tag(1) - Text("settings.option.reusable.ae") - .tag(2) - Text("settings.option.reusable.off") - .tag(3) - } - } - if utilities.versionType == "INTERNAL" { - MalachiteCellViewUtils( - icon: "hand.draw", - disabled: nil, - dangerous: false) - { - Picker("settings.option.ui.settingsgesture", selection: $settingsGestureFingers) { - Text("settings.option.ui.settingsgesture.1") - .tag(1) - Text("settings.option.ui.settingsgesture.2") - .tag(2) - Text("settings.option.ui.settingsgesture.3") - .tag(3) - } - } - } - MalachiteCellViewUtils( - icon: "hand.raised.slash", - disabled: nil, - dangerous: false) - { - Picker("settings.option.ui.hiddengestures", selection: $uiHiderGestures) { - Text("settings.option.reusable.pinchzoomtapandhold") - .tag(0) - Text("settings.option.reusable.pinchzoom") - .tag(1) - Text("settings.option.reusable.tapandhold") - .tag(2) - Text("settings.option.reusable.off") - .tag(3) - } - } - MalachiteCellViewUtils( - icon: "clock", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.ui.idletimer", isOn: $idleTimerDisabled) - } - MalachiteCellViewUtils( - icon: "iphone.radiowaves.left.and.right", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.ui.haptics", isOn: $hapticsDisabled) - } - MalachiteCellViewUtils( - icon: "eye.slash", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.ui.hiddenonlaunch", isOn: $appStartsUIHidden) - } - } - .onChange(of: poiTapAndHold) {_ in - switch poiTapAndHold { - case 0: - utilities.preferences.userInterface.tapAndHold = ["ae", "af"] - case 1: - utilities.preferences.userInterface.tapAndHold = ["af"] - case 2: - utilities.preferences.userInterface.tapAndHold = ["ae"] - default: - utilities.preferences.userInterface.tapAndHold = ["off"] - } - } - .onChange(of: settingsGestureFingers) {_ in - utilities.preferences.evaintrnl.settingsGesture = settingsGestureFingers - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.settingsGestureNotification.name, object: nil) - } - .onChange(of: uiHiderGestures) {_ in - switch uiHiderGestures { - case 0: - utilities.preferences.userInterface.hiddenControls = ["zoom", "tah"] - case 1: - utilities.preferences.userInterface.hiddenControls = ["zoom"] - case 2: - utilities.preferences.userInterface.hiddenControls = ["tah"] - default: - utilities.preferences.userInterface.hiddenControls = ["off"] - } - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aeafTapGestureNotification.name, object: nil) - } - .onChange(of: idleTimerDisabled) { _ in - utilities.preferences.userInterface.idleTimerDisabled = idleTimerDisabled - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.idleTimerNotification.name, object: nil) - } - .onChange(of: hapticsDisabled) { _ in - utilities.preferences.userInterface.hapticFeedback = hapticsDisabled - } - .onChange(of: appStartsUIHidden) { _ in - utilities.preferences.userInterface.appLaunch = appStartsUIHidden - } - } - - /// A variable to hold the debug settings section. Only available with debug builds. - // TODO: Update to the new preferences system - var debugSettingsSection: some View { - Section(header: Text("settings.header.debug")) { - MalachiteCellViewUtils( - icon: "text.redaction", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.debug.logging.userdefaults", isOn: $debugLoggingUserDefaults) - } - MalachiteCellViewUtils( - icon: "iphone.slash", - disabled: nil, - dangerous: false) - { - Toggle("settings.option.debug.breakapp", isOn: $breakApp) - } - MalachiteCellViewUtils( - icon: "trash", - disabled: nil, - dangerous: true) - { - Button { - utilities.debugNSLog("[Preferences] Resetting all preferences, relaunch the app to complete!") - utilities.preferences.ext.resetPreferences() - } label: { - if #available(iOS 17.0, *) { - Text("settings.option.debug.erase.userdefaults") - .foregroundStyle(.red) - } else { - Text("settings.option.debug.erase.userdefaults") - .foregroundColor(.red) - } - } - } - - if utilities.versionType == "INTERNAL" { - MalachiteCellViewUtils( - icon: "trash", - disabled: nil, - dangerous: true) - { - Button { - utilities.internalNSLog("[Preferences] Resetting all GameKit data!") - utilities.games.achievements.resetAchievements() - } label: { - if #available(iOS 17.0, *) { - Text("settings.option.debug.erase.gamekit") - .foregroundStyle(.red) - } else { - Text("settings.option.debug.erase.gamekit") - .foregroundColor(.red) - } - } - } - } - } - .onChange(of: debugLoggingUserDefaults) {_ in - utilities.preferences.debug.logging.preferences = debugLoggingUserDefaults - } - } - - func onAppear() { - supportsHDR = utilities.function.supportsHDR - supportsHEIC = utilities.function.supportsHEIC() - - if !supportsHEIC { - formatFooterText = "settings.footer.photo.heif".localized - } - - if !supportsHDR { - formatFooterText = (formatFooterText != nil) ? formatFooterText! + "settings.footer.photo.hdr".localized : "settings.footer.photo.hdr".localized - } - - shouldStabilize = utilities.preferences.preview.stablize - shouldUseFastPath = utilities.preferences.preview.fastPath - zoomMaximum = utilities.preferences.capture.maximumZoom - previewAspect = utilities.preferences.preview.aspect ? 1 : 0 - - switch utilities.preferences.capture.mp.ultrawide { - case 12: - ultrawideMegapixelCount = 1 - case 48: - ultrawideMegapixelCount = 2 - default: - ultrawideMegapixelCount = 0 - } - - switch utilities.preferences.capture.mp.wideangle { - case 12: - wideMegapixelCount = 1 - case 48: - wideMegapixelCount = 2 - default: - wideMegapixelCount = 0 - } - - switch utilities.preferences.capture.mp.telephoto { - default: - telephotoMegapixelCount = 0 - } - - hdrSwitch = utilities.preferences.capture.hdr - - // TODO: Better way to do this - photoFormat = utilities.preferences.capture.format.jpeg ? 0 : 1 - photoFormat = utilities.preferences.capture.format.heic ? 1 : 0 - - switch utilities.preferences.capture.continuous { - case ["ae", "af"]: - continuousAEAF = 0 - case ["af"]: - continuousAEAF = 1 - case ["ae"]: - continuousAEAF = 2 - default: - continuousAEAF = 3 - } - - watermarkText = utilities.preferences.watermark.text - watermarkSwitch = utilities.preferences.watermark.enabled - - settingsGestureFingers = utilities.preferences.evaintrnl.settingsGesture - idleTimerDisabled = utilities.preferences.userInterface.idleTimerDisabled - hapticsDisabled = utilities.preferences.userInterface.hapticFeedback - appStartsUIHidden = utilities.preferences.userInterface.appLaunch - - switch utilities.preferences.userInterface.tapAndHold { - case ["ae", "af"]: - poiTapAndHold = 0 - case ["af"]: - poiTapAndHold = 1 - case ["ae"]: - poiTapAndHold = 2 - default: - poiTapAndHold = 3 - } - - switch utilities.preferences.userInterface.hiddenControls { - case ["zoom", "tah"]: - uiHiderGestures = 0 - case ["zoom"]: - uiHiderGestures = 1 - case ["tah"]: - uiHiderGestures = 2 - default: - uiHiderGestures = 3 - } - - debugLoggingUserDefaults = utilities.preferences.debug.logging.preferences - breakApp = utilities.preferences.debug.breakApp - } - - func onDisappear() { - utilities.preferences.preview.aspect = (previewAspect == 1) - utilities.preferences.preview.stablize = shouldStabilize - utilities.preferences.preview.fastPath = shouldUseFastPath - utilities.preferences.capture.maximumZoom = zoomMaximum - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aspectFillNotification.name, object: nil) - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.stabilizerNotification.name, object: nil) - - switch ultrawideMegapixelCount { - case 1: - utilities.preferences.capture.mp.ultrawide = 12 - case 2: - utilities.preferences.capture.mp.ultrawide = 48 - default: - utilities.preferences.capture.mp.ultrawide = 8 - } - - switch wideMegapixelCount { - case 1: - utilities.preferences.capture.mp.wideangle = 12 - case 2: - utilities.preferences.capture.mp.wideangle = 48 - default: - utilities.preferences.capture.mp.wideangle = 8 - } - - switch telephotoMegapixelCount { - default: - utilities.preferences.capture.mp.telephoto = 12 - } - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.megaPixelSwitchNotification.name, object: nil) - - utilities.preferences.capture.hdr = hdrSwitch - utilities.preferences.capture.format.jpeg = photoFormat == 0 ? true : false - utilities.preferences.capture.format.heic = photoFormat == 1 ? true : false - - switch continuousAEAF { - case 0: - utilities.preferences.capture.continuous = ["ae", "af"] - case 1: - utilities.preferences.capture.continuous = ["af"] - case 2: - utilities.preferences.capture.continuous = ["ae"] - default: - utilities.preferences.capture.continuous = ["off"] - } - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.continousAEAFNotification.name, object: nil) - - utilities.preferences.watermark.enabled = watermarkSwitch - utilities.preferences.watermark.text = watermarkText.isEmpty ? "Shot with Malachite" : String(watermarkText.prefix(65)) - - utilities.preferences.evaintrnl.settingsGesture = settingsGestureFingers - utilities.preferences.userInterface.idleTimerDisabled = idleTimerDisabled - utilities.preferences.userInterface.hapticFeedback = hapticsDisabled - utilities.preferences.userInterface.appLaunch = appStartsUIHidden - - switch poiTapAndHold { - case 0: - utilities.preferences.userInterface.tapAndHold = ["ae", "af"] - case 1: - utilities.preferences.userInterface.tapAndHold = ["af"] - case 2: - utilities.preferences.userInterface.tapAndHold = ["ae"] - default: - utilities.preferences.userInterface.tapAndHold = ["off"] - } - - switch uiHiderGestures { - case 0: - utilities.preferences.userInterface.hiddenControls = ["zoom", "tah"] - case 1: - utilities.preferences.userInterface.hiddenControls = ["zoom"] - case 2: - utilities.preferences.userInterface.hiddenControls = ["tah"] - default: - utilities.preferences.userInterface.hiddenControls = ["off"] - } - - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.settingsGestureNotification.name, object: nil) - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aeafTapGestureNotification.name, object: nil) - NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.idleTimerNotification.name, object: nil) - - utilities.preferences.debug.logging.preferences = debugLoggingUserDefaults - utilities.preferences.debug.breakApp = breakApp - } -} diff --git a/Malachite/Views/MalachiteView.swift b/Malachite/Views/MalachiteView.swift deleted file mode 100755 index f2eb692..0000000 --- a/Malachite/Views/MalachiteView.swift +++ /dev/null @@ -1,1190 +0,0 @@ -// -// ViewController.swift -// Malachite -// -// Created by Eva Isabella Luna on 11/25/23. -// - -import SwiftUI -import UIKit -import Foundation -import AVFoundation -import AVKit -import LockedCameraCapture -import Photos -import GameKit - -struct MalachiteView_SwiftUIWrapped: UIViewControllerRepresentable { - let rootURL: URL? - typealias UIViewControllerType = MalachiteView - func makeUIViewController(context: Self.Context) -> MalachiteView { - return MalachiteView() - } - - func updateUIViewController(_ uiViewController: MalachiteView, context: Self.Context) { - } -} - -@available(iOS 18.0, *) -extension MalachiteView_SwiftUIWrapped { - init(_ session: LockedCameraCaptureSession) { - self.rootURL = session.sessionContentURL - } -} - -class MalachiteView: UIViewController, AVCaptureMetadataOutputObjectsDelegate, AVCapturePhotoCaptureDelegate, AVCaptureSessionControlsDelegate { - func sessionControlsDidBecomeActive(_ session: AVCaptureSession) { - if !uiIsHidden { runUIHider() } - } - - func sessionControlsWillEnterFullscreenAppearance(_ session: AVCaptureSession) { - if !uiIsHidden { runUIHider() } - } - - func sessionControlsWillExitFullscreenAppearance(_ session: AVCaptureSession) { - if uiIsHidden { runUIHider() } - } - - func sessionControlsDidBecomeInactive(_ session: AVCaptureSession) { - if uiIsHidden { runUIHider() } - if cameraIndex != nil { - runInputSwitch() - self.cameraIndex = nil - } - } - - /// The `AVCaptureSession` Malachite uses for everything. - var cameraSession: AVCaptureSession? - /// The currently selected `AVCaptureDevice` for input to ``cameraSession``. - var selectedDevice: AVCaptureDevice? - /// An array respresenting the device's available rear cameras. This variable will be `nil` if no cameras are present (as is the case of the iOS simulator) - var availableRearCameras = [AVCaptureDevice]() - /// The device's currently available rear ultra-wide angle `AVCaptureDevice`, if available. This variable is `nil` if no ultra-wide angle camera is present (i.e. single-camera, Simulator). - var ultraWideDevice: AVCaptureDevice? - /// The device's currently available wide angle `AVCaptureDevice`, if available. This variable is `nil` if no wide angle camera is present (currently only in the Simulator). - var wideAngleDevice: AVCaptureDevice? - /// The currently selected `AVCaptureDeviceInput` for input to ``cameraSession``. - var selectedInput: AVCaptureDeviceInput? - /// The `AVCapturePhotoOutput` used to capture photos with ``selectedDevice`` and ``cameraSession``. - var photoOutput = AVCapturePhotoOutput() - /// The `AVCaptureVideoPreviewLayer` used to allow users to see a preview of their camera before taking a shot with ``photoOutput``. - var cameraPreview: AVCaptureVideoPreviewLayer? - /// A `Bool` that determines whether or not the wide angle lens is in use. - var wideAngleInUse = true - /// A `Bool` that determines whether or not the app is still initializing. Uses for tasks that should only be run once at the start of Malachite. - var initRun = true - /// A `CGFloat` that temporarily holds the zoom factor. - var zoomFloater: CGFloat? - /// A `Float` that temporarily holds the focus factor. - var focusFloater: Float? - /// A `Float` that temporarily holds the level of flash brightness to use. - var flashFloater: Float? - /// A `Bool` that temporarily holds the current status of the flashlight. - var flashStatus = Bool() - /// An `Int` that temporarily holds the index of the camera to switch to. - var cameraIndex: Int? - - /// A `UIButton` that enables the user to switch between the ultra-wide and wide angle cameras. - var cameraButton = UIButton() - /// A `UIButton` that enables the user to toggle the flashlight's on state. - var flashlightButton = UIButton() - /// A `UIButton` that enables the user to take photos. - var captureButton = UIButton() - /// A `UIButton` that enables the user to change settings within the app. - var settingsButton = UIButton() - - /// A `UIButton` that enables the user to reveal the ``focusSlider`` for manual focus adjustment. - var focusButton = UIButton() - /// A `UIButton` that holds the ``focusSlider`` for improved blur compatibility and shaping. - var focusSliderButton = UIButton() - /// A `UISlider` that enables the user to manually adjust the lens position. - var focusSlider = UISlider() - /// A `UIButton` that enables the user to toggle the lock states for the ``focusSlider`` and the ``aeafRecognizer``. - var focusLockButton = UIButton() - /// A `Bool` that determines whether or not the ``focusSlider`` is currently displayed on the user's screen. - var manualFocusSliderIsActive = false - /// A `Bool` that determines whether or not the ``focusLockButton`` is currently set to Locked. - var manualFocusLockIsActive = false - - /// A `UIButton` that enables the user to reveal the ``exposureSlider`` for manual exposure adjustment. - var exposureButton = UIButton() - /// A `UIButton` that holds the ``exposureSlider`` for improved blur compatibility and shaping. - var exposureSliderButton = UIButton() - /// A `UISlider` that enables the user to manually adjust the exposure level. - var exposureSlider = UISlider() - /// A `UIButton` that enables the user to toggle the lock state for the ``exposureSlider``. Auto exposure toggling will come at a later date. - var exposureLockButton = UIButton() - /// A `Bool` that determines whether or not the ``exposureSlider`` is currently displayed on the user's screen. - var manualExposureSliderIsActive = false - /// A `Bool` that determines whether or not the ``exposureLockButton`` is currently set to Locked. - var manualExposureLockIsActive = false - - /// A `UIPinchGestureRecognizer` that handles zooming in and out of the ``cameraSession``. - var zoomRecognizer = UIPinchGestureRecognizer() - /// A `UILongPressGestureRecognizer` that handles enabling the AE+AF system at a specific point on the display for the ``cameraSession``. - var aeafRecognizer = UILongPressGestureRecognizer() - /// A `UIButton` that contains the blur for the on-screen feedback produced by the auto focus gesture. - var aeafFeedback = UIButton() - /// A `UIPanGestureRecognizer` that handles opening settings with a gesture. - var settingsRecognizer = UISwipeGestureRecognizer() - /// A `UILongPressGestureRecognizer` that handles hiding all elements of the user interface, and disabling the ``zoomRecognizer`` and ``aeafRecognizer`` gestures. - var uiHiderRecognizer = UILongPressGestureRecognizer() - /// - var eventInteraction: Any? = { - if #available(iOS 17.2, *) { - return AVCaptureEventInteraction?.self - } else { - return nil - } - }() - /// A `Bool` that determines whether or not the user interface is currently hidden to the user. - var uiIsHidden = false - - /// The title for the focus slider. - var focusTitle = UILabel() - /// The title for the exposure slider. - var exposureTitle = UILabel() - /// The button used to display what camera is in use. - var currentCamera = UIButton() - - /// The minimum zoom value that the ``zoomRecognizer`` is allowed to reach. - let minimumZoom: CGFloat = 1.0 - /// The maximum zoom value that the ``zoomRecognizer`` is allowed to reach. - let maximumZoom: CGFloat = 5.0 - /// The last known zoom factor that the ``zoomRecognizer`` was set to. - var lastZoomFactor: CGFloat = 1.0 - - /// A `UIActivityIndicatorView` used to let the user know that Malachite is processing the image. - var progressIndicator = UIActivityIndicatorView() - - /// An instance of ``MalachiteClassesObject`` for reuse across the app. - public var utilities = MalachiteClassesObject() - /// An observer for the device's rotation. - private var rotationObserver: NSObjectProtocol? - - var cameraView = UIView() - - /** - viewDidLoad override for the main user interface. - - This function currently serves to do the following: - - Create and assign a value to all variables needed to run ``cameraSession`` and its preview layer, ``cameraPreview``. - - Read the user's preferences to determine the preview layer's aspect ratio. - - Register notifications for changes to certain options in ``MalachiteSettingsView``, as well as orientation changes. - */ - override func viewDidLoad() { - super.viewDidLoad() - self.view.backgroundColor = .clear - - utilities.debugNSLog("[Initialization] Starting up Malachite") - - if utilities.versionType == "INTERNAL" { - utilities.internalNSLog("[Initialization] Running an INTERNAL build, logging will be force enabled") - } else if utilities.versionType == "DEBUG" { - utilities.debugNSLog("[Initialization] Running a DEBUG build, logging will be force enabled") - } else if utilities.versionType == "RELEASE" { - utilities.NSLog("[Initialization] Running a RELEASE build") - } - - #if APP_EXTENSION - utilities.debugNSLog("[Initialization] Running out of an app extension.") - #elseif MAIN_APP - utilities.debugNSLog("[Initialization] Running out of the main app.") - #endif - - if utilities.versionType == "INTERNAL" { - if !utilities.preferences.ext.deviceModel.isSameDevice(in: &utilities.preferences) { - utilities.internalNSLog("[Initialization] This is a new device, rechecking compatibility.") - utilities.preferences.general.deviceModel = utilities.preferences.ext.deviceModel.get() - } else { - utilities.internalNSLog("[Initialization] This is the same device, can skip compatibility checks.") - } - } - - if !utilities.function.supportsHEIC() { - utilities.debugNSLog("[Initialization] HEIF enabled on a device that doesn't support it, disabling") - utilities.preferences.capture.format.heic = false - utilities.preferences.capture.format.jpeg = true - } - - cameraPreview?.frame.size = self.view.frame.size - utilities.debugNSLog("[Initialization] Bringing up AVCaptureSession") - cameraSession = AVCaptureSession() - - utilities.debugNSLog("[Initialization] Bringing up AVCaptureDeviceInput") - - utilities.debugNSLog("[Camera Input] Getting current camera system capabilities") - - var camerasToDiscover: [AVCaptureDevice.DeviceType] = [] - if #available(iOS 17.0, *) { camerasToDiscover = [.builtInUltraWideCamera, .builtInWideAngleCamera, .builtInTelephotoCamera, .continuityCamera, .external] } - else { camerasToDiscover = [.builtInUltraWideCamera, .builtInWideAngleCamera, .builtInTelephotoCamera] } - - utilities.debugNSLog("[Camera Input] Discovering available cameras") - let currentProcess = ProcessInfo() - AVCaptureDevice.DiscoverySession.init(deviceTypes: camerasToDiscover, mediaType: .video, position: (currentProcess.isiOSAppOnMac || currentProcess.isMacCatalystApp) ? .unspecified : .back).devices.forEach { device in - self.availableRearCameras.append(device) - utilities.debugNSLog("[Camera Input] \(device.localizedName)") - utilities.debugNSLog("[Camera Input] \(device.deviceType.rawValue) available") - } - - runInputSwitch() - - if #available(iOS 18.0, *) { - cameraSession?.setControlsDelegate(self, queue: utilities.sessionQueue) - } - - if self.availableRearCameras.first != nil { - photoOutput = AVCapturePhotoOutput() - if #available(iOS 16.0, *) {} else { photoOutput.isHighResolutionCaptureEnabled = true } - photoOutput.maxPhotoQualityPrioritization = .quality - cameraSession?.sessionPreset = AVCaptureSession.Preset.photo - cameraSession?.addOutput(photoOutput) - - utilities.debugNSLog("[Initialization] Bringing up AVCaptureVideoPreviewLayer") - cameraPreview = AVCaptureVideoPreviewLayer(session: cameraSession!) - - var statusBarOrientation = UIInterfaceOrientation.portrait - #if MAIN_APP - if let windowScene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene { - statusBarOrientation = windowScene.interfaceOrientation - } - #endif - cameraPreview?.frame = view.layer.bounds - let videoOrientation: AVCaptureVideoOrientation = (statusBarOrientation.videoOrientation) - cameraPreview?.connection?.videoOrientation = videoOrientation - - if utilities.preferences.preview.aspect { - cameraPreview?.videoGravity = AVLayerVideoGravity.resizeAspectFill - } else { - cameraPreview?.videoGravity = AVLayerVideoGravity.resizeAspect - } - - cameraView = UIView(frame: self.view.bounds) - cameraView.layer.addSublayer(cameraPreview!) - self.view.insertSubview(cameraView, at: 0) - - utilities.debugNSLog("[Initialization] Starting session stream") - DispatchQueue.global(qos: .background).async { - self.cameraSession?.startRunning() - } - } else { - utilities.debugNSLog("[Initialization] No cameras detected, skipping to user interface bringup") - } - - utilities.debugNSLog("[Initialization] Setting up notification observer for orientation changes") - #if MAIN_APP - NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil) - #endif - NotificationCenter.default.addObserver(self, selector: #selector(changeAspectFill), name: MalachiteFunctionUtils.Notifications.aspectFillNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeExposureLimit), name: MalachiteFunctionUtils.Notifications.exposureLimitNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeStabilizerMode), name: MalachiteFunctionUtils.Notifications.stabilizerNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeGameCenterEnabled), name: MalachiteFunctionUtils.Notifications.gameCenterEnabledNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(runManualExposureUIHiderWhenUnsupported), name: MalachiteFunctionUtils.Notifications.unsupportedISOValueNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(runManualFocusUIHiderWhenUnsupported), name: MalachiteFunctionUtils.Notifications.unsupportedLensPositionNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeContinuousAEAF), name: MalachiteFunctionUtils.Notifications.continousAEAFNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeAEAFRecognizer), name: MalachiteFunctionUtils.Notifications.aeafTapGestureNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeIdleTimerState), name: MalachiteFunctionUtils.Notifications.idleTimerNotification.name, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(runInputMegapixelSwitch), name: MalachiteFunctionUtils.Notifications.megaPixelSwitchNotification.name, object: nil) - - UIDevice.current.beginGeneratingDeviceOrientationNotifications() - - if utilities.versionType == "INTERNAL" || utilities.versionType == "DEBUG" { - if utilities.preferences.debug.logging.preferences { - MalachitePreferencesUtils().printPreferences() - } - } - - if #available (iOS 17.2, *) { - let interaction = AVCaptureEventInteraction { event in - if event.phase == .ended { - self.runImageCapture() - } - } - self.view.addInteraction(interaction) - eventInteraction = interaction - } - - - let cameraAuthStatus = PHPhotoLibrary.authorizationStatus(for: .addOnly) - - if cameraAuthStatus == .notDetermined { - PHPhotoLibrary.requestAuthorization(for: .addOnly) { [self] status in - utilities.debugNSLog("[Permissions] Camera authorization status: \(status)") - } - } - } - - /** - viewDidAppear override for the main user interface. - - This function currently serves to do the following: - - Create all buttons and gestures required to operate the user interface. - - Set up GameKit integration for achievements and leaderboard reporting. - - `DEBUG` builds of Malachite additionally do the following: - - Dump the contents of UserDefaults. - - Dump the contents of Game Center achievements and leaderboards. - */ - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - utilities.debugNSLog("[Initialization] Presenting user interface") - setupView() - if utilities.versionType == "INTERNAL" { - setupView_INTERNAL() - } - - self.changeGameCenterEnabled() - } - - /** - Function used to determine what rotation Malachite should be in on iPadOS. - - iPhones follow the stock camera apps's behavior of only rotating buttons, while iPads get the ability to rotate the entire device. - TODO: fix bugs regarding this... - */ - func transformOrientation(orientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation { - switch orientation { - case .landscapeLeft: - return .landscapeLeft - case .landscapeRight: - return .landscapeRight - case .portraitUpsideDown: - return .portraitUpsideDown - default: - return .portrait - } - } - - func setupView_INTERNAL() { - settingsRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(runSettingsGesture)) - updateSettingsGestureFingerCount() - settingsRecognizer.direction = .up - - self.view.addGestureRecognizer(settingsRecognizer) - - NotificationCenter.default.addObserver(self, selector: #selector(updateSettingsGestureFingerCount), name: MalachiteFunctionUtils.Notifications.settingsGestureNotification.name, object: nil) - } - - /** - Function to register buttons and gestures for operating Malachite. - - This function creates and provides layout properties for the following views: - - ``cameraButton`` - - ``flashlightButton`` - - ``captureButton`` - - ``focusButton`` - - ``focusSliderButton`` - - ``focusSlider`` - - ``focusLockButton`` - - ``exposureButton`` - - ``exposureSliderButton`` - - ``exposureSlider`` - - ``exposureLockButton`` - - ``settingsButton`` - - This function also creates the following gesture recognizers: - - ``zoomRecognizer`` - Pinch-to-zoom gesture - - ``aeafRecognizer`` - Tap and hold with one finger - - ``uiHiderRecognizer`` - Tap and hold with two fingers - */ - func setupView(){ - self.view.backgroundColor = .black - -#if targetEnvironment(simulator) - setupLmaoView() -#endif - - cameraButton = utilities.views.returnProperButton(symbolName: "camera", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - - flashlightButton = utilities.views.returnProperButton(symbolName: "flashlight.off.fill", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - captureButton = utilities.views.returnProperButton(symbolName: "camera.aperture", cornerRadius: 45, viewForBounds: view, hapticClass: utilities.haptics) - focusButton = utilities.views.returnProperButton(symbolName: "scope", cornerRadius: 30, viewForBounds: view, hapticClass: utilities.haptics) - focusSliderButton = utilities.views.returnProperButton(symbolName: "", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - focusLockButton = utilities.views.returnProperButton(symbolName: "lock.open", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - exposureButton = utilities.views.returnProperButton(symbolName: "plusminus", cornerRadius: 30, viewForBounds: view, hapticClass: utilities.haptics) - exposureSliderButton = utilities.views.returnProperButton(symbolName: "", cornerRadius: 30, viewForBounds: view, hapticClass: utilities.haptics) - exposureLockButton = utilities.views.returnProperButton(symbolName: "lock.open", cornerRadius: 30, viewForBounds: view, hapticClass: utilities.haptics) - settingsButton = utilities.views.returnProperButton(symbolName: "gear", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - aeafFeedback = utilities.views.returnProperButton(symbolName: "", cornerRadius: 60, viewForBounds: self.view, hapticClass: utilities.haptics) - currentCamera = utilities.views.returnProperButton(symbolName: "", cornerRadius: 30, viewForBounds: self.view, hapticClass: nil) - focusSlider.translatesAutoresizingMaskIntoConstraints = false - exposureSlider.translatesAutoresizingMaskIntoConstraints = false - focusLockButton.alpha = 0.0 - exposureLockButton.alpha = 0.0 - aeafFeedback.alpha = 0.0 - - self.view.addSubview(cameraButton) - self.view.addSubview(flashlightButton) - self.view.addSubview(captureButton) - self.view.addSubview(focusButton) - self.view.addSubview(focusSliderButton) - self.view.addSubview(focusLockButton) - self.view.addSubview(exposureButton) - self.view.addSubview(exposureSliderButton) - self.view.addSubview(exposureLockButton) - self.view.addSubview(settingsButton) - self.view.addSubview(aeafFeedback) - self.view.addSubview(currentCamera) - focusSliderButton.addSubview(focusSlider) - exposureSliderButton.addSubview(exposureSlider) - - if self.availableRearCameras.count > 0 { - cameraButton.addTarget(self, action: #selector(self.runInputSwitch), for: .touchUpInside) - flashlightButton.addTarget(self, action: #selector(self.runFlashlightToggle), for: .touchUpInside) - captureButton.addTarget(self, action: #selector(self.runImageCapture), for: .touchUpInside) - focusSlider.addTarget(self, action: #selector(self.runManualFocusController), for: .valueChanged) - focusSlider.addTarget(utilities.haptics, action: #selector(utilities.haptics.buttonMediumHaptics(_:)), for: .touchUpInside) - focusLockButton.addTarget(self, action: #selector(runManualFocusLockController), for: .touchUpInside) - exposureSlider.addTarget(self, action: #selector(runManualExposureController), for: .valueChanged) - exposureSlider.addTarget(utilities.haptics, action: #selector(utilities.haptics.buttonMediumHaptics(_:)), for: .touchUpInside) - exposureLockButton.addTarget(self, action: #selector(self.runManualExposureLockController), for: .touchUpInside) - } - - focusButton.addTarget(self, action: #selector(self.runManualFocusUIHider), for: .touchUpInside) - exposureButton.addTarget(self, action: #selector(self.runManualExposureUIHider), for: .touchUpInside) - settingsButton.addTarget(self, action: #selector(self.presentSettingsView), for: .touchUpInside) - - zoomRecognizer = UIPinchGestureRecognizer(target: self, action:#selector(runZoomController)) - aeafRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(runaeafController)) - uiHiderRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(runUIHider)) - uiHiderRecognizer.numberOfTouchesRequired = 2 - - - focusTitle = utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: view, textForFlow: NSLocalizedString("uibutton.focus.title", comment: ""), anchorConstant: 10) - exposureTitle = utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: view, textForFlow: NSLocalizedString("uibutton.exposure.title", comment: ""), anchorConstant: 80) - - self.view.addGestureRecognizer(zoomRecognizer) - let tapGestureElements = utilities.preferences.userInterface.tapAndHold - if !tapGestureElements.contains("off") { self.view.addGestureRecognizer(aeafRecognizer) } - self.view.addGestureRecognizer(uiHiderRecognizer) - - var lockButtonsX = -80.0 - var lockButtonsY = 0.0 - - if self.view.frame.size.width >= 370 { - utilities.debugNSLog("[Initialization] Device screen is capable of displaying lock button inline") - lockButtonsX = -300.0 - } else { - // TODO: Make lock buttons not clip into other bars! - NSLog("[Initialization] Device screen is too small for inline lock button") - lockButtonsY = 70.0 - } - - utilities.tooltips.fadeOutTooltipFlow(labelsToFade: [ focusTitle, exposureTitle]) - utilities.tooltips.zoomTooltipFlow(button: currentCamera, viewForBounds: self.view, camera: selectedDevice) - - NSLayoutConstraint.activate([ - cameraButton.widthAnchor.constraint(equalToConstant: 60), - cameraButton.heightAnchor.constraint(equalToConstant: 60), - cameraButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - cameraButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), - - settingsButton.widthAnchor.constraint(equalToConstant: 60), - settingsButton.heightAnchor.constraint(equalToConstant: 60), - settingsButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -80), - settingsButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), - - flashlightButton.widthAnchor.constraint(equalToConstant: 60), - flashlightButton.heightAnchor.constraint(equalToConstant: 60), - flashlightButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - flashlightButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - - captureButton.widthAnchor.constraint(equalToConstant: 90), - captureButton.heightAnchor.constraint(equalToConstant: 90), - captureButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - captureButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor, constant: 0), - - focusButton.widthAnchor.constraint(equalToConstant: 60), - focusButton.heightAnchor.constraint(equalToConstant: 60), - focusButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10), - focusButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - - focusSliderButton.widthAnchor.constraint(equalToConstant: 210), - focusSliderButton.heightAnchor.constraint(equalToConstant: 60), - focusSliderButton.topAnchor.constraint(equalTo: focusButton.topAnchor), - focusSliderButton.leadingAnchor.constraint(equalTo: focusButton.trailingAnchor, constant: 10), - - focusSlider.widthAnchor.constraint(equalToConstant: 180), - focusSlider.heightAnchor.constraint(equalToConstant: 80), - focusSlider.centerYAnchor.constraint(equalTo: focusSliderButton.centerYAnchor), - focusSlider.centerXAnchor.constraint(equalTo: focusSliderButton.trailingAnchor, constant: -105), - - focusLockButton.widthAnchor.constraint(equalToConstant: 60), - focusLockButton.heightAnchor.constraint(equalToConstant: 60), - focusLockButton.topAnchor.constraint(equalTo: focusButton.topAnchor, constant: lockButtonsY), - focusLockButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: lockButtonsX), - - exposureButton.widthAnchor.constraint(equalToConstant: 60), - exposureButton.heightAnchor.constraint(equalToConstant: 60), - exposureButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 80), - exposureButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - - exposureSliderButton.widthAnchor.constraint(equalToConstant: 210), - exposureSliderButton.heightAnchor.constraint(equalToConstant: 60), - exposureSliderButton.topAnchor.constraint(equalTo: exposureButton.topAnchor), - exposureSliderButton.leadingAnchor.constraint(equalTo: exposureButton.trailingAnchor, constant: 10), - - exposureSlider.widthAnchor.constraint(equalToConstant: 180), - exposureSlider.heightAnchor.constraint(equalToConstant: 80), - exposureSlider.centerYAnchor.constraint(equalTo: exposureSliderButton.centerYAnchor), - exposureSlider.centerXAnchor.constraint(equalTo: exposureSliderButton.trailingAnchor, constant: -105), - - exposureLockButton.widthAnchor.constraint(equalToConstant: 60), - exposureLockButton.heightAnchor.constraint(equalToConstant: 60), - exposureLockButton.topAnchor.constraint(equalTo: exposureButton.topAnchor, constant: lockButtonsY), - exposureLockButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: lockButtonsX), - - currentCamera.widthAnchor.constraint(equalToConstant: 60), - currentCamera.heightAnchor.constraint(equalToConstant: 60), - currentCamera.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10), - currentCamera.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), - ]) - - if utilities.preferences.userInterface.appLaunch { - cameraButton.alpha = 0.0 - flashlightButton.alpha = 0.0 - captureButton.alpha = 0.0 - focusButton.alpha = 0.0 - focusSliderButton.alpha = 0.0 - exposureButton.alpha = 0.0 - exposureSliderButton.alpha = 0.0 - settingsButton.alpha = 0.0 - currentCamera.alpha = 0.0 - focusTitle.alpha = 0.0 - exposureTitle.alpha = 0.0 - - uiIsHidden = true - } - - setupGameKitAlert() - changeIdleTimerState() - } - - func setupGameKitAlert() { - if utilities.preferences.general.gamekit.alerted { - let alert = UIAlertController(title: "alert.title.gamekit".localized, message: "alert.detail.gamekit".localized, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.reopen", comment: "Default action"), style: .default, handler: { _ in - exit(11) - })) - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.report", comment: "Default action"), style: .default, handler: { _ in - guard let url = URL(string: "https://www.youtube.com/watch?v=At8v_Yc044Y") else { - return - } - #if MAIN_APP - UIApplication.shared.open(url, options: [:], completionHandler: nil) - #endif - })) - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ignore", comment: "Default action"), style: .default, handler: { _ in - self.utilities.preferences.general.gamekit.alerted = false - })) - self.present(alert, animated: true, completion: nil) - } - } - - func setupLmaoView() { - let lmaoView = UIImageView(image: utilities.views.returnImageForSimulator()) - self.view.addSubview(lmaoView) - - NSLayoutConstraint.activate([ - lmaoView.widthAnchor.constraint(equalToConstant: 60), - lmaoView.heightAnchor.constraint(equalToConstant: 60), - lmaoView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -80), - lmaoView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - ]) - } - - /// Function to enable or disable the idle timer. - @objc func changeIdleTimerState() { - #if MAIN_APP - UIApplication.shared.isIdleTimerDisabled = utilities.preferences.userInterface.idleTimerDisabled - #endif - } - - /// Function to dynamically update the aspect ratio for ``cameraPreview`` through ``MalachiteSettingsView``. - @objc func changeAspectFill() { - UIView.animate(withDuration: 20) { [self] in - if utilities.preferences.preview.aspect { - cameraPreview?.videoGravity = AVLayerVideoGravity.resizeAspectFill - } else { - cameraPreview?.videoGravity = AVLayerVideoGravity.resizeAspect - } - } - } - - /// Function to dynamically change the auto exposure and ``exposureSlider`` values when toggling in ``MalachiteSettingsView``. - @objc func changeExposureLimit() { - guard let exposure = selectedDevice?.isExposureModeSupported(.continuousAutoExposure) else { return } - do { - try selectedDevice?.lockForConfiguration() - defer { selectedDevice?.unlockForConfiguration() } - if exposure { selectedDevice?.exposureMode = .continuousAutoExposure } - } catch { - utilities.debugNSLog("[Change Exposure Limit] Couldn't lock device for configuration") - } - - UIView.animate(withDuration: 0.5) { - self.exposureSlider.value = 0.0 - } - } - - @objc func changeContinuousAEAF() { - utilities.function.continuousAEAF(device: selectedDevice!) - } - - @objc func changeAEAFRecognizer() { - let tapGestureElements = utilities.preferences.userInterface.tapAndHold - guard let currentGestureRecognizers = self.view.gestureRecognizers else { return } - - if tapGestureElements.contains("off") { - if currentGestureRecognizers.contains(aeafRecognizer) { - utilities.debugNSLog("[AE+AF] Disabling tap and hold gesture") - self.view.removeGestureRecognizer(aeafRecognizer) - } - } else { - if !currentGestureRecognizers.contains(aeafRecognizer) { - utilities.debugNSLog("[AE+AF] Enabling tap and hold gesture") - self.view.addGestureRecognizer(aeafRecognizer) - } - } - } - - /// Function to change the video stabilization mode for the ``cameraPreview``. - @objc func changeStabilizerMode() { - if utilities.preferences.preview.stablize { - if #available(iOS 17.0, *) { - if ((selectedDevice?.activeFormat.isVideoStabilizationModeSupported(.previewOptimized)) != nil) { - utilities.debugNSLog("[Preview Stabilization] Enabling enhanced stabilization mode") - cameraPreview?.connection!.preferredVideoStabilizationMode = .previewOptimized - return - } - } - - if ((selectedDevice?.activeFormat.isVideoStabilizationModeSupported(.standard)) != nil) { - utilities.debugNSLog("[Preview Stabilization] Enabling standard stabilization mode") - cameraPreview?.connection!.preferredVideoStabilizationMode = .standard - } - } else { - cameraPreview?.connection!.preferredVideoStabilizationMode = .off - } - } - - /// Function to change the GameKit enabled state. - @objc func changeGameCenterEnabled() { - DispatchQueue.global(qos: .background).async { [self] in - if utilities.preferences.general.gamekit.enabled { - utilities.games.setupGameCenter() - } - } - } - - /// Function to present ``MalachiteSettingsView`` - @objc func presentSettingsView() { -#if APP_EXTENSION - utilities.debugNSLog("[Settings] Attempt to access Settings UI from app extension") - let alert = UIAlertController(title: "alert.title.app_extensions.settings".localized, message: "alert.detail.app_extensions.settings".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = settingsButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in settingsButton } - } - alert.addAction(UIAlertAction(title: "alert.button.ok".localized, style: .default, handler: { _ in - self.utilities.debugNSLog("[Settings] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - return -#elseif MAIN_APP - var aboutView = MalachiteSettingsView(dismissAction: {self.dismiss( animated: true, completion: nil )}) - aboutView.utilities = self.utilities - let hostingController = UIHostingController(rootView: aboutView) - hostingController.modalPresentationStyle = UIModalPresentationStyle.popover - hostingController.popoverPresentationController?.sourceView = settingsButton - hostingController.isModalInPresentation = true - if #available(iOS 26.0, *) { - hostingController.preferredTransition = .zoom { [self] _ in - settingsButton - } - } - self.present(hostingController, animated: true, completion: nil) -#endif - } - - /// Function to switch cameras and attach new inputs to ``cameraSession``, and set settings based on the `activeFormat` of ``selectedDevice``. - @objc func runInputSwitch() { - cameraSession?.beginConfiguration() - if (self.availableRearCameras.count < 2 || utilities.preferences.debug.breakApp) && !self.initRun { - utilities.debugNSLog("[Camera Input] Only one AVCaptureDevice is available to use, showing error") - let alert = UIAlertController(title: "alert.title.camera_switch".localized, message: "alert.detail.camera_switch".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = cameraButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in cameraButton } - } - alert.addAction(UIAlertAction(title: "alert.button.ok".localized, style: .default, handler: { _ in - self.utilities.debugNSLog("[Camera Input] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - return - } - - UIView.animate(withDuration: 0.5) { - self.focusSlider.value = 0.0 - self.exposureSlider.value = 0.0 - } - - if cameraIndex != nil { selectedDevice = availableRearCameras[cameraIndex!] } else { - if availableRearCameras.count > 1 && !initRun { - if let devicePosition = availableRearCameras.firstIndex(of: selectedDevice!) { - if devicePosition == (availableRearCameras.count - 1) { - selectedDevice = availableRearCameras[0] - } else { - selectedDevice = availableRearCameras[devicePosition + 1] - } - } - } - } - - utilities.function.switchInput(session: &cameraSession!, - cameras: availableRearCameras, - device: &selectedDevice, - output: &photoOutput, - input: &selectedInput, - button: cameraButton, - firstRun: &initRun) - - - if #available(iOS 18.0, *) { addControls() } - - cameraSession?.commitConfiguration() - - DispatchQueue.main.async() { [self] in - utilities.tooltips.zoomTooltipFlow(button: currentCamera, viewForBounds: view, camera: selectedDevice) - } - } - - @available(iOS 18.0, *) - func addControls() { - guard cameraSession!.supportsControls else { return } - - let systemBiasSlider = AVCaptureSystemExposureBiasSlider(device: selectedDevice!) - - let zoomSlider = AVCaptureSlider("Zoom", symbolName: "plus.viewfinder", in: 1.0...Float(MalachiteClassesObject().preferences.capture.maximumZoom)) - zoomSlider.setActionQueue(utilities.sessionQueue) { position in - self.zoomFloater = CGFloat(position) - self.runZoomController() - self.zoomFloater = nil - } - - let focusSlider = AVCaptureSlider("Focus", symbolName: "scope", in: 0.0...1.0) - focusSlider.setActionQueue(utilities.sessionQueue) { position in - self.focusFloater = position - self.runManualFocusController() - self.focusFloater = nil - } - - let cameraSwitcher = AVCaptureIndexPicker("Cameras", symbolName: "camera.fill", localizedIndexTitles: self.availableRearCameras.map { $0.localizedName } ) - cameraSwitcher.selectedIndex = self.availableRearCameras.firstIndex(of: self.selectedDevice!)! - cameraSwitcher.setActionQueue(utilities.sessionQueue) { index in - self.cameraIndex = index - } - - let flashSwitcher = AVCaptureIndexPicker("Flash", symbolName: "bolt.fill", numberOfIndexes: 2, localizedTitleTransform: { index in - switch index { - case 0: return NSLocalizedString("flash.off", comment: "Off") - case 1: return NSLocalizedString("flash.on", comment: "On") - default: return "" - } - }) - flashSwitcher.setActionQueue(utilities.sessionQueue) { index in - flashSwitcher.selectedIndex = self.flashStatus ? 1 : 0 - self.runFlashlightToggle() - flashSwitcher.selectedIndex = self.flashStatus ? 1 : 0 - } - - let flashSlider = AVCaptureSlider("Flash Level", symbolName: "lightbulb.fill", in: 0.0...1.0) - flashSlider.setActionQueue(utilities.sessionQueue) { position in - if (position == 0.0 && self.flashStatus) || (position != 0.0 && !self.flashStatus) { - self.flashFloater = position - self.runFlashlightToggle() - self.flashFloater = nil - flashSwitcher.selectedIndex = self.flashStatus ? 1 : 0 - } else { - self.utilities.function.flashLevelTest(captureDevice: self.selectedDevice!, floater: position) - } - } - - if utilities.versionType == "INTERNAL" { - utilities.function.addControlsToSession(session: &cameraSession!, controls: [ zoomSlider, focusSlider, cameraSwitcher, flashSwitcher, flashSlider, systemBiasSlider]) - } - } - - @objc func runInputMegapixelSwitch() { - utilities.function.switchInputMegapixels(device: selectedDevice!, photoOutput: self.photoOutput) - } - - /// Function to toggle the flashlight's on state. - @objc func runFlashlightToggle() { - // TODO: change to isFlashAvailable. hasFlash doesn't mean it can currently be used - guard let flashlight = selectedDevice?.hasFlash else { return } - if flashlight && !utilities.preferences.debug.breakApp { - utilities.function.toggleFlash(captureDevice: &selectedDevice!, - flashlightButton: flashlightButton, - floater: flashFloater, - isFlashOn: &flashStatus) - } else { - utilities.debugNSLog("[Flashlight] No flashlight available") - let alert = UIAlertController(title: "alert.title.flashlight".localized, message: "alert.detail.flashlight".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = flashlightButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in flashlightButton } - } - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ok", comment: "Default action"), style: .default, handler: { _ in - self.utilities.debugNSLog("[Flashlight] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - } - } - - /// Function to take an image. - @objc func runImageCapture() { - self.captureButton.isEnabled = false - progressIndicator = UIActivityIndicatorView(frame: self.captureButton.frame) - self.view.addSubview(progressIndicator) - self.captureButton.setImage(nil, for: .normal) - progressIndicator.startAnimating() - - let status = PHPhotoLibrary.authorizationStatus(for: .addOnly) - - if (status == .authorized || status == .limited) && !utilities.preferences.debug.breakApp { - self.photoOutput = utilities.function.captureImage(output: self.photoOutput, viewForBounds: self.view, captureDelegate: self) - } else { - utilities.debugNSLog("[Capture Photo] PHPhotoLibrary not authorized, showing error") - let alert = UIAlertController(title: "alert.title.phphotolibrary".localized, message: "alert.detail.phphotolibrary".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = captureButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in captureButton } - } - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ok", comment: "Default action"), style: .default, handler: { _ in - self.utilities.debugNSLog("[Capture Photo] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - } - } - - /// Function for opening ``MalachitePhotoPreview`` and running GameKit commands after photo processing is completed. - func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { - guard let imageData = photo.fileDataRepresentation() else { return } - let getterForOrientation = UIImage(data: imageData) - let previewImage = UIImage(ciImage: CIImage(data: imageData, options: [.applyOrientationProperty: true, - .properties: [kCGImagePropertyOrientation: CGImagePropertyOrientation(getterForOrientation!.imageOrientation).rawValue]])!) - let photoPreview = MalachitePhotoPreview() - photoPreview.photoImageData = imageData - photoPreview.photoImageView.frame = view.frame - photoPreview.photoImage = previewImage - if utilities.versionType == "INTERNAL" && utilities.preferences.preview.fastPath { - photoPreview.savePhoto(finalImage: photoPreview.finalizeImageForExport(imageData: imageData)) - } else { - let navigationController = UINavigationController(rootViewController: photoPreview) - navigationController.modalPresentationStyle = UIModalPresentationStyle.pageSheet - navigationController.isModalInPresentation = true - navigationController.isNavigationBarHidden = true - navigationController.popoverPresentationController?.sourceView = captureButton - if #available(iOS 26.0, *) { - navigationController.preferredTransition = .zoom { [self] _ in captureButton } - } - self.present(navigationController, animated: true, completion: nil) - NotificationCenter.default.addObserver(photoPreview, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil) - } - - self.captureButton.isEnabled = true - self.captureButton.setImage(UIImage(systemName: "camera.aperture"), for: .normal) - progressIndicator.stopAnimating() - - DispatchQueue.global(qos: .background).async { [self] in - utilities.preferences.ext.runPhotoCounter() - if utilities.games.gameCenterEnabled { - let numPhotos = utilities.preferences.general.photoCount - if numPhotos == 1 { - let firstPhoto = utilities.games.achievements.pullAchievement(achievementName: "first_photo") - firstPhoto.percentComplete = 100 - utilities.games.achievements.pushAchievement(achievementBody: firstPhoto) - } - utilities.games.leaderboards.pushLeaderboard(scoreToSubmit: numPhotos, leaderboardToSubmit: "photos_taken") - } - } - } - - /// Function to zoom in and out with ``zoomRecognizer``. - @objc func runZoomController() { - utilities.function.zoom(sender: zoomRecognizer, - floater: zoomFloater ?? zoomRecognizer.scale, - captureDevice: &selectedDevice!, - lastZoomFactor: &lastZoomFactor, - hapticClass: utilities.haptics) - } - - /// Function to autofocus + autoexposure with ``aeafRecognizer``. - @objc func runaeafController() { - utilities.function.pointOfInterestAEAF(sender: aeafRecognizer, - captureDevice: &selectedDevice!, - button: aeafFeedback, - viewForScale: self.view, - hapticClass: utilities.haptics) - } - - /// Function to handle ``exposureSlider`` interaction. - @objc func runManualExposureController() { - guard let exposure = selectedDevice?.isExposureModeSupported(.custom) else { return } - if exposure && !utilities.preferences.debug.breakApp { - utilities.function.manualExposure(captureDevice: &selectedDevice!, - sender: exposureSlider) - } else { - utilities.debugNSLog("[Manual Exposure] Current camera is not capable of adjusting exposure") - let alert = UIAlertController(title: "alert.title.exposure".localized, message: "alert.detail.exposure".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = exposureButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in exposureButton } - } - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ok", comment: "Default action"), style: .default, handler: { _ in - self.utilities.debugNSLog("[Manual Exposure] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - } - } - - /// Function to show and hide the ``exposureSliderButton`` and ``exposureLockButton``. - @objc func runManualExposureUIHider() { - guard let exposure = selectedDevice?.isExposureModeSupported(.custom) else { return } - if exposure && !utilities.preferences.debug.breakApp { - manualExposureSliderIsActive = utilities.views.runSliderControllers(sliderIsShown: manualExposureSliderIsActive, - optionButton: exposureButton, - lockButton: exposureLockButton, - associatedSliderButton: exposureSliderButton) - } else { - utilities.debugNSLog("[Manual Focus] Current camera is not capable of adjusting exposure") - let alert = UIAlertController(title: "alert.title.exposure".localized, message: "alert.detail.exposure".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = exposureButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in exposureButton } - } - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ok", comment: "Default action"), style: .default, handler: { _ in - self.utilities.debugNSLog("[Manual Exposure] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - } - } - - @objc func runManualExposureUIHiderWhenUnsupported() { - if manualExposureSliderIsActive { - manualExposureSliderIsActive = utilities.views.runSliderControllers(sliderIsShown: manualExposureSliderIsActive, - optionButton: exposureButton, - lockButton: exposureLockButton, - associatedSliderButton: exposureSliderButton) - } else { - manualExposureSliderIsActive = utilities.views.runSliderControllers(sliderIsShown: true, - optionButton: exposureButton, - lockButton: exposureLockButton, - associatedSliderButton: exposureSliderButton) - } - } - - /// Function to lock and unlock the ``exposureSlider``. - @objc func runManualExposureLockController() { - manualExposureLockIsActive = utilities.views.runLockControllers(lockIsActive: manualExposureLockIsActive, - lockButton: &exposureLockButton, - associatedSlider: &exposureSlider, - associatedGestureRecognizer: nil, - viewForRecognizers: self.view) - } - - /// Function to handle ``focusSlider`` interaction. - @objc func runManualFocusController() { - guard let focus = selectedDevice?.isLockingFocusWithCustomLensPositionSupported else { return } - if focus && !utilities.preferences.debug.breakApp { - utilities.function.manualFocus(captureDevice: &selectedDevice!, - sender: focusSlider, - floater: focusFloater ?? focusSlider.value) - } else { - utilities.debugNSLog("[Manual Focus] Current camera is not capable of adjusting focus") - let alert = UIAlertController(title: "alert.title.focus".localized, message: "alert.detail.focus".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = focusButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in focusButton } - } - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ok", comment: "Default action"), style: .default, handler: { _ in - self.utilities.debugNSLog("[Manual Focus] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - } - } - - /// Function to handle ``focusSlider`` interaction. - @objc func runManualFocusUIHider() { - guard let focus = selectedDevice?.isLockingFocusWithCustomLensPositionSupported else { return } - if focus && !utilities.preferences.debug.breakApp { - manualFocusSliderIsActive = utilities.views.runSliderControllers(sliderIsShown: manualFocusSliderIsActive, - optionButton: focusButton, - lockButton: focusLockButton, - associatedSliderButton: focusSliderButton) - } else { - utilities.debugNSLog("[Manual Focus] Current camera is not capable of adjusting focus") - let alert = UIAlertController(title: "alert.title.focus".localized, message: "alert.detail.focus".localized, preferredStyle: .actionSheet) - alert.popoverPresentationController?.sourceView = focusButton - if #available(iOS 26.0, *) { - alert.preferredTransition = .zoom { [self] _ in focusButton } - } - alert.addAction(UIAlertAction(title: NSLocalizedString("alert.button.ok", comment: "Default action"), style: .default, handler: { _ in - self.utilities.debugNSLog("[Manual Focus] Dialog has been dismissed") - })) - self.present(alert, animated: true, completion: nil) - } - } - - @objc func runManualFocusUIHiderWhenUnsupported() { - if manualFocusSliderIsActive { - manualFocusSliderIsActive = utilities.views.runSliderControllers(sliderIsShown: manualFocusSliderIsActive, - optionButton: focusButton, - lockButton: focusLockButton, - associatedSliderButton: focusSliderButton) - } else { - manualFocusSliderIsActive = utilities.views.runSliderControllers(sliderIsShown: true, - optionButton: focusButton, - lockButton: focusLockButton, - associatedSliderButton: focusSliderButton) - } - } - - /// Function to show and hide the ``focusSliderButton`` and ``focusLockButton``. - @objc func runManualFocusLockController() { - manualFocusLockIsActive = utilities.views.runLockControllers(lockIsActive: manualFocusLockIsActive, - lockButton: &focusLockButton, - associatedSlider: &focusSlider, - associatedGestureRecognizer: aeafRecognizer, - viewForRecognizers: self.view) - } - - @objc func updateSettingsGestureFingerCount() { - settingsRecognizer.numberOfTouchesRequired = utilities.preferences.evaintrnl.settingsGesture - } - - @objc func runSettingsGesture() { - if settingsRecognizer.state == UIGestureRecognizer.State.ended { - self.presentSettingsView() - } - } - - /// Function to show and hide the user interface that was drawn with ``setupView()``. - @objc func runUIHider() { - if uiHiderRecognizer.state == UITapGestureRecognizer.State.ended || uiHiderRecognizer.state == UITapGestureRecognizer.State.changed { return } - - let gestureRecognizers = [ zoomRecognizer, aeafRecognizer ] - - DispatchQueue.main.async { [self] in - if !uiIsHidden { - hideUI() - } else { - showUI() - utilities.tooltips.zoomTooltipFlow(button: currentCamera, viewForBounds: self.view, camera: selectedDevice) - } - - uiIsHidden = !uiIsHidden - utilities.haptics.triggerNotificationHaptic(type: .success) - } - - func hideUI() { - UIView.animate(withDuration: 0.25) { [self] in - for subview in self.view.subviews { - if subview != cameraView { - if subview == focusLockButton { - if manualFocusSliderIsActive { subview.alpha = 0.0 } - } else if subview == exposureLockButton { - if manualExposureSliderIsActive { subview.alpha = 0.0 } - } else { - subview.alpha = 0.0 - } - } - } - } - let hiddenRecognizers = utilities.preferences.userInterface.hiddenControls - for gestureRecognizer in gestureRecognizers { - if gestureRecognizer == zoomRecognizer && !hiddenRecognizers.contains("zoom") { self.view.removeGestureRecognizer(gestureRecognizer) } - if gestureRecognizer == aeafRecognizer && !hiddenRecognizers.contains("tah") { self.view.removeGestureRecognizer(gestureRecognizer) } - } - } - - func showUI() { - UIView.animate(withDuration: 0.25) { [self] in - for subview in self.view.subviews { - if subview != cameraView { - if subview == focusLockButton { - if manualFocusSliderIsActive { subview.alpha = 1.0 } - } else if subview == exposureLockButton { - if manualExposureSliderIsActive { subview.alpha = 1.0 } - } else { - subview.alpha = 1.0 - } - } - } - } - - for gestureRecognizer in gestureRecognizers { - guard let currentRecognizers = self.view.gestureRecognizers else { return } - if !currentRecognizers.contains(gestureRecognizer) { - self.view.addGestureRecognizer(gestureRecognizer) - } - } - } - } - - /// Function to handle device rotation. - @objc func orientationChanged() { - utilities.views.rotateButtonsWithOrientation(buttonsToRotate: [ cameraButton, - flashlightButton, - captureButton, - settingsButton, - focusButton, - focusLockButton, - exposureButton, - exposureLockButton ]) - } - - /// Override function to force the status bar to never be shown. - override var prefersStatusBarHidden: Bool { - return true - } - - /// Override function to force the app to be in portrait mode on iPhone. - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - if utilities.idiom == .phone { - return .portrait - } - - return .all - } - - /// Override function to force the system to reject gestures from the bottom of the screen. - override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge { - return [.bottom] - } - - /// Override function for layoutSubviews. - override func viewWillLayoutSubviews() { - super.viewWillLayoutSubviews() - cameraView.center = CGPoint(x: cameraView.bounds.midX, y: cameraView.bounds.midY) - cameraView.frame = self.view.bounds - } - - /// Override function to trigger actions when the screen rotates. - override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { - super.viewWillTransition(to: size, with: coordinator) - - coordinator.animate(alongsideTransition: { [self] context in - #if MAIN_APP - if let windowScene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene { - let orientation = windowScene.interfaceOrientation - self.cameraPreview?.connection!.videoOrientation = self.transformOrientation(orientation: orientation) - } - #endif - self.cameraPreview?.frame.size = self.view.frame.size - }) - } -} - diff --git a/MalachiteWatch/ContentView.swift b/MalachiteWatch/ContentView.swift deleted file mode 100644 index 7233ba0..0000000 --- a/MalachiteWatch/ContentView.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// ContentView.swift -// MalachiteWatch Watch App -// -// Created by Eva Isabella Luna on 7/24/25. -// - -import SwiftUI - -struct ContentView: View { - var body: some View { - VStack { - if #available(watchOS 8.0, *) { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") - } else { - Image(systemName: "globe") - .imageScale(.large) - .foregroundColor(.accentColor) - Text("Hello, world!") - } - } - .padding() - } -} - -#Preview { - ContentView() -} diff --git a/PRIVACY_POLICY.md b/PRIVACY_POLICY.md index 543edff..5eac8b6 100755 --- a/PRIVACY_POLICY.md +++ b/PRIVACY_POLICY.md @@ -1,5 +1,5 @@ # PRIVACY POLICY -This privacy policy will reference of the following terms: Malachite (the "App"), Adam Tunnicliff (the "Developer"), and the end-user interacting with the App (the "User"). +This privacy policy will reference of the following terms: mlchtCamera (the "App"), Adam Tunnicliff (the "Developer"), and the end-user interacting with the App (the "User"). 1.0: The App does not collect any data, and contains no functionality to do so. @@ -7,4 +7,4 @@ This privacy policy will reference of the following terms: Malachite (the "App") Some of this Apple Inc.-collected information is accessible to Developer for debugging and statistics purposes only, and it cannot be used to identify any user. This information is also not shared with any other entity. -Privacy policy updated on **January 2nd, 2024**. +Privacy policy updated on **January 21st, 2026**. diff --git a/README.md b/README.md index c337c5f..61520c9 100755 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ -# Malachite -*noun* -1. a crystal with the properties of revealing hidden parts of yourself -2. the name of my app to take control of the macro lens on iPhone +# mlchtCamera + +*noun*; the name of my app to take back control of your iPhone or iPad's camera lenses. + --- ## What is it? -Malachite is a **work-in-progress** app that gives you more control over the macro lens of (or connected to) your iPhone. +With a name loosely based on the **malachite crystal** with properties of revealing hidden parts of yourself, mlchtCamera is a **work-in-progress** camera app, designed to put pro controls into the hands of even more users. ## What do I need?[^1] +mlchtCamera will run on any iPhone or iPad with **iOS 15.0** or later. + I recommend one of the following configurations... -- An iPhone or iPad with one of the following: +- An iPhone or iPad with one of the following... - A built-in ultra-wide camera that supports Apple's macro mode: - iPhone 13 Pro or iPhone 13 Pro Max - iPhone 14 Pro or iPhone 14 Pro Max - iPhone 15 Pro or iPhone 15 Pro Max - iPhone 16 or later (excluding iPhone 16e) - - A third-party lens attachment -...and **iOS 15.0** or later. +- ...and/or a third-party lens attachment ## What can I do with this? - [x] Enjoy a fully-native, no-external library Swift app @@ -42,12 +43,22 @@ Malachite is on TestFlight, but only for **[my Patrons](https://patreon.com/crys ### Building from source 1. Clone this repo 2. Open `Codesigning.example.xcconfig`, make changes, and save it as `Codesigning.xcconfig`. -3. Open `Malachite.xcodeproj` +3. Open `mlchtCamera.xcodeproj` 4. Build! ## What started this one, Eva? -So, I live with my love: @ThatStella7922. She and I are both big nerds, and I caught her using her macro lens on an Xbox 360 motherboard to let her work with traces and pads while she was RGH'ing it. The idea for a magnifier app came from how much time it took for her to get into the right camera setting, turn the flashlight on, and still not have much control beyond autofocus and zooming. With Malachite, I strove to solve this problem - and then I ended up making it even greater for the people who love macro photography on their own iPhones. +So, I live with my love: @ThatStella7922. She and I are both big nerds, and I caught her using her macro lens on an Xbox 360 motherboard to let her work with traces and pads while she was RGH'ing it. The idea for a magnifier app came from how much time it took for her to get into the right camera setting, turn the flashlight on, and still not have much control beyond autofocus and zooming. With Malachite, I strove to solve this problem - and thus, we had Malachite with its original purpose: a macro magnifier. + +As I was working on it, I'd drop builds into my Discord server. A few users came in and asked for various features - including image capture. I was originally opposed to it, since it *was* just for magnification... and yet, a few hours later, I'd hooked everything up to add image capture support - saving HEICs to the user's library or directly out of the share sheet. mlchtCamera ended up morphing into a macro photography app that people used and enjoyed - and requested more out of. + +At this point, I've added plenty of extras. Camera switching, manual exposure, hardware button controls - those are just a few and I plan to implement so much more in the future. It took me a while to accept it, but this little side-project of mine was becoming something different, and the goal solidified itself as this: creating a powerful pro camera app that truly harnesses iPhone and iPad hardware, while staying simplistic in its design and accessible to anyone who wants to get into photography. + +To the people who helped get me here (and you know who you are), I thank you for helping me figure it out. + +### Addendum + +This app was originally named "Malachite" - directly after the crystal mentioned before. However, bouncing between Apple Developer accounts resulted in issues with the name and bundle identifiers used in the past. The rename to "mlchtCamera" keeps the core name while also making it unique. -[^1]: Malachite is validated against iPhone SE (1st generation) with no lens attachment, iPhone 8 Plus with no working main camera, iPhone 11, iPhone 16 Pro Max, and iPad Pro (11-inch). Not all features are available across all devices, due to hardware and software limitations. iOS version support may change depending on the difficulty of targeting older iOS versions and/or other factors. +[^1]: mlcht is validated against iPhone SE (1st generation) with no lens attachment, iPhone 8 Plus with no working main camera, iPhone 11, iPhone 17 Pro Max, and iPad Pro (11-inch). Not all features are available across all devices, due to hardware and software limitations. iOS version support may change depending on the difficulty of targeting older iOS versions and/or other factors. [^2]: Pinch-to-zoom will feature haptic feedback when reaching the minimum and maximum zoom levels in a future commit. [^3]: Capturing images in RAW and ProRAW is being looked into. HEIC requires iPhone 7 or later, iPad (6th generation) or later, iPad Air (3rd generation) or later, iPad mini (5th generation) or later, iPad Pro (12.9-inch, 2nd generation) or later, iPad Pro (10.5-inch), iPad Pro (11-inch) or later, or iPod touch (7th generation) diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh index 4d795e1..de8d3ff 100755 --- a/ci_scripts/ci_post_clone.sh +++ b/ci_scripts/ci_post_clone.sh @@ -1,9 +1,9 @@ #!/bin/zsh # ci_post_xcodebuild.sh -# Malachite +# mlchtCamera # # Created by Eva Isabella Lunaon 5/13/25. # -mv $CI_PRIMARY_REPOSITORY_PATH/Malachite/Codesigning.example.xcconfig $CI_PRIMARY_REPOSITORY_PATH/Malachite/Codesigning.xcconfig +mv $CI_PRIMARY_REPOSITORY_PATH/mlchtCamera/Codesigning.example.xcconfig $CI_PRIMARY_REPOSITORY_PATH/mlchtCamera/Codesigning.xcconfig diff --git a/ci_scripts/ci_post_xcodebuild.sh b/ci_scripts/ci_post_xcodebuild.sh index 836d0f1..86ef2a8 100755 --- a/ci_scripts/ci_post_xcodebuild.sh +++ b/ci_scripts/ci_post_xcodebuild.sh @@ -1,7 +1,7 @@ #!/bin/zsh # ci_post_xcodebuild.sh -# Malachite +# mlchtCamera # # Created by Stella Luna on 1/9/24. # diff --git a/mlchtCamera.xcodeproj/project.pbxproj b/mlchtCamera.xcodeproj/project.pbxproj new file mode 100755 index 0000000..bfe53af --- /dev/null +++ b/mlchtCamera.xcodeproj/project.pbxproj @@ -0,0 +1,2354 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 90; + objects = { + +/* Begin PBXBuildFile section */ + 78145F782E65135C003AEE51 /* Init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78145F772E651357003AEE51 /* Init.swift */; }; + 78145F792E65135C003AEE51 /* Init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78145F772E651357003AEE51 /* Init.swift */; }; + 78145F7A2E65135C003AEE51 /* Init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78145F772E651357003AEE51 /* Init.swift */; }; + 7818E23C2E80971C0046F621 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7818E23B2E8097180046F621 /* Compatibility.swift */; }; + 7818E23D2E80971C0046F621 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7818E23B2E8097180046F621 /* Compatibility.swift */; }; + 7818E23E2E80971C0046F621 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7818E23B2E8097180046F621 /* Compatibility.swift */; }; + 7824CAED2E62CDDE0072870F /* CameraView+Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7824CAEC2E62CDD90072870F /* CameraView+Overrides.swift */; }; + 7824CAEE2E62CDDE0072870F /* CameraView+Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7824CAEC2E62CDD90072870F /* CameraView+Overrides.swift */; }; + 7824CAEF2E62CDDE0072870F /* CameraView+Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7824CAEC2E62CDD90072870F /* CameraView+Overrides.swift */; }; + 7824CAF12E62E4970072870F /* Camera+Bringup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7824CAF02E62E4920072870F /* Camera+Bringup.swift */; }; + 7824CAF22E62E4970072870F /* Camera+Bringup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7824CAF02E62E4920072870F /* Camera+Bringup.swift */; }; + 7824CAF32E62E4970072870F /* Camera+Bringup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7824CAF02E62E4920072870F /* Camera+Bringup.swift */; }; + 782850012E80C05100826FA7 /* Camera+Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782850002E80C04D00826FA7 /* Camera+Permissions.swift */; }; + 782850022E80C05100826FA7 /* Camera+Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782850002E80C04D00826FA7 /* Camera+Permissions.swift */; }; + 782850032E80C05100826FA7 /* Camera+Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782850002E80C04D00826FA7 /* Camera+Permissions.swift */; }; + 7837C10B2E34C45B009396B0 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5D2B99AE72005E10FA /* SwiftUI.framework */; }; + 7837C10C2E34C45B009396B0 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5B2B99AE72005E10FA /* WidgetKit.framework */; }; + 7837C10E2E34C45B009396B0 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; + 7837C1172E34C47D009396B0 /* mlchtWidgetBundleWatch.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7837C1152E34C45B009396B0 /* mlchtWidgetBundleWatch.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 7847D5032E8F26EC006759A6 /* View+Sliders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7847D5022E8F26E9006759A6 /* View+Sliders.swift */; }; + 7847D5042E8F26EC006759A6 /* View+Sliders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7847D5022E8F26E9006759A6 /* View+Sliders.swift */; }; + 7847D5052E8F26EC006759A6 /* View+Sliders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7847D5022E8F26E9006759A6 /* View+Sliders.swift */; }; + 784EDDD42E5EE52600DFF370 /* PhotoPreviewView+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784EDDD32E5EE52100DFF370 /* PhotoPreviewView+Controls.swift */; }; + 784EDDD52E5EE52600DFF370 /* PhotoPreviewView+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784EDDD32E5EE52100DFF370 /* PhotoPreviewView+Controls.swift */; }; + 784EDDD62E5EE52600DFF370 /* PhotoPreviewView+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784EDDD32E5EE52100DFF370 /* PhotoPreviewView+Controls.swift */; }; + 785F084D2B12D41100244EB4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F084C2B12D41100244EB4 /* AppDelegate.swift */; }; + 785F084F2B12D41100244EB4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785F084E2B12D41100244EB4 /* SceneDelegate.swift */; }; + 7863E4E32F695699008566AE /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7863E4E22F695697008566AE /* Location.swift */; }; + 7863E4E42F695699008566AE /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7863E4E22F695697008566AE /* Location.swift */; }; + 7863E4E52F695699008566AE /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7863E4E22F695697008566AE /* Location.swift */; }; + 7866F5CE2E6299A4009AC9BF /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5CD2E6299A0009AC9BF /* Camera.swift */; }; + 7866F5CF2E6299A4009AC9BF /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5CD2E6299A0009AC9BF /* Camera.swift */; }; + 7866F5D02E6299A4009AC9BF /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5CD2E6299A0009AC9BF /* Camera.swift */; }; + 7866F5D22E62A596009AC9BF /* CameraView+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5D12E62A591009AC9BF /* CameraView+Notifications.swift */; }; + 7866F5D32E62A596009AC9BF /* CameraView+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5D12E62A591009AC9BF /* CameraView+Notifications.swift */; }; + 7866F5D42E62A596009AC9BF /* CameraView+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5D12E62A591009AC9BF /* CameraView+Notifications.swift */; }; + 7866F5D62E62A5FA009AC9BF /* temputils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5D52E62A5F7009AC9BF /* temputils.swift */; }; + 7866F5D72E62A5FA009AC9BF /* temputils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5D52E62A5F7009AC9BF /* temputils.swift */; }; + 7866F5D82E62A5FA009AC9BF /* temputils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5D52E62A5F7009AC9BF /* temputils.swift */; }; + 7866F5DB2E62ADB1009AC9BF /* Game+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5DA2E62ADAB009AC9BF /* Game+View.swift */; }; + 7866F5DC2E62ADB1009AC9BF /* Game+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5DA2E62ADAB009AC9BF /* Game+View.swift */; }; + 7866F5DD2E62ADB1009AC9BF /* Game+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7866F5DA2E62ADAB009AC9BF /* Game+View.swift */; }; + 786DAE232E4F28A600BE3137 /* DeveloperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE172E4F28A600BE3137 /* DeveloperView.swift */; }; + 786DAE242E4F28A600BE3137 /* DeveloperView+BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE182E4F28A600BE3137 /* DeveloperView+BuildInfo.swift */; }; + 786DAE252E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE192E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift */; }; + 786DAE262E4F28A600BE3137 /* DeveloperView+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1A2E4F28A600BE3137 /* DeveloperView+Settings.swift */; }; + 786DAE272E4F28A600BE3137 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1C2E4F28A600BE3137 /* AboutView.swift */; }; + 786DAE282E4F28A600BE3137 /* CompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1D2E4F28A600BE3137 /* CompatibilityView.swift */; }; + 786DAE292E4F28A600BE3137 /* PhotoPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1E2E4F28A600BE3137 /* PhotoPreviewView.swift */; }; + 786DAE2A2E4F28A600BE3137 /* QuickHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1F2E4F28A600BE3137 /* QuickHelpView.swift */; }; + 786DAE2B2E4F28A600BE3137 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE202E4F28A600BE3137 /* SettingsView.swift */; }; + 786DAE2C2E4F28A600BE3137 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE212E4F28A600BE3137 /* CameraView.swift */; }; + 786DAE2D2E4F28A600BE3137 /* DeveloperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE172E4F28A600BE3137 /* DeveloperView.swift */; }; + 786DAE2E2E4F28A600BE3137 /* DeveloperView+BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE182E4F28A600BE3137 /* DeveloperView+BuildInfo.swift */; }; + 786DAE2F2E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE192E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift */; }; + 786DAE302E4F28A600BE3137 /* DeveloperView+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1A2E4F28A600BE3137 /* DeveloperView+Settings.swift */; }; + 786DAE312E4F28A600BE3137 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1C2E4F28A600BE3137 /* AboutView.swift */; }; + 786DAE322E4F28A600BE3137 /* CompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1D2E4F28A600BE3137 /* CompatibilityView.swift */; }; + 786DAE332E4F28A600BE3137 /* PhotoPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1E2E4F28A600BE3137 /* PhotoPreviewView.swift */; }; + 786DAE342E4F28A600BE3137 /* QuickHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1F2E4F28A600BE3137 /* QuickHelpView.swift */; }; + 786DAE352E4F28A600BE3137 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE202E4F28A600BE3137 /* SettingsView.swift */; }; + 786DAE362E4F28A600BE3137 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE212E4F28A600BE3137 /* CameraView.swift */; }; + 786DAE372E4F28A600BE3137 /* DeveloperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE172E4F28A600BE3137 /* DeveloperView.swift */; }; + 786DAE382E4F28A600BE3137 /* DeveloperView+BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE182E4F28A600BE3137 /* DeveloperView+BuildInfo.swift */; }; + 786DAE392E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE192E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift */; }; + 786DAE3A2E4F28A600BE3137 /* DeveloperView+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1A2E4F28A600BE3137 /* DeveloperView+Settings.swift */; }; + 786DAE3B2E4F28A600BE3137 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1C2E4F28A600BE3137 /* AboutView.swift */; }; + 786DAE3C2E4F28A600BE3137 /* CompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1D2E4F28A600BE3137 /* CompatibilityView.swift */; }; + 786DAE3D2E4F28A600BE3137 /* PhotoPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1E2E4F28A600BE3137 /* PhotoPreviewView.swift */; }; + 786DAE3E2E4F28A600BE3137 /* QuickHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE1F2E4F28A600BE3137 /* QuickHelpView.swift */; }; + 786DAE3F2E4F28A600BE3137 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE202E4F28A600BE3137 /* SettingsView.swift */; }; + 786DAE402E4F28A600BE3137 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE212E4F28A600BE3137 /* CameraView.swift */; }; + 786DAE4C2E4F28B100BE3137 /* MalachitePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE412E4F28B100BE3137 /* MalachitePreferences.swift */; }; + 786DAE4D2E4F28B100BE3137 /* MalachitePreferencesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE422E4F28B100BE3137 /* MalachitePreferencesUtils.swift */; }; + 786DAE4E2E4F28B100BE3137 /* MalachiteFunctionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE442E4F28B100BE3137 /* MalachiteFunctionUtils.swift */; }; + 786DAE4F2E4F28B100BE3137 /* MalachiteGameUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE452E4F28B100BE3137 /* MalachiteGameUtils.swift */; }; + 786DAE502E4F28B100BE3137 /* MalachiteHapticUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE462E4F28B100BE3137 /* MalachiteHapticUtils.swift */; }; + 786DAE512E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE472E4F28B100BE3137 /* MalachiteIntentUtils.swift */; }; + 786DAE522E4F28B100BE3137 /* MalachiteTooltipUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE482E4F28B100BE3137 /* MalachiteTooltipUtils.swift */; }; + 786DAE532E4F28B100BE3137 /* MalachiteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE492E4F28B100BE3137 /* MalachiteUtils.swift */; }; + 786DAE542E4F28B100BE3137 /* MalachiteViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE4A2E4F28B100BE3137 /* MalachiteViewUtils.swift */; }; + 786DAE552E4F28B100BE3137 /* MalachitePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE412E4F28B100BE3137 /* MalachitePreferences.swift */; }; + 786DAE562E4F28B100BE3137 /* MalachitePreferencesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE422E4F28B100BE3137 /* MalachitePreferencesUtils.swift */; }; + 786DAE572E4F28B100BE3137 /* MalachiteFunctionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE442E4F28B100BE3137 /* MalachiteFunctionUtils.swift */; }; + 786DAE582E4F28B100BE3137 /* MalachiteGameUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE452E4F28B100BE3137 /* MalachiteGameUtils.swift */; }; + 786DAE592E4F28B100BE3137 /* MalachiteHapticUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE462E4F28B100BE3137 /* MalachiteHapticUtils.swift */; }; + 786DAE5A2E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE472E4F28B100BE3137 /* MalachiteIntentUtils.swift */; }; + 786DAE5B2E4F28B100BE3137 /* MalachiteTooltipUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE482E4F28B100BE3137 /* MalachiteTooltipUtils.swift */; }; + 786DAE5C2E4F28B100BE3137 /* MalachiteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE492E4F28B100BE3137 /* MalachiteUtils.swift */; }; + 786DAE5D2E4F28B100BE3137 /* MalachiteViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE4A2E4F28B100BE3137 /* MalachiteViewUtils.swift */; }; + 786DAE5E2E4F28B100BE3137 /* MalachitePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE412E4F28B100BE3137 /* MalachitePreferences.swift */; }; + 786DAE5F2E4F28B100BE3137 /* MalachitePreferencesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE422E4F28B100BE3137 /* MalachitePreferencesUtils.swift */; }; + 786DAE602E4F28B100BE3137 /* MalachiteFunctionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE442E4F28B100BE3137 /* MalachiteFunctionUtils.swift */; }; + 786DAE612E4F28B100BE3137 /* MalachiteGameUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE452E4F28B100BE3137 /* MalachiteGameUtils.swift */; }; + 786DAE622E4F28B100BE3137 /* MalachiteHapticUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE462E4F28B100BE3137 /* MalachiteHapticUtils.swift */; }; + 786DAE632E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE472E4F28B100BE3137 /* MalachiteIntentUtils.swift */; }; + 786DAE642E4F28B100BE3137 /* MalachiteTooltipUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE482E4F28B100BE3137 /* MalachiteTooltipUtils.swift */; }; + 786DAE652E4F28B100BE3137 /* MalachiteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE492E4F28B100BE3137 /* MalachiteUtils.swift */; }; + 786DAE662E4F28B100BE3137 /* MalachiteViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE4A2E4F28B100BE3137 /* MalachiteViewUtils.swift */; }; + 786DAE672E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE472E4F28B100BE3137 /* MalachiteIntentUtils.swift */; }; + 786DAE6D2E4F28B700BE3137 /* ControlCenterWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE682E4F28B700BE3137 /* ControlCenterWidget.swift */; }; + 786DAE6E2E4F28B700BE3137 /* LockScreenWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE6A2E4F28B700BE3137 /* LockScreenWidget.swift */; }; + 786DAE6F2E4F28B700BE3137 /* MalachiteWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE6B2E4F28B700BE3137 /* MalachiteWidgetBundle.swift */; }; + 786DAE712E4F28B700BE3137 /* ControlCenterWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE682E4F28B700BE3137 /* ControlCenterWidget.swift */; }; + 786DAE722E4F28B700BE3137 /* LockScreenWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE6A2E4F28B700BE3137 /* LockScreenWidget.swift */; }; + 786DAE732E4F28B700BE3137 /* MalachiteWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE6B2E4F28B700BE3137 /* MalachiteWidgetBundle.swift */; }; + 786DAE782E4F28B900BE3137 /* MalachiteCaptureBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE762E4F28B900BE3137 /* MalachiteCaptureBundle.swift */; }; + 786DAE7E2E4F28BF00BE3137 /* mlchtRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786DAE7B2E4F28BF00BE3137 /* mlchtRemote.swift */; }; + 78729C562E7368F3001027E9 /* Camera+Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78729C552E7368F0001027E9 /* Camera+Input.swift */; }; + 78729C572E7368F3001027E9 /* Camera+Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78729C552E7368F0001027E9 /* Camera+Input.swift */; }; + 78729C582E7368F3001027E9 /* Camera+Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78729C552E7368F0001027E9 /* Camera+Input.swift */; }; + 787B1CAA2B8B095E000AFECC /* mlchtCamera.docc in Sources */ = {isa = PBXBuildFile; fileRef = 787B1CA92B8B095E000AFECC /* mlchtCamera.docc */; }; + 788262602E513080000085AC /* SettingsView+About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882625F2E51307B000085AC /* SettingsView+About.swift */; }; + 788262612E513080000085AC /* SettingsView+About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882625F2E51307B000085AC /* SettingsView+About.swift */; }; + 788262622E513080000085AC /* SettingsView+About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882625F2E51307B000085AC /* SettingsView+About.swift */; }; + 788262642E51309C000085AC /* SettingsView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262632E513094000085AC /* SettingsView+Preview.swift */; }; + 788262652E51309C000085AC /* SettingsView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262632E513094000085AC /* SettingsView+Preview.swift */; }; + 788262662E51309C000085AC /* SettingsView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262632E513094000085AC /* SettingsView+Preview.swift */; }; + 7882626C2E5130C1000085AC /* SettingsView+Resolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882626B2E5130BB000085AC /* SettingsView+Resolution.swift */; }; + 7882626D2E5130C1000085AC /* SettingsView+Resolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882626B2E5130BB000085AC /* SettingsView+Resolution.swift */; }; + 7882626E2E5130C1000085AC /* SettingsView+Resolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882626B2E5130BB000085AC /* SettingsView+Resolution.swift */; }; + 788262702E5130CA000085AC /* SettingsView+Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882626F2E5130C6000085AC /* SettingsView+Photo.swift */; }; + 788262712E5130CA000085AC /* SettingsView+Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882626F2E5130C6000085AC /* SettingsView+Photo.swift */; }; + 788262722E5130CA000085AC /* SettingsView+Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882626F2E5130C6000085AC /* SettingsView+Photo.swift */; }; + 788262742E5130D4000085AC /* SettingsView+Watermarking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262732E5130CF000085AC /* SettingsView+Watermarking.swift */; }; + 788262752E5130D4000085AC /* SettingsView+Watermarking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262732E5130CF000085AC /* SettingsView+Watermarking.swift */; }; + 788262762E5130D4000085AC /* SettingsView+Watermarking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262732E5130CF000085AC /* SettingsView+Watermarking.swift */; }; + 788262782E5130DB000085AC /* SettingsView+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262772E5130D8000085AC /* SettingsView+UI.swift */; }; + 788262792E5130DB000085AC /* SettingsView+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262772E5130D8000085AC /* SettingsView+UI.swift */; }; + 7882627A2E5130DB000085AC /* SettingsView+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262772E5130D8000085AC /* SettingsView+UI.swift */; }; + 7882627C2E514750000085AC /* DeveloperView+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882627B2E514749000085AC /* DeveloperView+Internal.swift */; }; + 7882627D2E514750000085AC /* DeveloperView+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882627B2E514749000085AC /* DeveloperView+Internal.swift */; }; + 7882627E2E514750000085AC /* DeveloperView+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882627B2E514749000085AC /* DeveloperView+Internal.swift */; }; + 788262852E514F12000085AC /* QuickHelpView+About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262842E514F0C000085AC /* QuickHelpView+About.swift */; }; + 788262862E514F12000085AC /* QuickHelpView+About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262842E514F0C000085AC /* QuickHelpView+About.swift */; }; + 788262872E514F12000085AC /* QuickHelpView+About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262842E514F0C000085AC /* QuickHelpView+About.swift */; }; + 788262892E514F18000085AC /* QuickHelpView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262882E514F14000085AC /* QuickHelpView+Preview.swift */; }; + 7882628A2E514F18000085AC /* QuickHelpView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262882E514F14000085AC /* QuickHelpView+Preview.swift */; }; + 7882628B2E514F18000085AC /* QuickHelpView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262882E514F14000085AC /* QuickHelpView+Preview.swift */; }; + 7882628D2E514F25000085AC /* QuickHelpView+Resolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882628C2E514F1F000085AC /* QuickHelpView+Resolution.swift */; }; + 7882628E2E514F25000085AC /* QuickHelpView+Resolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882628C2E514F1F000085AC /* QuickHelpView+Resolution.swift */; }; + 7882628F2E514F25000085AC /* QuickHelpView+Resolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7882628C2E514F1F000085AC /* QuickHelpView+Resolution.swift */; }; + 788262912E514F2D000085AC /* QuickHelpView+Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262902E514F28000085AC /* QuickHelpView+Photo.swift */; }; + 788262922E514F2D000085AC /* QuickHelpView+Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262902E514F28000085AC /* QuickHelpView+Photo.swift */; }; + 788262932E514F2D000085AC /* QuickHelpView+Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262902E514F28000085AC /* QuickHelpView+Photo.swift */; }; + 788262952E514F38000085AC /* QuickHelpView+Watermarking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262942E514F31000085AC /* QuickHelpView+Watermarking.swift */; }; + 788262962E514F38000085AC /* QuickHelpView+Watermarking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262942E514F31000085AC /* QuickHelpView+Watermarking.swift */; }; + 788262972E514F38000085AC /* QuickHelpView+Watermarking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262942E514F31000085AC /* QuickHelpView+Watermarking.swift */; }; + 788262992E514F3F000085AC /* QuickHelpView+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262982E514F3B000085AC /* QuickHelpView+UI.swift */; }; + 7882629A2E514F3F000085AC /* QuickHelpView+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262982E514F3B000085AC /* QuickHelpView+UI.swift */; }; + 7882629B2E514F3F000085AC /* QuickHelpView+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262982E514F3B000085AC /* QuickHelpView+UI.swift */; }; + 788262BA2E5170F4000085AC /* AboutView+Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262B92E5170EF000085AC /* AboutView+Info.swift */; }; + 788262BB2E5170F4000085AC /* AboutView+Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262B92E5170EF000085AC /* AboutView+Info.swift */; }; + 788262BC2E5170F4000085AC /* AboutView+Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262B92E5170EF000085AC /* AboutView+Info.swift */; }; + 788262DA2E5171F0000085AC /* AboutView+Story.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262D92E5171EC000085AC /* AboutView+Story.swift */; }; + 788262DB2E5171F0000085AC /* AboutView+Story.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262D92E5171EC000085AC /* AboutView+Story.swift */; }; + 788262DC2E5171F0000085AC /* AboutView+Story.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262D92E5171EC000085AC /* AboutView+Story.swift */; }; + 788262DE2E517252000085AC /* AboutView+Eggs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262DD2E51724C000085AC /* AboutView+Eggs.swift */; }; + 788262DF2E517252000085AC /* AboutView+Eggs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262DD2E51724C000085AC /* AboutView+Eggs.swift */; }; + 788262E02E517252000085AC /* AboutView+Eggs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262DD2E51724C000085AC /* AboutView+Eggs.swift */; }; + 788262E22E5172E8000085AC /* AboutView+Credits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262E12E5172E3000085AC /* AboutView+Credits.swift */; }; + 788262E32E5172E8000085AC /* AboutView+Credits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262E12E5172E3000085AC /* AboutView+Credits.swift */; }; + 788262E42E5172E8000085AC /* AboutView+Credits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788262E12E5172E3000085AC /* AboutView+Credits.swift */; }; + 78917F5C2B99AE72005E10FA /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5B2B99AE72005E10FA /* WidgetKit.framework */; }; + 78917F5E2B99AE72005E10FA /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78917F5D2B99AE72005E10FA /* SwiftUI.framework */; }; + 78917F692B99AE73005E10FA /* mlchtWidgetBundle.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 78917F592B99AE72005E10FA /* mlchtWidgetBundle.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 78A9DD6A2CD55551002C131D /* mlchtCaptureBundle.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 78A9DD612CD55551002C131D /* mlchtCaptureBundle.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 78A9DE312CD581F1002C131D /* LockedCameraCapture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78A9DE302CD581F1002C131D /* LockedCameraCapture.framework */; }; + 78A9DE752CD5AE5F002C131D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; + 78AF68682E5D97E500BB9D5C /* CameraView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF68672E5D97E000BB9D5C /* CameraView+Preview.swift */; }; + 78AF68692E5D97E500BB9D5C /* CameraView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF68672E5D97E000BB9D5C /* CameraView+Preview.swift */; }; + 78AF686A2E5D97E500BB9D5C /* CameraView+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF68672E5D97E000BB9D5C /* CameraView+Preview.swift */; }; + 78AF686C2E5D97EC00BB9D5C /* CameraView+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF686B2E5D97E800BB9D5C /* CameraView+Controls.swift */; }; + 78AF686D2E5D97EC00BB9D5C /* CameraView+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF686B2E5D97E800BB9D5C /* CameraView+Controls.swift */; }; + 78AF686E2E5D97EC00BB9D5C /* CameraView+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF686B2E5D97E800BB9D5C /* CameraView+Controls.swift */; }; + 78AF687D2E5D99EF00BB9D5C /* Preferences+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF687C2E5D99E300BB9D5C /* Preferences+Extension.swift */; }; + 78AF687E2E5D99EF00BB9D5C /* Preferences+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF687C2E5D99E300BB9D5C /* Preferences+Extension.swift */; }; + 78AF687F2E5D99EF00BB9D5C /* Preferences+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AF687C2E5D99E300BB9D5C /* Preferences+Extension.swift */; }; + 78B72BB12E332830002E2D4E /* mlchtRemote.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 78B72BA72E33282E002E2D4E /* mlchtRemote.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 78C2EFC02E6971E600C6DD79 /* Init+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C2EFBF2E6971DE00C6DD79 /* Init+Debug.swift */; }; + 78C2EFC12E6971E600C6DD79 /* Init+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C2EFBF2E6971DE00C6DD79 /* Init+Debug.swift */; }; + 78C2EFC22E6971E600C6DD79 /* Init+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C2EFBF2E6971DE00C6DD79 /* Init+Debug.swift */; }; + 78C2EFC42E6972D600C6DD79 /* Init+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C2EFC32E6972D300C6DD79 /* Init+Internal.swift */; }; + 78C2EFC52E6972D600C6DD79 /* Init+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C2EFC32E6972D300C6DD79 /* Init+Internal.swift */; }; + 78C2EFC62E6972D600C6DD79 /* Init+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C2EFC32E6972D300C6DD79 /* Init+Internal.swift */; }; + 78CAC36A2CCD99B600A35AE8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; + 78D3F0042F6284F400760240 /* Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F0032F6284F200760240 /* Watch.swift */; }; + 78D3F0052F6284F400760240 /* Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F0032F6284F200760240 /* Watch.swift */; }; + 78D3F0062F6284F400760240 /* Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F0032F6284F200760240 /* Watch.swift */; }; + 78D3F00A2F629B1C00760240 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F0092F629B1A00760240 /* Connection.swift */; }; + 78D3F00C2F629B5700760240 /* Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F00B2F629B5500760240 /* Misc.swift */; }; + 78D3F00E2F629BC200760240 /* Connection+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F00D2F629BBD00760240 /* Connection+Notifications.swift */; }; + 78D3F0212F62A50A00760240 /* Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F01D2F62A50A00760240 /* Content.swift */; }; + 78D3F0222F62A50A00760240 /* Content+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F01E2F62A50A00760240 /* Content+Controls.swift */; }; + 78D3F0232F62A50A00760240 /* Content+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F01F2F62A50A00760240 /* Content+Debug.swift */; }; + 78D3F0252F62AA9000760240 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78D3F0242F62AA8E00760240 /* View.swift */; }; + 78E170772E51C97B009BEF2F /* CompatibilityView+Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E170762E51C971009BEF2F /* CompatibilityView+Camera.swift */; }; + 78E170782E51C97B009BEF2F /* CompatibilityView+Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E170762E51C971009BEF2F /* CompatibilityView+Camera.swift */; }; + 78E170792E51C97B009BEF2F /* CompatibilityView+Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E170762E51C971009BEF2F /* CompatibilityView+Camera.swift */; }; + 78E1707B2E51CC63009BEF2F /* CompatibilityView+Resolutions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1707A2E51CC5C009BEF2F /* CompatibilityView+Resolutions.swift */; }; + 78E1707C2E51CC63009BEF2F /* CompatibilityView+Resolutions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1707A2E51CC5C009BEF2F /* CompatibilityView+Resolutions.swift */; }; + 78E1707D2E51CC63009BEF2F /* CompatibilityView+Resolutions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1707A2E51CC5C009BEF2F /* CompatibilityView+Resolutions.swift */; }; + 78E1707F2E51CE45009BEF2F /* CompatibilityView+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1707E2E51CE3D009BEF2F /* CompatibilityView+Format.swift */; }; + 78E170802E51CE45009BEF2F /* CompatibilityView+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1707E2E51CE3D009BEF2F /* CompatibilityView+Format.swift */; }; + 78E170812E51CE45009BEF2F /* CompatibilityView+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1707E2E51CE3D009BEF2F /* CompatibilityView+Format.swift */; }; + 78EC5D5F2E4EC110005044E2 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7837C1012E34BD60009396B0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 785F08412B12D41100244EB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 78917F582B99AE72005E10FA; + remoteInfo = WidgetBundle; + }; + 7837C1182E34C47D009396B0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 785F08412B12D41100244EB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7837C1042E34C45B009396B0; + remoteInfo = WidgetBundle_Watch; + }; + 78917F672B99AE73005E10FA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 785F08412B12D41100244EB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 78917F582B99AE72005E10FA; + remoteInfo = WidgetBundle; + }; + 78A9DD682CD55551002C131D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 785F08412B12D41100244EB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 78A9DD602CD55551002C131D; + remoteInfo = CaptureBundle; + }; + 78B72BAF2E332830002E2D4E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 785F08412B12D41100244EB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 78B72BA62E33282E002E2D4E; + remoteInfo = "MalachiteWatch Watch App"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7837C11A2E34C47D009396B0 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + dstPath = ""; + dstSubfolder = PlugIns; + files = ( + 7837C1172E34C47D009396B0 /* mlchtWidgetBundleWatch.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + }; + 78917F6A2B99AE73005E10FA /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + dstPath = ""; + dstSubfolder = PlugIns; + files = ( + 78917F692B99AE73005E10FA /* mlchtWidgetBundle.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + }; + 78A9DD702CD55551002C131D /* Embed ExtensionKit Extensions */ = { + isa = PBXCopyFilesBuildPhase; + dstPath = "$(EXTENSIONS_FOLDER_PATH)"; + dstSubfolder = Product; + files = ( + 78A9DD6A2CD55551002C131D /* mlchtCaptureBundle.appex in Embed ExtensionKit Extensions */, + ); + name = "Embed ExtensionKit Extensions"; + }; + 78B72BB62E332830002E2D4E /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolder = Product; + files = ( + 78B72BB12E332830002E2D4E /* mlchtRemote.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 78145F772E651357003AEE51 /* Init.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Init.swift; sourceTree = ""; }; + 78163D8D2CCB7BAE00146126 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; + 7818E23B2E8097180046F621 /* Compatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compatibility.swift; sourceTree = ""; }; + 7824CAEC2E62CDD90072870F /* CameraView+Overrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Overrides.swift"; sourceTree = ""; }; + 7824CAF02E62E4920072870F /* Camera+Bringup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Camera+Bringup.swift"; sourceTree = ""; }; + 782850002E80C04D00826FA7 /* Camera+Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Camera+Permissions.swift"; sourceTree = ""; }; + 782FC7752B2DAFAB007709C1 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; + 7837C1152E34C45B009396B0 /* mlchtWidgetBundleWatch.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = mlchtWidgetBundleWatch.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 78474D2B2D7AC879006FBB96 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + 7847D5022E8F26E9006759A6 /* View+Sliders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Sliders.swift"; sourceTree = ""; }; + 784EDDD32E5EE52100DFF370 /* PhotoPreviewView+Controls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PhotoPreviewView+Controls.swift"; sourceTree = ""; }; + 785345F72F5845F600DA7A99 /* mlchtWidgetBundle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = mlchtWidgetBundle.entitlements; sourceTree = ""; }; + 785345F82F58461300DA7A99 /* mlchtCaptureBundle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = mlchtCaptureBundle.entitlements; sourceTree = ""; }; + 785345F92F58467700DA7A99 /* mlchtWidgetBundleWatch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = mlchtWidgetBundleWatch.entitlements; sourceTree = ""; }; + 78562BC22B450A7600920160 /* PRIVACY_POLICY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = PRIVACY_POLICY.md; sourceTree = SOURCE_ROOT; }; + 785F08492B12D41100244EB4 /* mlchtCamera.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mlchtCamera.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 785F084C2B12D41100244EB4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 785F084E2B12D41100244EB4 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 785F085A2B12D41300244EB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7863E4E22F695697008566AE /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = ""; }; + 7866F5CD2E6299A0009AC9BF /* Camera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Camera.swift; sourceTree = ""; }; + 7866F5D12E62A591009AC9BF /* CameraView+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Notifications.swift"; sourceTree = ""; }; + 7866F5D52E62A5F7009AC9BF /* temputils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = temputils.swift; sourceTree = ""; }; + 7866F5DA2E62ADAB009AC9BF /* Game+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Game+View.swift"; sourceTree = ""; }; + 786DAE172E4F28A600BE3137 /* DeveloperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperView.swift; sourceTree = ""; }; + 786DAE182E4F28A600BE3137 /* DeveloperView+BuildInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeveloperView+BuildInfo.swift"; sourceTree = ""; }; + 786DAE192E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeveloperView+DeviceInfo.swift"; sourceTree = ""; }; + 786DAE1A2E4F28A600BE3137 /* DeveloperView+Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeveloperView+Settings.swift"; sourceTree = ""; }; + 786DAE1C2E4F28A600BE3137 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; + 786DAE1D2E4F28A600BE3137 /* CompatibilityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompatibilityView.swift; sourceTree = ""; }; + 786DAE1E2E4F28A600BE3137 /* PhotoPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPreviewView.swift; sourceTree = ""; }; + 786DAE1F2E4F28A600BE3137 /* QuickHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickHelpView.swift; sourceTree = ""; }; + 786DAE202E4F28A600BE3137 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 786DAE212E4F28A600BE3137 /* CameraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = ""; }; + 786DAE412E4F28B100BE3137 /* MalachitePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachitePreferences.swift; sourceTree = ""; }; + 786DAE422E4F28B100BE3137 /* MalachitePreferencesUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachitePreferencesUtils.swift; sourceTree = ""; }; + 786DAE442E4F28B100BE3137 /* MalachiteFunctionUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteFunctionUtils.swift; sourceTree = ""; }; + 786DAE452E4F28B100BE3137 /* MalachiteGameUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteGameUtils.swift; sourceTree = ""; }; + 786DAE462E4F28B100BE3137 /* MalachiteHapticUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteHapticUtils.swift; sourceTree = ""; }; + 786DAE472E4F28B100BE3137 /* MalachiteIntentUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteIntentUtils.swift; sourceTree = ""; }; + 786DAE482E4F28B100BE3137 /* MalachiteTooltipUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteTooltipUtils.swift; sourceTree = ""; }; + 786DAE492E4F28B100BE3137 /* MalachiteUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteUtils.swift; sourceTree = ""; }; + 786DAE4A2E4F28B100BE3137 /* MalachiteViewUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteViewUtils.swift; sourceTree = ""; }; + 786DAE682E4F28B700BE3137 /* ControlCenterWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlCenterWidget.swift; sourceTree = ""; }; + 786DAE692E4F28B700BE3137 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 786DAE6A2E4F28B700BE3137 /* LockScreenWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenWidget.swift; sourceTree = ""; }; + 786DAE6B2E4F28B700BE3137 /* MalachiteWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteWidgetBundle.swift; sourceTree = ""; }; + 786DAE752E4F28B900BE3137 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 786DAE762E4F28B900BE3137 /* MalachiteCaptureBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MalachiteCaptureBundle.swift; sourceTree = ""; }; + 786DAE7A2E4F28BF00BE3137 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 786DAE7B2E4F28BF00BE3137 /* mlchtRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mlchtRemote.swift; sourceTree = ""; }; + 78729C552E7368F0001027E9 /* Camera+Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Camera+Input.swift"; sourceTree = ""; }; + 787B1CA92B8B095E000AFECC /* mlchtCamera.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = mlchtCamera.docc; sourceTree = ""; }; + 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = mlchtCamera/Localizable.xcstrings; sourceTree = SOURCE_ROOT; }; + 7882625F2E51307B000085AC /* SettingsView+About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+About.swift"; sourceTree = ""; }; + 788262632E513094000085AC /* SettingsView+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+Preview.swift"; sourceTree = ""; }; + 7882626B2E5130BB000085AC /* SettingsView+Resolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+Resolution.swift"; sourceTree = ""; }; + 7882626F2E5130C6000085AC /* SettingsView+Photo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+Photo.swift"; sourceTree = ""; }; + 788262732E5130CF000085AC /* SettingsView+Watermarking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+Watermarking.swift"; sourceTree = ""; }; + 788262772E5130D8000085AC /* SettingsView+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+UI.swift"; sourceTree = ""; }; + 7882627B2E514749000085AC /* DeveloperView+Internal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeveloperView+Internal.swift"; sourceTree = ""; }; + 788262842E514F0C000085AC /* QuickHelpView+About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuickHelpView+About.swift"; sourceTree = ""; }; + 788262882E514F14000085AC /* QuickHelpView+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuickHelpView+Preview.swift"; sourceTree = ""; }; + 7882628C2E514F1F000085AC /* QuickHelpView+Resolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuickHelpView+Resolution.swift"; sourceTree = ""; }; + 788262902E514F28000085AC /* QuickHelpView+Photo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuickHelpView+Photo.swift"; sourceTree = ""; }; + 788262942E514F31000085AC /* QuickHelpView+Watermarking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuickHelpView+Watermarking.swift"; sourceTree = ""; }; + 788262982E514F3B000085AC /* QuickHelpView+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuickHelpView+UI.swift"; sourceTree = ""; }; + 788262B92E5170EF000085AC /* AboutView+Info.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AboutView+Info.swift"; sourceTree = ""; }; + 788262D92E5171EC000085AC /* AboutView+Story.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AboutView+Story.swift"; sourceTree = ""; }; + 788262DD2E51724C000085AC /* AboutView+Eggs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AboutView+Eggs.swift"; sourceTree = ""; }; + 788262E12E5172E3000085AC /* AboutView+Credits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AboutView+Credits.swift"; sourceTree = ""; }; + 788589912B8974680018E2DA /* mlchtCamera.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = mlchtCamera.entitlements; sourceTree = ""; }; + 78917F592B99AE72005E10FA /* mlchtWidgetBundle.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = mlchtWidgetBundle.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 78917F5B2B99AE72005E10FA /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 78917F5D2B99AE72005E10FA /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 78A9DD612CD55551002C131D /* mlchtCaptureBundle.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = mlchtCaptureBundle.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 78A9DE302CD581F1002C131D /* LockedCameraCapture.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LockedCameraCapture.framework; path = System/Library/Frameworks/LockedCameraCapture.framework; sourceTree = SDKROOT; }; + 78AF68672E5D97E000BB9D5C /* CameraView+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Preview.swift"; sourceTree = ""; }; + 78AF686B2E5D97E800BB9D5C /* CameraView+Controls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Controls.swift"; sourceTree = ""; }; + 78AF687C2E5D99E300BB9D5C /* Preferences+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Extension.swift"; sourceTree = ""; }; + 78B72BA72E33282E002E2D4E /* mlchtRemote.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mlchtRemote.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 78C2EFBF2E6971DE00C6DD79 /* Init+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Init+Debug.swift"; sourceTree = ""; }; + 78C2EFC32E6972D300C6DD79 /* Init+Internal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Init+Internal.swift"; sourceTree = ""; }; + 78D3F0032F6284F200760240 /* Watch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Watch.swift; sourceTree = ""; }; + 78D3F0092F629B1A00760240 /* Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; + 78D3F00B2F629B5500760240 /* Misc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Misc.swift; sourceTree = ""; }; + 78D3F00D2F629BBD00760240 /* Connection+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Notifications.swift"; sourceTree = ""; }; + 78D3F01D2F62A50A00760240 /* Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Content.swift; sourceTree = ""; }; + 78D3F01E2F62A50A00760240 /* Content+Controls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Controls.swift"; sourceTree = ""; }; + 78D3F01F2F62A50A00760240 /* Content+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Debug.swift"; sourceTree = ""; }; + 78D3F0242F62AA8E00760240 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; + 78E142C92DD426260016B3DB /* Codesigning.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Codesigning.xcconfig; sourceTree = ""; }; + 78E170762E51C971009BEF2F /* CompatibilityView+Camera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompatibilityView+Camera.swift"; sourceTree = ""; }; + 78E1707A2E51CC5C009BEF2F /* CompatibilityView+Resolutions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompatibilityView+Resolutions.swift"; sourceTree = ""; }; + 78E1707E2E51CE3D009BEF2F /* CompatibilityView+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompatibilityView+Format.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 786DAD932E4F283000BE3137 /* ci_scripts */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = ci_scripts; + sourceTree = ""; + }; + 786DAD982E4F283500BE3137 /* Assets */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Assets; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7837C10A2E34C45B009396B0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + files = ( + 7837C10B2E34C45B009396B0 /* SwiftUI.framework in Frameworks */, + 7837C10C2E34C45B009396B0 /* WidgetKit.framework in Frameworks */, + ); + }; + 78917F562B99AE72005E10FA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + files = ( + 78917F5E2B99AE72005E10FA /* SwiftUI.framework in Frameworks */, + 78917F5C2B99AE72005E10FA /* WidgetKit.framework in Frameworks */, + ); + }; + 78A9DD5E2CD55551002C131D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + files = ( + 78A9DE312CD581F1002C131D /* LockedCameraCapture.framework in Frameworks */, + ); + }; + 78B72BA42E33282E002E2D4E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + files = ( + ); + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7818E23A2E8097110046F621 /* CompatibilityUtils */ = { + isa = PBXGroup; + children = ( + 7818E23B2E8097180046F621 /* Compatibility.swift */, + ); + path = CompatibilityUtils; + sourceTree = ""; + }; + 7847D5012E8F26D1006759A6 /* ViewUtils */ = { + isa = PBXGroup; + children = ( + 7847D5022E8F26E9006759A6 /* View+Sliders.swift */, + 786DAE4A2E4F28B100BE3137 /* MalachiteViewUtils.swift */, + ); + path = ViewUtils; + sourceTree = ""; + }; + 785F08402B12D41100244EB4 = { + isa = PBXGroup; + children = ( + 78474D2B2D7AC879006FBB96 /* .gitignore */, + 78163D8D2CCB7BAE00146126 /* CHANGELOG.md */, + 782FC7752B2DAFAB007709C1 /* README.md */, + 78562BC22B450A7600920160 /* PRIVACY_POLICY.md */, + 786DAD932E4F283000BE3137 /* ci_scripts */, + 785F084B2B12D41100244EB4 /* mlchtCamera */, + 786DAE7C2E4F28BF00BE3137 /* mlchtRemote */, + 78917F5A2B99AE72005E10FA /* Frameworks */, + 785F084A2B12D41100244EB4 /* Products */, + ); + sourceTree = ""; + }; + 785F084A2B12D41100244EB4 /* Products */ = { + isa = PBXGroup; + children = ( + 785F08492B12D41100244EB4 /* mlchtCamera.app */, + 78917F592B99AE72005E10FA /* mlchtWidgetBundle.appex */, + 78A9DD612CD55551002C131D /* mlchtCaptureBundle.appex */, + 78B72BA72E33282E002E2D4E /* mlchtRemote.app */, + 7837C1152E34C45B009396B0 /* mlchtWidgetBundleWatch.appex */, + ); + name = Products; + sourceTree = ""; + }; + 785F084B2B12D41100244EB4 /* mlchtCamera */ = { + isa = PBXGroup; + children = ( + 786DAD982E4F283500BE3137 /* Assets */, + 78E142C92DD426260016B3DB /* Codesigning.xcconfig */, + 787B1CA92B8B095E000AFECC /* mlchtCamera.docc */, + 788589912B8974680018E2DA /* mlchtCamera.entitlements */, + 785F085A2B12D41300244EB4 /* Info.plist */, + 786DAE222E4F28A600BE3137 /* Views */, + 786DAE4B2E4F28B100BE3137 /* Utilities */, + 786DAE772E4F28B900BE3137 /* CaptureBundle */, + 786DAE6C2E4F28B700BE3137 /* WidgetBundle */, + 7881A98B2C1F77AB00B1F83B /* Localizable.xcstrings */, + 785F084C2B12D41100244EB4 /* AppDelegate.swift */, + 785F084E2B12D41100244EB4 /* SceneDelegate.swift */, + ); + path = mlchtCamera; + sourceTree = ""; + }; + 7863E4E12F69567C008566AE /* LocationUtils */ = { + isa = PBXGroup; + children = ( + 7863E4E22F695697008566AE /* Location.swift */, + ); + path = LocationUtils; + sourceTree = ""; + }; + 7866F5CC2E62999B009AC9BF /* CameraUtils */ = { + isa = PBXGroup; + children = ( + 7866F5CD2E6299A0009AC9BF /* Camera.swift */, + 782850002E80C04D00826FA7 /* Camera+Permissions.swift */, + 7824CAF02E62E4920072870F /* Camera+Bringup.swift */, + 78729C552E7368F0001027E9 /* Camera+Input.swift */, + ); + path = CameraUtils; + sourceTree = ""; + }; + 7866F5D92E62ADA0009AC9BF /* GameUtils */ = { + isa = PBXGroup; + children = ( + 7866F5DA2E62ADAB009AC9BF /* Game+View.swift */, + 786DAE452E4F28B100BE3137 /* MalachiteGameUtils.swift */, + ); + path = GameUtils; + sourceTree = ""; + }; + 786DAE1B2E4F28A600BE3137 /* DeveloperView */ = { + isa = PBXGroup; + children = ( + 786DAE172E4F28A600BE3137 /* DeveloperView.swift */, + 786DAE1A2E4F28A600BE3137 /* DeveloperView+Settings.swift */, + 7882627B2E514749000085AC /* DeveloperView+Internal.swift */, + 786DAE182E4F28A600BE3137 /* DeveloperView+BuildInfo.swift */, + 786DAE192E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift */, + ); + path = DeveloperView; + sourceTree = ""; + }; + 786DAE222E4F28A600BE3137 /* Views */ = { + isa = PBXGroup; + children = ( + 78AF68662E5D8E9D00BB9D5C /* PhotoPreviewView */, + 78AF68652E5D8E9200BB9D5C /* CameraView */, + 78E170752E51C312009BEF2F /* CompatibilityView */, + 788262B82E517076000085AC /* AboutView */, + 786DAE802E4F28D600BE3137 /* SettingsView */, + 7882627F2E514CA8000085AC /* QuickHelpView */, + 786DAE1B2E4F28A600BE3137 /* DeveloperView */, + ); + path = Views; + sourceTree = ""; + }; + 786DAE432E4F28B100BE3137 /* PreferenceUtils */ = { + isa = PBXGroup; + children = ( + 78AF687C2E5D99E300BB9D5C /* Preferences+Extension.swift */, + 786DAE412E4F28B100BE3137 /* MalachitePreferences.swift */, + 786DAE422E4F28B100BE3137 /* MalachitePreferencesUtils.swift */, + ); + path = PreferenceUtils; + sourceTree = ""; + }; + 786DAE4B2E4F28B100BE3137 /* Utilities */ = { + isa = PBXGroup; + children = ( + 78D3F0022F6284F000760240 /* WatchUtils */, + 7818E23A2E8097110046F621 /* CompatibilityUtils */, + 78C2EFBE2E6971CB00C6DD79 /* InitUtils */, + 7866F5D52E62A5F7009AC9BF /* temputils.swift */, + 7866F5CC2E62999B009AC9BF /* CameraUtils */, + 7863E4E12F69567C008566AE /* LocationUtils */, + 786DAE432E4F28B100BE3137 /* PreferenceUtils */, + 7847D5012E8F26D1006759A6 /* ViewUtils */, + 786DAE442E4F28B100BE3137 /* MalachiteFunctionUtils.swift */, + 786DAE462E4F28B100BE3137 /* MalachiteHapticUtils.swift */, + 786DAE472E4F28B100BE3137 /* MalachiteIntentUtils.swift */, + 786DAE482E4F28B100BE3137 /* MalachiteTooltipUtils.swift */, + 786DAE492E4F28B100BE3137 /* MalachiteUtils.swift */, + 7866F5D92E62ADA0009AC9BF /* GameUtils */, + ); + path = Utilities; + sourceTree = ""; + }; + 786DAE6C2E4F28B700BE3137 /* WidgetBundle */ = { + isa = PBXGroup; + children = ( + 785345F72F5845F600DA7A99 /* mlchtWidgetBundle.entitlements */, + 785345F92F58467700DA7A99 /* mlchtWidgetBundleWatch.entitlements */, + 786DAE682E4F28B700BE3137 /* ControlCenterWidget.swift */, + 786DAE692E4F28B700BE3137 /* Info.plist */, + 786DAE6A2E4F28B700BE3137 /* LockScreenWidget.swift */, + 786DAE6B2E4F28B700BE3137 /* MalachiteWidgetBundle.swift */, + ); + path = WidgetBundle; + sourceTree = ""; + }; + 786DAE772E4F28B900BE3137 /* CaptureBundle */ = { + isa = PBXGroup; + children = ( + 785345F82F58461300DA7A99 /* mlchtCaptureBundle.entitlements */, + 786DAE752E4F28B900BE3137 /* Info.plist */, + 786DAE762E4F28B900BE3137 /* MalachiteCaptureBundle.swift */, + ); + path = CaptureBundle; + sourceTree = ""; + }; + 786DAE7C2E4F28BF00BE3137 /* mlchtRemote */ = { + isa = PBXGroup; + children = ( + 78D3F0202F62A50A00760240 /* Views */, + 78D3F0072F629AF800760240 /* Utilities */, + 786DAE7A2E4F28BF00BE3137 /* Info.plist */, + 786DAE7B2E4F28BF00BE3137 /* mlchtRemote.swift */, + ); + path = mlchtRemote; + sourceTree = ""; + }; + 786DAE802E4F28D600BE3137 /* SettingsView */ = { + isa = PBXGroup; + children = ( + 786DAE202E4F28A600BE3137 /* SettingsView.swift */, + 7882625F2E51307B000085AC /* SettingsView+About.swift */, + 788262632E513094000085AC /* SettingsView+Preview.swift */, + 7882626B2E5130BB000085AC /* SettingsView+Resolution.swift */, + 7882626F2E5130C6000085AC /* SettingsView+Photo.swift */, + 788262732E5130CF000085AC /* SettingsView+Watermarking.swift */, + 788262772E5130D8000085AC /* SettingsView+UI.swift */, + ); + path = SettingsView; + sourceTree = ""; + }; + 7882627F2E514CA8000085AC /* QuickHelpView */ = { + isa = PBXGroup; + children = ( + 786DAE1F2E4F28A600BE3137 /* QuickHelpView.swift */, + 788262842E514F0C000085AC /* QuickHelpView+About.swift */, + 788262882E514F14000085AC /* QuickHelpView+Preview.swift */, + 7882628C2E514F1F000085AC /* QuickHelpView+Resolution.swift */, + 788262902E514F28000085AC /* QuickHelpView+Photo.swift */, + 788262942E514F31000085AC /* QuickHelpView+Watermarking.swift */, + 788262982E514F3B000085AC /* QuickHelpView+UI.swift */, + ); + path = QuickHelpView; + sourceTree = ""; + }; + 788262B82E517076000085AC /* AboutView */ = { + isa = PBXGroup; + children = ( + 786DAE1C2E4F28A600BE3137 /* AboutView.swift */, + 788262B92E5170EF000085AC /* AboutView+Info.swift */, + 788262D92E5171EC000085AC /* AboutView+Story.swift */, + 788262E12E5172E3000085AC /* AboutView+Credits.swift */, + 788262DD2E51724C000085AC /* AboutView+Eggs.swift */, + ); + path = AboutView; + sourceTree = ""; + }; + 78917F5A2B99AE72005E10FA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 78A9DE302CD581F1002C131D /* LockedCameraCapture.framework */, + 78917F5B2B99AE72005E10FA /* WidgetKit.framework */, + 78917F5D2B99AE72005E10FA /* SwiftUI.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 78AF68652E5D8E9200BB9D5C /* CameraView */ = { + isa = PBXGroup; + children = ( + 786DAE212E4F28A600BE3137 /* CameraView.swift */, + 78AF686B2E5D97E800BB9D5C /* CameraView+Controls.swift */, + 78AF68672E5D97E000BB9D5C /* CameraView+Preview.swift */, + 7866F5D12E62A591009AC9BF /* CameraView+Notifications.swift */, + 7824CAEC2E62CDD90072870F /* CameraView+Overrides.swift */, + ); + path = CameraView; + sourceTree = ""; + }; + 78AF68662E5D8E9D00BB9D5C /* PhotoPreviewView */ = { + isa = PBXGroup; + children = ( + 784EDDD32E5EE52100DFF370 /* PhotoPreviewView+Controls.swift */, + 786DAE1E2E4F28A600BE3137 /* PhotoPreviewView.swift */, + ); + path = PhotoPreviewView; + sourceTree = ""; + }; + 78C2EFBE2E6971CB00C6DD79 /* InitUtils */ = { + isa = PBXGroup; + children = ( + 78145F772E651357003AEE51 /* Init.swift */, + 78C2EFBF2E6971DE00C6DD79 /* Init+Debug.swift */, + 78C2EFC32E6972D300C6DD79 /* Init+Internal.swift */, + ); + path = InitUtils; + sourceTree = ""; + }; + 78D3F0022F6284F000760240 /* WatchUtils */ = { + isa = PBXGroup; + children = ( + 78D3F0032F6284F200760240 /* Watch.swift */, + ); + path = WatchUtils; + sourceTree = ""; + }; + 78D3F0072F629AF800760240 /* Utilities */ = { + isa = PBXGroup; + children = ( + 78D3F0262F62AA9600760240 /* ViewUtils */, + 78D3F00B2F629B5500760240 /* Misc.swift */, + 78D3F0082F629B1700760240 /* ConnectionUtils */, + ); + path = Utilities; + sourceTree = ""; + }; + 78D3F0082F629B1700760240 /* ConnectionUtils */ = { + isa = PBXGroup; + children = ( + 78D3F00D2F629BBD00760240 /* Connection+Notifications.swift */, + 78D3F0092F629B1A00760240 /* Connection.swift */, + ); + path = ConnectionUtils; + sourceTree = ""; + }; + 78D3F0202F62A50A00760240 /* Views */ = { + isa = PBXGroup; + children = ( + 78D3F01D2F62A50A00760240 /* Content.swift */, + 78D3F01E2F62A50A00760240 /* Content+Controls.swift */, + 78D3F01F2F62A50A00760240 /* Content+Debug.swift */, + ); + path = Views; + sourceTree = ""; + }; + 78D3F0262F62AA9600760240 /* ViewUtils */ = { + isa = PBXGroup; + children = ( + 78D3F0242F62AA8E00760240 /* View.swift */, + ); + path = ViewUtils; + sourceTree = ""; + }; + 78E170752E51C312009BEF2F /* CompatibilityView */ = { + isa = PBXGroup; + children = ( + 786DAE1D2E4F28A600BE3137 /* CompatibilityView.swift */, + 78E170762E51C971009BEF2F /* CompatibilityView+Camera.swift */, + 78E1707A2E51CC5C009BEF2F /* CompatibilityView+Resolutions.swift */, + 78E1707E2E51CE3D009BEF2F /* CompatibilityView+Format.swift */, + ); + path = CompatibilityView; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7837C1042E34C45B009396B0 /* mlchtWidgetBundleWatch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7837C1112E34C45B009396B0 /* Build configuration list for PBXNativeTarget "mlchtWidgetBundleWatch" */; + buildPhases = ( + 7837C1052E34C45B009396B0 /* Sources */, + 7837C10A2E34C45B009396B0 /* Frameworks */, + 7837C10D2E34C45B009396B0 /* Resources */, + ); + buildRules = ( + ); + fileSystemSynchronizedGroups = ( + 786DAD982E4F283500BE3137 /* Assets */, + ); + name = mlchtWidgetBundleWatch; + productName = MalachiteWidgetBundle; + productReference = 7837C1152E34C45B009396B0 /* mlchtWidgetBundleWatch.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 785F08482B12D41100244EB4 /* mlchtCamera */ = { + isa = PBXNativeTarget; + buildConfigurationList = 785F085D2B12D41300244EB4 /* Build configuration list for PBXNativeTarget "mlchtCamera" */; + buildPhases = ( + 785F08452B12D41100244EB4 /* Sources */, + 785F08472B12D41100244EB4 /* Resources */, + 78917F6A2B99AE73005E10FA /* Embed Foundation Extensions */, + 78A9DD702CD55551002C131D /* Embed ExtensionKit Extensions */, + 78B72BB62E332830002E2D4E /* Embed Watch Content */, + 78329B0F2C22699A006D326F /* Embed build information */, + ); + buildRules = ( + ); + dependencies = ( + 78917F682B99AE73005E10FA /* PBXTargetDependency */, + 78A9DD692CD55551002C131D /* PBXTargetDependency */, + 78B72BB02E332830002E2D4E /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 786DAD982E4F283500BE3137 /* Assets */, + ); + name = mlchtCamera; + productName = Malachite; + productReference = 785F08492B12D41100244EB4 /* mlchtCamera.app */; + productType = "com.apple.product-type.application"; + }; + 78917F582B99AE72005E10FA /* mlchtWidgetBundle */ = { + isa = PBXNativeTarget; + buildConfigurationList = 78917F6D2B99AE73005E10FA /* Build configuration list for PBXNativeTarget "mlchtWidgetBundle" */; + buildPhases = ( + 78917F552B99AE72005E10FA /* Sources */, + 78917F562B99AE72005E10FA /* Frameworks */, + 78917F572B99AE72005E10FA /* Resources */, + ); + buildRules = ( + ); + fileSystemSynchronizedGroups = ( + 786DAD982E4F283500BE3137 /* Assets */, + ); + name = mlchtWidgetBundle; + productName = MalachiteWidgetBundle; + productReference = 78917F592B99AE72005E10FA /* mlchtWidgetBundle.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 78A9DD602CD55551002C131D /* mlchtCaptureBundle */ = { + isa = PBXNativeTarget; + buildConfigurationList = 78A9DD6C2CD55551002C131D /* Build configuration list for PBXNativeTarget "mlchtCaptureBundle" */; + buildPhases = ( + 78A9DD5D2CD55551002C131D /* Sources */, + 78A9DD5E2CD55551002C131D /* Frameworks */, + 78A9DD5F2CD55551002C131D /* Resources */, + ); + buildRules = ( + ); + fileSystemSynchronizedGroups = ( + 786DAD982E4F283500BE3137 /* Assets */, + ); + name = mlchtCaptureBundle; + productName = MalachiteCaptureBundle; + productReference = 78A9DD612CD55551002C131D /* mlchtCaptureBundle.appex */; + productType = "com.apple.product-type.extensionkit-extension"; + }; + 78B72BA62E33282E002E2D4E /* mlchtRemote */ = { + isa = PBXNativeTarget; + buildConfigurationList = 78B72BB22E332830002E2D4E /* Build configuration list for PBXNativeTarget "mlchtRemote" */; + buildPhases = ( + 78B72BA32E33282E002E2D4E /* Sources */, + 78B72BA42E33282E002E2D4E /* Frameworks */, + 78B72BA52E33282E002E2D4E /* Resources */, + 7837C11A2E34C47D009396B0 /* Embed Foundation Extensions */, + 78EC5D602E4EC1AD005044E2 /* Embed build information */, + ); + buildRules = ( + ); + dependencies = ( + 7837C1022E34BD60009396B0 /* PBXTargetDependency */, + 7837C1192E34C47D009396B0 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 786DAD982E4F283500BE3137 /* Assets */, + ); + name = mlchtRemote; + productName = "MalachiteWatch Watch App"; + productReference = 78B72BA72E33282E002E2D4E /* mlchtRemote.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 785F08412B12D41100244EB4 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2600; + LastUpgradeCheck = 1630; + TargetAttributes = { + 785F08482B12D41100244EB4 = { + CreatedOnToolsVersion = 15.1; + }; + 78917F582B99AE72005E10FA = { + CreatedOnToolsVersion = 15.3; + }; + 78A9DD602CD55551002C131D = { + CreatedOnToolsVersion = 16.2; + }; + 78B72BA62E33282E002E2D4E = { + CreatedOnToolsVersion = 26.0; + }; + }; + }; + buildConfigurationList = 785F08442B12D41100244EB4 /* Build configuration list for PBXProject "mlchtCamera" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 785F08402B12D41100244EB4; + preferredProjectObjectVersion = 90; + productRefGroup = 785F084A2B12D41100244EB4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 785F08482B12D41100244EB4 /* mlchtCamera */, + 78B72BA62E33282E002E2D4E /* mlchtRemote */, + 78917F582B99AE72005E10FA /* mlchtWidgetBundle */, + 7837C1042E34C45B009396B0 /* mlchtWidgetBundleWatch */, + 78A9DD602CD55551002C131D /* mlchtCaptureBundle */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7837C10D2E34C45B009396B0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + 7837C10E2E34C45B009396B0 /* Localizable.xcstrings in Resources */, + ); + }; + 785F08472B12D41100244EB4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + 78EC5D5F2E4EC110005044E2 /* Localizable.xcstrings in Resources */, + ); + }; + 78917F572B99AE72005E10FA /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + 78CAC36A2CCD99B600A35AE8 /* Localizable.xcstrings in Resources */, + ); + }; + 78A9DD5F2CD55551002C131D /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + 78A9DE752CD5AE5F002C131D /* Localizable.xcstrings in Resources */, + ); + }; + 78B72BA52E33282E002E2D4E /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + ); + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 78329B0F2C22699A006D326F /* Embed build information */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + inputPaths = ( + "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}", + ); + name = "Embed build information "; + shellPath = /bin/bash; + shellScript = ( + "eval \"${GCC_PREPROCESSOR_DEFINITIONS}\"", + "", + "PATH=/opt/homebrew/bin:/usr/local/bin:$PATH", + "PLISTBUDDY=\"/usr/libexec/PlistBuddy\"", + "INFOPLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"", + "", + "# If there is no git repo to pull this info from, this is set to undefined later.", + "CFBUILDHASH=$(git --git-dir=\"${PROJECT_DIR}/.git\" --work-tree=\"${PROJECT_DIR}\" rev-parse --short HEAD)", + "CFBUILDBRAN=$(git branch --show-current)", + "CFBUILDDATE=$(date +'%Y-%m-%d-%H.%M.%S')", + "if [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && -z $(system_profiler SPHardwareDataType | grep 'MacVM1,1') ]]; then", + " # If the build is not INTERNAL, these are set to redacted later.", + " CFBUILDUSER=$(whoami)", + " CFBUILDHOST=$(hostname)", + "elif [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && !(-z $(system_profiler SPHardwareDataType | grep 'MacVM1,1')) ]]; then", + " # Xcode Cloud built this, but the hostname string is atrocious. Replace with these.", + " CFBUILDUSER=\"evaluna (App Store Connect)\"", + " CFBUILDHOST=\"Xcode Cloud\"", + "fi", + "", + "if [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && -z $(gpg --list-secret-keys | grep 4A5B8C2A065654E24DE50FF03716ACDC524F1879) ]]; then", + " echo \"Error: INTERNAL build type detected but Eva's GPG key was not found.\"", + " echo \"Error: See 'Embed build information' in Build Phases for a solution\"", + " # Be aware that I use the INTERNAL target to gate off any unstable/unready features. By removing this line, ", + " # you are acknowledging that you will not receive support for any issues encountered with mlchtCamera until you ", + " # are running DEBUG or RELEASE. If you just want a DEBUG build, change the Build Configuration to Debug in ", + " # Product > Scheme > Edit Scheme...", + " if [[ -z $(system_profiler SPHardwareDataType | grep 'MacVM1,1') ]]; then", + " exit", + " else", + " # This line of code should keep internal TestFlight builds as INTERNAL, and external ones as DEBUG", + " if [[ $(git --git-dir=\"${PROJECT_DIR}/.git\" rev-parse --abbrev-ref HEAD) != *\"dev\"* ]]; then", + " CFBUILDTYPE=\"DEBUG\"", + " fi", + " fi", + "fi", + "", + "", + "", + "# Print all of the variables in the build log. Useful for debugging", + "echo \"\"", + "echo \"CFBUILDHASH = ${CFBUILDHASH}\"", + "echo \"CFBUILDBRAN = ${CFBUILDBRAN}\"", + "echo \"CFBUILDDATE = ${CFBUILDDATE}\"", + "echo \"CFBUILDTYPE = ${CFBUILDTYPE}\"", + "if [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" ]]; then", + " echo \"CFBUILDUSER = ${CFBUILDUSER}\"", + " echo \"CFBUILDHOST = ${CFBUILDHOST}\"", + "fi", + "echo \"INFOPLIST = ${INFOPLIST}\"", + "", + "# Make sure the key is present in the plist before trying to set it.", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildHash' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildHash string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildBran' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildBran string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildDate' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildDate string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildType' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildType string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildUser' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildUser string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildHost' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildHost string\" \"${INFOPLIST}\"; fi", + "", + "# Fallback values, either to fill in the gaps or prevent app crashes", + "if [ -z \"$CFBUILDHASH\" ]; then CFBUILDHASH=undefined; fi", + "if [ -z \"$CFBUILDBRAN\" ]; then CFBUILDBRAN=undefined; fi", + "if [ -z \"$CFBUILDDATE\" ]; then CFBUILDDATE=undefined; fi", + "if [ -z \"$CFBUILDTYPE\" ]; then CFBUILDTYPE=debug; fi", + "if [ -z \"$CFBUILDUSER\" ]; then CFBUILDUSER=redacted; fi", + "if [ -z \"$CFBUILDHOST\" ]; then CFBUILDHOST=redacted; fi", + "", + "# Set the values in the Info.plist", + "$PLISTBUDDY -c \"Set :CFBuildHash $CFBUILDHASH\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildBran $CFBUILDBRAN\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildDate $CFBUILDDATE\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildType $CFBUILDTYPE\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildUser $CFBUILDUSER\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildHost $CFBUILDHOST\" \"${INFOPLIST}\"", + "", + "", + "", + "", + "", + "", + "", + ); + }; + 78EC5D602E4EC1AD005044E2 /* Embed build information */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + inputPaths = ( + "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}", + ); + name = "Embed build information"; + shellPath = /bin/bash; + shellScript = ( + "eval \"${GCC_PREPROCESSOR_DEFINITIONS}\"", + "", + "PATH=/opt/homebrew/bin:/usr/local/bin:$PATH", + "PLISTBUDDY=\"/usr/libexec/PlistBuddy\"", + "INFOPLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"", + "", + "# If there is no git repo to pull this info from, this is set to undefined later.", + "CFBUILDHASH=$(git --git-dir=\"${PROJECT_DIR}/.git\" --work-tree=\"${PROJECT_DIR}\" rev-parse --short HEAD)", + "CFBUILDBRAN=$(git branch --show-current)", + "CFBUILDDATE=$(date +'%Y-%m-%d-%H.%M.%S')", + "if [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && -z $(system_profiler SPHardwareDataType | grep 'MacVM1,1') ]]; then", + " # If the build is not INTERNAL, these are set to redacted later.", + " CFBUILDUSER=$(whoami)", + " CFBUILDHOST=$(hostname)", + "elif [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && !(-z $(system_profiler SPHardwareDataType | grep 'MacVM1,1')) ]]; then", + " # Xcode Cloud built this, but the hostname string is atrocious. Replace with these.", + " CFBUILDUSER=\"evaluna (App Store Connect)\"", + " CFBUILDHOST=\"Xcode Cloud\"", + "fi", + "", + "if [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" && -z $(gpg --list-secret-keys | grep 4A5B8C2A065654E24DE50FF03716ACDC524F1879) ]]; then", + " echo \"Error: INTERNAL build type detected but Eva's GPG key was not found.\"", + " echo \"Error: See 'Embed build information' in Build Phases for a solution\"", + " # Be aware that I use the INTERNAL target to gate off any unstable/unready features. By removing this line, ", + " # you are acknowledging that you will not receive support for any issues encountered with mlchtCamera until you ", + " # are running DEBUG or RELEASE. If you just want a DEBUG build, change the Build Configuration to Debug in ", + " # Product > Scheme > Edit Scheme...", + " if [[ -z $(system_profiler SPHardwareDataType | grep 'MacVM1,1') ]]; then", + " exit", + " else", + " # This line of code should keep internal TestFlight builds as INTERNAL, and external ones as DEBUG", + " if [[ $(git --git-dir=\"${PROJECT_DIR}/.git\" rev-parse --abbrev-ref HEAD) != *\"dev\"* ]]; then", + " CFBUILDTYPE=\"DEBUG\"", + " fi", + " fi", + "fi", + "", + "", + "", + "# Print all of the variables in the build log. Useful for debugging", + "echo \"\"", + "echo \"CFBUILDHASH = ${CFBUILDHASH}\"", + "echo \"CFBUILDBRAN = ${CFBUILDBRAN}\"", + "echo \"CFBUILDDATE = ${CFBUILDDATE}\"", + "echo \"CFBUILDTYPE = ${CFBUILDTYPE}\"", + "if [[ \"${CFBUILDTYPE}\" == \"INTERNAL\" ]]; then", + " echo \"CFBUILDUSER = ${CFBUILDUSER}\"", + " echo \"CFBUILDHOST = ${CFBUILDHOST}\"", + "fi", + "echo \"INFOPLIST = ${INFOPLIST}\"", + "", + "# Make sure the key is present in the plist before trying to set it.", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildHash' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildHash string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildBran' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildBran string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildDate' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildDate string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildType' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildType string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildUser' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildUser string\" \"${INFOPLIST}\"; fi", + "TMP=$(/usr/libexec/PlistBuddy -c 'print :CFBuildHost' \"${INFOPLIST}\" 2>/dev/null)", + "if [ -z \"$TMP\" ]; then $PLISTBUDDY -c \"Add :CFBuildHost string\" \"${INFOPLIST}\"; fi", + "", + "# Fallback values, either to fill in the gaps or prevent app crashes", + "if [ -z \"$CFBUILDHASH\" ]; then CFBUILDHASH=undefined; fi", + "if [ -z \"$CFBUILDBRAN\" ]; then CFBUILDBRAN=undefined; fi", + "if [ -z \"$CFBUILDDATE\" ]; then CFBUILDDATE=undefined; fi", + "if [ -z \"$CFBUILDTYPE\" ]; then CFBUILDTYPE=debug; fi", + "if [ -z \"$CFBUILDUSER\" ]; then CFBUILDUSER=redacted; fi", + "if [ -z \"$CFBUILDHOST\" ]; then CFBUILDHOST=redacted; fi", + "", + "# Set the values in the Info.plist", + "$PLISTBUDDY -c \"Set :CFBuildHash $CFBUILDHASH\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildBran $CFBUILDBRAN\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildDate $CFBUILDDATE\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildType $CFBUILDTYPE\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildUser $CFBUILDUSER\" \"${INFOPLIST}\"", + "$PLISTBUDDY -c \"Set :CFBuildHost $CFBUILDHOST\" \"${INFOPLIST}\"", + "", + "", + "", + "", + "", + "", + "", + ); + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7837C1052E34C45B009396B0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + 786DAE712E4F28B700BE3137 /* ControlCenterWidget.swift in Sources */, + 786DAE722E4F28B700BE3137 /* LockScreenWidget.swift in Sources */, + 786DAE732E4F28B700BE3137 /* MalachiteWidgetBundle.swift in Sources */, + 786DAE672E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */, + ); + }; + 785F08452B12D41100244EB4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + 788262BB2E5170F4000085AC /* AboutView+Info.swift in Sources */, + 78E1707D2E51CC63009BEF2F /* CompatibilityView+Resolutions.swift in Sources */, + 7866F5DC2E62ADB1009AC9BF /* Game+View.swift in Sources */, + 788262DA2E5171F0000085AC /* AboutView+Story.swift in Sources */, + 78AF686C2E5D97EC00BB9D5C /* CameraView+Controls.swift in Sources */, + 786DAE552E4F28B100BE3137 /* MalachitePreferences.swift in Sources */, + 786DAE562E4F28B100BE3137 /* MalachitePreferencesUtils.swift in Sources */, + 78AF68682E5D97E500BB9D5C /* CameraView+Preview.swift in Sources */, + 7882628A2E514F18000085AC /* QuickHelpView+Preview.swift in Sources */, + 78729C582E7368F3001027E9 /* Camera+Input.swift in Sources */, + 788262E32E5172E8000085AC /* AboutView+Credits.swift in Sources */, + 786DAE572E4F28B100BE3137 /* MalachiteFunctionUtils.swift in Sources */, + 788262932E514F2D000085AC /* QuickHelpView+Photo.swift in Sources */, + 78D3F0062F6284F400760240 /* Watch.swift in Sources */, + 786DAE582E4F28B100BE3137 /* MalachiteGameUtils.swift in Sources */, + 786DAE592E4F28B100BE3137 /* MalachiteHapticUtils.swift in Sources */, + 7863E4E52F695699008566AE /* Location.swift in Sources */, + 78E170772E51C97B009BEF2F /* CompatibilityView+Camera.swift in Sources */, + 786DAE5A2E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */, + 7818E23D2E80971C0046F621 /* Compatibility.swift in Sources */, + 788262622E513080000085AC /* SettingsView+About.swift in Sources */, + 786DAE5B2E4F28B100BE3137 /* MalachiteTooltipUtils.swift in Sources */, + 786DAE5C2E4F28B100BE3137 /* MalachiteUtils.swift in Sources */, + 7866F5D22E62A596009AC9BF /* CameraView+Notifications.swift in Sources */, + 786DAE5D2E4F28B100BE3137 /* MalachiteViewUtils.swift in Sources */, + 78C2EFC62E6972D600C6DD79 /* Init+Internal.swift in Sources */, + 788262722E5130CA000085AC /* SettingsView+Photo.swift in Sources */, + 786DAE2D2E4F28A600BE3137 /* DeveloperView.swift in Sources */, + 78145F7A2E65135C003AEE51 /* Init.swift in Sources */, + 786DAE2E2E4F28A600BE3137 /* DeveloperView+BuildInfo.swift in Sources */, + 7866F5D72E62A5FA009AC9BF /* temputils.swift in Sources */, + 7882627A2E5130DB000085AC /* SettingsView+UI.swift in Sources */, + 786DAE2F2E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift in Sources */, + 786DAE302E4F28A600BE3137 /* DeveloperView+Settings.swift in Sources */, + 7824CAED2E62CDDE0072870F /* CameraView+Overrides.swift in Sources */, + 78AF687E2E5D99EF00BB9D5C /* Preferences+Extension.swift in Sources */, + 786DAE312E4F28A600BE3137 /* AboutView.swift in Sources */, + 786DAE322E4F28A600BE3137 /* CompatibilityView.swift in Sources */, + 78C2EFC22E6971E600C6DD79 /* Init+Debug.swift in Sources */, + 7882627E2E514750000085AC /* DeveloperView+Internal.swift in Sources */, + 786DAE332E4F28A600BE3137 /* PhotoPreviewView.swift in Sources */, + 786DAE342E4F28A600BE3137 /* QuickHelpView.swift in Sources */, + 784EDDD62E5EE52600DFF370 /* PhotoPreviewView+Controls.swift in Sources */, + 788262862E514F12000085AC /* QuickHelpView+About.swift in Sources */, + 7882626E2E5130C1000085AC /* SettingsView+Resolution.swift in Sources */, + 786DAE352E4F28A600BE3137 /* SettingsView.swift in Sources */, + 7882628F2E514F25000085AC /* QuickHelpView+Resolution.swift in Sources */, + 782850012E80C05100826FA7 /* Camera+Permissions.swift in Sources */, + 7882629A2E514F3F000085AC /* QuickHelpView+UI.swift in Sources */, + 788262762E5130D4000085AC /* SettingsView+Watermarking.swift in Sources */, + 786DAE362E4F28A600BE3137 /* CameraView.swift in Sources */, + 785F084D2B12D41100244EB4 /* AppDelegate.swift in Sources */, + 788262962E514F38000085AC /* QuickHelpView+Watermarking.swift in Sources */, + 787B1CAA2B8B095E000AFECC /* mlchtCamera.docc in Sources */, + 7824CAF22E62E4970072870F /* Camera+Bringup.swift in Sources */, + 788262662E51309C000085AC /* SettingsView+Preview.swift in Sources */, + 788262E02E517252000085AC /* AboutView+Eggs.swift in Sources */, + 7866F5CE2E6299A4009AC9BF /* Camera.swift in Sources */, + 7847D5052E8F26EC006759A6 /* View+Sliders.swift in Sources */, + 78E1707F2E51CE45009BEF2F /* CompatibilityView+Format.swift in Sources */, + 785F084F2B12D41100244EB4 /* SceneDelegate.swift in Sources */, + ); + }; + 78917F552B99AE72005E10FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + 788262BC2E5170F4000085AC /* AboutView+Info.swift in Sources */, + 78E1707B2E51CC63009BEF2F /* CompatibilityView+Resolutions.swift in Sources */, + 7866F5DD2E62ADB1009AC9BF /* Game+View.swift in Sources */, + 788262DB2E5171F0000085AC /* AboutView+Story.swift in Sources */, + 78AF686E2E5D97EC00BB9D5C /* CameraView+Controls.swift in Sources */, + 786DAE5E2E4F28B100BE3137 /* MalachitePreferences.swift in Sources */, + 786DAE5F2E4F28B100BE3137 /* MalachitePreferencesUtils.swift in Sources */, + 78AF68692E5D97E500BB9D5C /* CameraView+Preview.swift in Sources */, + 788262892E514F18000085AC /* QuickHelpView+Preview.swift in Sources */, + 78729C562E7368F3001027E9 /* Camera+Input.swift in Sources */, + 788262E22E5172E8000085AC /* AboutView+Credits.swift in Sources */, + 786DAE602E4F28B100BE3137 /* MalachiteFunctionUtils.swift in Sources */, + 788262922E514F2D000085AC /* QuickHelpView+Photo.swift in Sources */, + 78D3F0052F6284F400760240 /* Watch.swift in Sources */, + 786DAE612E4F28B100BE3137 /* MalachiteGameUtils.swift in Sources */, + 786DAE622E4F28B100BE3137 /* MalachiteHapticUtils.swift in Sources */, + 7863E4E42F695699008566AE /* Location.swift in Sources */, + 78E170792E51C97B009BEF2F /* CompatibilityView+Camera.swift in Sources */, + 786DAE632E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */, + 7818E23C2E80971C0046F621 /* Compatibility.swift in Sources */, + 788262612E513080000085AC /* SettingsView+About.swift in Sources */, + 786DAE642E4F28B100BE3137 /* MalachiteTooltipUtils.swift in Sources */, + 786DAE652E4F28B100BE3137 /* MalachiteUtils.swift in Sources */, + 7866F5D32E62A596009AC9BF /* CameraView+Notifications.swift in Sources */, + 786DAE662E4F28B100BE3137 /* MalachiteViewUtils.swift in Sources */, + 78C2EFC52E6972D600C6DD79 /* Init+Internal.swift in Sources */, + 788262702E5130CA000085AC /* SettingsView+Photo.swift in Sources */, + 786DAE372E4F28A600BE3137 /* DeveloperView.swift in Sources */, + 78145F792E65135C003AEE51 /* Init.swift in Sources */, + 786DAE382E4F28A600BE3137 /* DeveloperView+BuildInfo.swift in Sources */, + 7866F5D82E62A5FA009AC9BF /* temputils.swift in Sources */, + 788262792E5130DB000085AC /* SettingsView+UI.swift in Sources */, + 786DAE392E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift in Sources */, + 786DAE6D2E4F28B700BE3137 /* ControlCenterWidget.swift in Sources */, + 7824CAEE2E62CDDE0072870F /* CameraView+Overrides.swift in Sources */, + 78AF687D2E5D99EF00BB9D5C /* Preferences+Extension.swift in Sources */, + 786DAE6E2E4F28B700BE3137 /* LockScreenWidget.swift in Sources */, + 786DAE6F2E4F28B700BE3137 /* MalachiteWidgetBundle.swift in Sources */, + 78C2EFC02E6971E600C6DD79 /* Init+Debug.swift in Sources */, + 7882627D2E514750000085AC /* DeveloperView+Internal.swift in Sources */, + 786DAE3A2E4F28A600BE3137 /* DeveloperView+Settings.swift in Sources */, + 786DAE3B2E4F28A600BE3137 /* AboutView.swift in Sources */, + 784EDDD42E5EE52600DFF370 /* PhotoPreviewView+Controls.swift in Sources */, + 788262852E514F12000085AC /* QuickHelpView+About.swift in Sources */, + 7882626D2E5130C1000085AC /* SettingsView+Resolution.swift in Sources */, + 786DAE3C2E4F28A600BE3137 /* CompatibilityView.swift in Sources */, + 7882628E2E514F25000085AC /* QuickHelpView+Resolution.swift in Sources */, + 782850022E80C05100826FA7 /* Camera+Permissions.swift in Sources */, + 788262992E514F3F000085AC /* QuickHelpView+UI.swift in Sources */, + 788262742E5130D4000085AC /* SettingsView+Watermarking.swift in Sources */, + 786DAE3D2E4F28A600BE3137 /* PhotoPreviewView.swift in Sources */, + 786DAE3E2E4F28A600BE3137 /* QuickHelpView.swift in Sources */, + 788262952E514F38000085AC /* QuickHelpView+Watermarking.swift in Sources */, + 786DAE3F2E4F28A600BE3137 /* SettingsView.swift in Sources */, + 7824CAF32E62E4970072870F /* Camera+Bringup.swift in Sources */, + 788262652E51309C000085AC /* SettingsView+Preview.swift in Sources */, + 788262DE2E517252000085AC /* AboutView+Eggs.swift in Sources */, + 7866F5CF2E6299A4009AC9BF /* Camera.swift in Sources */, + 7847D5032E8F26EC006759A6 /* View+Sliders.swift in Sources */, + 78E170812E51CE45009BEF2F /* CompatibilityView+Format.swift in Sources */, + 786DAE402E4F28A600BE3137 /* CameraView.swift in Sources */, + ); + }; + 78A9DD5D2CD55551002C131D /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + 786DAE232E4F28A600BE3137 /* DeveloperView.swift in Sources */, + 788262782E5130DB000085AC /* SettingsView+UI.swift in Sources */, + 7866F5D02E6299A4009AC9BF /* Camera.swift in Sources */, + 786DAE242E4F28A600BE3137 /* DeveloperView+BuildInfo.swift in Sources */, + 786DAE252E4F28A600BE3137 /* DeveloperView+DeviceInfo.swift in Sources */, + 78E170782E51C97B009BEF2F /* CompatibilityView+Camera.swift in Sources */, + 788262602E513080000085AC /* SettingsView+About.swift in Sources */, + 786DAE4C2E4F28B100BE3137 /* MalachitePreferences.swift in Sources */, + 7847D5042E8F26EC006759A6 /* View+Sliders.swift in Sources */, + 78D3F0042F6284F400760240 /* Watch.swift in Sources */, + 78C2EFC42E6972D600C6DD79 /* Init+Internal.swift in Sources */, + 788262DF2E517252000085AC /* AboutView+Eggs.swift in Sources */, + 788262972E514F38000085AC /* QuickHelpView+Watermarking.swift in Sources */, + 788262752E5130D4000085AC /* SettingsView+Watermarking.swift in Sources */, + 7882627C2E514750000085AC /* DeveloperView+Internal.swift in Sources */, + 78729C572E7368F3001027E9 /* Camera+Input.swift in Sources */, + 78145F782E65135C003AEE51 /* Init.swift in Sources */, + 786DAE4D2E4F28B100BE3137 /* MalachitePreferencesUtils.swift in Sources */, + 788262DC2E5171F0000085AC /* AboutView+Story.swift in Sources */, + 78AF686D2E5D97EC00BB9D5C /* CameraView+Controls.swift in Sources */, + 788262642E51309C000085AC /* SettingsView+Preview.swift in Sources */, + 782850032E80C05100826FA7 /* Camera+Permissions.swift in Sources */, + 786DAE782E4F28B900BE3137 /* MalachiteCaptureBundle.swift in Sources */, + 786DAE4E2E4F28B100BE3137 /* MalachiteFunctionUtils.swift in Sources */, + 7824CAF12E62E4970072870F /* Camera+Bringup.swift in Sources */, + 7882629B2E514F3F000085AC /* QuickHelpView+UI.swift in Sources */, + 786DAE4F2E4F28B100BE3137 /* MalachiteGameUtils.swift in Sources */, + 7824CAEF2E62CDDE0072870F /* CameraView+Overrides.swift in Sources */, + 7882626C2E5130C1000085AC /* SettingsView+Resolution.swift in Sources */, + 78AF686A2E5D97E500BB9D5C /* CameraView+Preview.swift in Sources */, + 786DAE502E4F28B100BE3137 /* MalachiteHapticUtils.swift in Sources */, + 786DAE512E4F28B100BE3137 /* MalachiteIntentUtils.swift in Sources */, + 788262E42E5172E8000085AC /* AboutView+Credits.swift in Sources */, + 786DAE522E4F28B100BE3137 /* MalachiteTooltipUtils.swift in Sources */, + 786DAE532E4F28B100BE3137 /* MalachiteUtils.swift in Sources */, + 788262BA2E5170F4000085AC /* AboutView+Info.swift in Sources */, + 7866F5D42E62A596009AC9BF /* CameraView+Notifications.swift in Sources */, + 786DAE542E4F28B100BE3137 /* MalachiteViewUtils.swift in Sources */, + 78C2EFC12E6971E600C6DD79 /* Init+Debug.swift in Sources */, + 788262872E514F12000085AC /* QuickHelpView+About.swift in Sources */, + 786DAE262E4F28A600BE3137 /* DeveloperView+Settings.swift in Sources */, + 784EDDD52E5EE52600DFF370 /* PhotoPreviewView+Controls.swift in Sources */, + 788262912E514F2D000085AC /* QuickHelpView+Photo.swift in Sources */, + 786DAE272E4F28A600BE3137 /* AboutView.swift in Sources */, + 78E1707C2E51CC63009BEF2F /* CompatibilityView+Resolutions.swift in Sources */, + 786DAE282E4F28A600BE3137 /* CompatibilityView.swift in Sources */, + 7866F5D62E62A5FA009AC9BF /* temputils.swift in Sources */, + 78AF687F2E5D99EF00BB9D5C /* Preferences+Extension.swift in Sources */, + 786DAE292E4F28A600BE3137 /* PhotoPreviewView.swift in Sources */, + 788262712E5130CA000085AC /* SettingsView+Photo.swift in Sources */, + 78E170802E51CE45009BEF2F /* CompatibilityView+Format.swift in Sources */, + 7866F5DB2E62ADB1009AC9BF /* Game+View.swift in Sources */, + 7818E23E2E80971C0046F621 /* Compatibility.swift in Sources */, + 7882628D2E514F25000085AC /* QuickHelpView+Resolution.swift in Sources */, + 786DAE2A2E4F28A600BE3137 /* QuickHelpView.swift in Sources */, + 7882628B2E514F18000085AC /* QuickHelpView+Preview.swift in Sources */, + 786DAE2B2E4F28A600BE3137 /* SettingsView.swift in Sources */, + 7863E4E32F695699008566AE /* Location.swift in Sources */, + 786DAE2C2E4F28A600BE3137 /* CameraView.swift in Sources */, + ); + }; + 78B72BA32E33282E002E2D4E /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + 78D3F0252F62AA9000760240 /* View.swift in Sources */, + 78D3F0212F62A50A00760240 /* Content.swift in Sources */, + 78D3F0222F62A50A00760240 /* Content+Controls.swift in Sources */, + 78D3F0232F62A50A00760240 /* Content+Debug.swift in Sources */, + 78D3F00E2F629BC200760240 /* Connection+Notifications.swift in Sources */, + 78D3F00A2F629B1C00760240 /* Connection.swift in Sources */, + 786DAE7E2E4F28BF00BE3137 /* mlchtRemote.swift in Sources */, + 78D3F00C2F629B5700760240 /* Misc.swift in Sources */, + ); + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7837C1022E34BD60009396B0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 78917F582B99AE72005E10FA /* mlchtWidgetBundle */; + targetProxy = 7837C1012E34BD60009396B0 /* PBXContainerItemProxy */; + }; + 7837C1192E34C47D009396B0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7837C1042E34C45B009396B0 /* mlchtWidgetBundleWatch */; + targetProxy = 7837C1182E34C47D009396B0 /* PBXContainerItemProxy */; + }; + 78917F682B99AE73005E10FA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 78917F582B99AE72005E10FA /* mlchtWidgetBundle */; + targetProxy = 78917F672B99AE73005E10FA /* PBXContainerItemProxy */; + }; + 78A9DD692CD55551002C131D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 78A9DD602CD55551002C131D /* mlchtCaptureBundle */; + targetProxy = 78A9DD682CD55551002C131D /* PBXContainerItemProxy */; + }; + 78B72BB02E332830002E2D4E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 78B72BA62E33282E002E2D4E /* mlchtRemote */; + targetProxy = 78B72BAF2E332830002E2D4E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 7837C1122E34C45B009396B0 /* Debug configuration for PBXNativeTarget "mlchtWidgetBundleWatch" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/WidgetBundle/mlchtWidgetBundleWatch.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_ENHANCED_SECURITY = NO; + ENABLE_POINTER_AUTHENTICATION = NO; + EXCLUDED_ARCHS = arm64e; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/WidgetBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtWidgetBundle; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = "-DAPP_EXTENSION"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).watchremote.widgetbundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchos watchsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VERSIONING_SYSTEM = "apple-generic"; + WATCHOS_DEPLOYMENT_TARGET = 9.0; + }; + name = Debug; + }; + 7837C1132E34C45B009396B0 /* Internal configuration for PBXNativeTarget "mlchtWidgetBundleWatch" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/WidgetBundle/mlchtWidgetBundleWatch.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_ENHANCED_SECURITY = NO; + ENABLE_POINTER_AUTHENTICATION = NO; + EXCLUDED_ARCHS = arm64e; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/WidgetBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtWidgetBundle; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = "-DAPP_EXTENSION"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).watchremote.widgetbundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchos watchsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VERSIONING_SYSTEM = "apple-generic"; + WATCHOS_DEPLOYMENT_TARGET = 9.0; + }; + name = Internal; + }; + 7837C1142E34C45B009396B0 /* Release configuration for PBXNativeTarget "mlchtWidgetBundleWatch" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/WidgetBundle/mlchtWidgetBundleWatch.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_ENHANCED_SECURITY = NO; + ENABLE_POINTER_AUTHENTICATION = NO; + EXCLUDED_ARCHS = arm64e; + GCC_PREPROCESSOR_DEFINITIONS = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/WidgetBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtWidgetBundle; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = "-DAPP_EXTENSION"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).watchremote.widgetbundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchos watchsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VERSIONING_SYSTEM = "apple-generic"; + WATCHOS_DEPLOYMENT_TARGET = 9.0; + }; + name = Release; + }; + 785F085B2B12D41300244EB4 /* Debug configuration for PBXProject "mlchtCamera" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 95J8WZ4TN8; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 785F085C2B12D41300244EB4 /* Release configuration for PBXProject "mlchtCamera" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 95J8WZ4TN8; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_POINTER_AUTHENTICATION = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 785F085E2B12D41300244EB4 /* Debug configuration for PBXNativeTarget "mlchtCamera" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "asb_approved thatsinceguy"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/mlchtCamera.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "CFBUILDTYPE=DEBUG", + "$(inherited)", + ); + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtCamera; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCameraUsageDescription = "mlchtCamera uses the camera systems in order to show a viewfinder and take pictures."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "mlchtCamera can optionally embed your location in the pictures you take. You don't need to enable this if you never plan to embed locations, and can additionally configure this using the in-app Settings menu."; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "mlchtCamera uses write access to your library in order to save pictures. You don't need to enable this if you plan to use the app as a viewfinder."; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UIRequiresFullScreen = NO; + INFOPLIST_KEY_UIStatusBarHidden = YES; + INFOPLIST_KEY_UIStatusBarStyle = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OBJC_INTERFACE_HEADER_NAME = "mlchtCamera-Swift.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 785F085F2B12D41300244EB4 /* Release configuration for PBXNativeTarget "mlchtCamera" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "asb_approved thatsinceguy"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/mlchtCamera.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREPROCESSOR_DEFINITIONS = "CFBUILDTYPE=RELEASE"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtCamera; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCameraUsageDescription = "mlchtCamera uses the camera systems in order to show a viewfinder and take pictures."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "mlchtCamera can optionally embed your location in the pictures you take. You don't need to enable this if you never plan to embed locations, and can additionally configure this using the in-app Settings menu."; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "mlchtCamera uses write access to your library in order to save pictures. You don't need to enable this if you plan to use the app as a viewfinder."; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UIRequiresFullScreen = NO; + INFOPLIST_KEY_UIStatusBarHidden = YES; + INFOPLIST_KEY_UIStatusBarStyle = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = MAIN_APP; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OBJC_INTERFACE_HEADER_NAME = "mlchtCamera-Swift.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 78917F6B2B99AE73005E10FA /* Debug configuration for PBXNativeTarget "mlchtWidgetBundle" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/WidgetBundle/mlchtWidgetBundle.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/WidgetBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtWidgetBundle; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).widgetbundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WATCHOS_DEPLOYMENT_TARGET = 26.0; + }; + name = Debug; + }; + 78917F6C2B99AE73005E10FA /* Release configuration for PBXNativeTarget "mlchtWidgetBundle" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/WidgetBundle/mlchtWidgetBundle.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + GCC_PREPROCESSOR_DEFINITIONS = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/WidgetBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtWidgetBundle; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).widgetbundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WATCHOS_DEPLOYMENT_TARGET = 26.0; + }; + name = Release; + }; + 78A9DD6D2CD55551002C131D /* Debug configuration for PBXNativeTarget "mlchtCaptureBundle" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/CaptureBundle/mlchtCaptureBundle.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/CaptureBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtCaptureBundle; + INFOPLIST_KEY_NSCameraUsageDescription = "mlchtCamera uses the rear camera system on iPhone to present your magnified view."; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "mlchtCamera uses write access to your library in order to save photos."; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).capturebundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 78A9DD6E2CD55551002C131D /* Internal configuration for PBXNativeTarget "mlchtCaptureBundle" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/CaptureBundle/mlchtCaptureBundle.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/CaptureBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtCaptureBundle; + INFOPLIST_KEY_NSCameraUsageDescription = "mlchtCamera uses the rear camera system on iPhone to present your magnified view."; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "mlchtCamera uses write access to your library in order to save photos."; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).capturebundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Internal; + }; + 78A9DD6F2CD55551002C131D /* Release configuration for PBXNativeTarget "mlchtCaptureBundle" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/CaptureBundle/mlchtCaptureBundle.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/CaptureBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtCaptureBundle; + INFOPLIST_KEY_NSCameraUsageDescription = "mlchtCamera uses the rear camera system on iPhone to present your magnified view."; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "mlchtCamera uses write access to your library in order to save photos."; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).capturebundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 78B72BB32E332830002E2D4E /* Debug configuration for PBXNativeTarget "mlchtRemote" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_ENHANCED_SECURITY = NO; + ENABLE_POINTER_AUTHENTICATION = NO; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + EXCLUDED_ARCHS = arm64e; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtRemote/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtRemote; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "$(APP_PREFIX)"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).watchremote"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Debug; + }; + 78B72BB42E332830002E2D4E /* Internal configuration for PBXNativeTarget "mlchtRemote" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_ENHANCED_SECURITY = NO; + ENABLE_POINTER_AUTHENTICATION = NO; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + EXCLUDED_ARCHS = arm64e; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtRemote/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtRemote; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "$(APP_PREFIX)"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).watchremote"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Internal; + }; + 78B72BB52E332830002E2D4E /* Release configuration for PBXNativeTarget "mlchtRemote" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_ENHANCED_SECURITY = NO; + ENABLE_POINTER_AUTHENTICATION = NO; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + EXCLUDED_ARCHS = arm64e; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtRemote/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtRemote; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "$(APP_PREFIX)"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).watchremote"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = MAIN_APP; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Release; + }; + 78F9DA642CBCDDB900A99638 /* Internal configuration for PBXProject "mlchtCamera" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 95J8WZ4TN8; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_COMPILATION_MODE = singlefile; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Internal; + }; + 78F9DA652CBCDDB900A99638 /* Internal configuration for PBXNativeTarget "mlchtCamera" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "asb_approved thatsinceguy"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/mlchtCamera.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "CFBUILDTYPE=INTERNAL", + "$(inherited)", + ); + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtCamera; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCameraUsageDescription = "mlchtCamera uses the camera systems in order to show a viewfinder and take pictures."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "mlchtCamera can optionally embed your location in the pictures you take. You don't need to enable this if you never plan to embed locations, and can additionally configure this using the in-app Settings menu."; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "mlchtCamera uses write access to your library in order to save pictures. You don't need to enable this if you plan to use the app as a viewfinder."; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UIRequiresFullScreen = NO; + INFOPLIST_KEY_UIStatusBarHidden = YES; + INFOPLIST_KEY_UIStatusBarStyle = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "MAIN_APP $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OBJC_INTERFACE_HEADER_NAME = "mlchtCamera-Swift.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Internal; + }; + 78F9DA662CBCDDB900A99638 /* Internal configuration for PBXNativeTarget "mlchtWidgetBundle" */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78E142C92DD426260016B3DB /* Codesigning.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + ASSETCATALOG_OTHER_FLAGS = "--enable-icon-stack-fallback-generation=disabled"; + CODE_SIGN_ENTITLEMENTS = mlchtCamera/WidgetBundle/mlchtWidgetBundle.entitlements; + CODE_SIGN_STYLE = "$(CODE_SIGN_STYLE)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = YES; + ENABLE_C_BOUNDS_SAFETY = YES; + ENABLE_ENHANCED_SECURITY = YES; + ENABLE_POINTER_AUTHENTICATION = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = mlchtCamera/WidgetBundle/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = mlchtWidgetBundle; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_PREFIX).widgetbundle"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WATCHOS_DEPLOYMENT_TARGET = 26.0; + }; + name = Internal; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7837C1112E34C45B009396B0 /* Build configuration list for PBXNativeTarget "mlchtWidgetBundleWatch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7837C1122E34C45B009396B0 /* Debug configuration for PBXNativeTarget "mlchtWidgetBundleWatch" */, + 7837C1132E34C45B009396B0 /* Internal configuration for PBXNativeTarget "mlchtWidgetBundleWatch" */, + 7837C1142E34C45B009396B0 /* Release configuration for PBXNativeTarget "mlchtWidgetBundleWatch" */, + ); + defaultConfigurationName = Internal; + }; + 785F08442B12D41100244EB4 /* Build configuration list for PBXProject "mlchtCamera" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 785F085B2B12D41300244EB4 /* Debug configuration for PBXProject "mlchtCamera" */, + 78F9DA642CBCDDB900A99638 /* Internal configuration for PBXProject "mlchtCamera" */, + 785F085C2B12D41300244EB4 /* Release configuration for PBXProject "mlchtCamera" */, + ); + defaultConfigurationName = Internal; + }; + 785F085D2B12D41300244EB4 /* Build configuration list for PBXNativeTarget "mlchtCamera" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 785F085E2B12D41300244EB4 /* Debug configuration for PBXNativeTarget "mlchtCamera" */, + 78F9DA652CBCDDB900A99638 /* Internal configuration for PBXNativeTarget "mlchtCamera" */, + 785F085F2B12D41300244EB4 /* Release configuration for PBXNativeTarget "mlchtCamera" */, + ); + defaultConfigurationName = Internal; + }; + 78917F6D2B99AE73005E10FA /* Build configuration list for PBXNativeTarget "mlchtWidgetBundle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 78917F6B2B99AE73005E10FA /* Debug configuration for PBXNativeTarget "mlchtWidgetBundle" */, + 78F9DA662CBCDDB900A99638 /* Internal configuration for PBXNativeTarget "mlchtWidgetBundle" */, + 78917F6C2B99AE73005E10FA /* Release configuration for PBXNativeTarget "mlchtWidgetBundle" */, + ); + defaultConfigurationName = Internal; + }; + 78A9DD6C2CD55551002C131D /* Build configuration list for PBXNativeTarget "mlchtCaptureBundle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 78A9DD6D2CD55551002C131D /* Debug configuration for PBXNativeTarget "mlchtCaptureBundle" */, + 78A9DD6E2CD55551002C131D /* Internal configuration for PBXNativeTarget "mlchtCaptureBundle" */, + 78A9DD6F2CD55551002C131D /* Release configuration for PBXNativeTarget "mlchtCaptureBundle" */, + ); + defaultConfigurationName = Internal; + }; + 78B72BB22E332830002E2D4E /* Build configuration list for PBXNativeTarget "mlchtRemote" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 78B72BB32E332830002E2D4E /* Debug configuration for PBXNativeTarget "mlchtRemote" */, + 78B72BB42E332830002E2D4E /* Internal configuration for PBXNativeTarget "mlchtRemote" */, + 78B72BB52E332830002E2D4E /* Release configuration for PBXNativeTarget "mlchtRemote" */, + ); + defaultConfigurationName = Internal; + }; +/* End XCConfigurationList section */ + }; + rootObject = 785F08412B12D41100244EB4 /* Project object */; +} diff --git a/Malachite.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mlchtCamera.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Malachite.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to mlchtCamera.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Malachite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mlchtCamera.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Malachite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to mlchtCamera.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Malachite.xcodeproj/xcshareddata/xcschemes/Malachite.xcscheme b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtCamera.xcscheme old mode 100755 new mode 100644 similarity index 79% rename from Malachite.xcodeproj/xcshareddata/xcschemes/Malachite.xcscheme rename to mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtCamera.xcscheme index 32432b4..5d94408 --- a/Malachite.xcodeproj/xcshareddata/xcschemes/Malachite.xcscheme +++ b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtCamera.xcscheme @@ -16,15 +16,15 @@ + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + buildConfiguration = "Internal"> + BuildableName = "mlchtCaptureBundle.appex" + BlueprintName = "mlchtCaptureBundle" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + buildConfiguration = "Internal"> diff --git a/Malachite.xcodeproj/xcshareddata/xcschemes/MalachiteWatch.xcscheme b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtRemote.xcscheme similarity index 76% rename from Malachite.xcodeproj/xcshareddata/xcschemes/MalachiteWatch.xcscheme rename to mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtRemote.xcscheme index 9ff921b..ca40e36 100644 --- a/Malachite.xcodeproj/xcshareddata/xcschemes/MalachiteWatch.xcscheme +++ b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtRemote.xcscheme @@ -16,9 +16,9 @@ + BuildableName = "mlchtRemote.app" + BlueprintName = "mlchtRemote" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtRemote.app" + BlueprintName = "mlchtRemote" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtRemote.app" + BlueprintName = "mlchtRemote" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + buildConfiguration = "Internal"> diff --git a/Malachite.xcodeproj/xcshareddata/xcschemes/WidgetBundle.xcscheme b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtWidgetBundle.xcscheme old mode 100755 new mode 100644 similarity index 76% rename from Malachite.xcodeproj/xcshareddata/xcschemes/WidgetBundle.xcscheme rename to mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtWidgetBundle.xcscheme index 8541a26..97ae352 --- a/Malachite.xcodeproj/xcshareddata/xcschemes/WidgetBundle.xcscheme +++ b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtWidgetBundle.xcscheme @@ -17,9 +17,9 @@ + BuildableName = "mlchtWidgetBundle.appex" + BlueprintName = "mlchtWidgetBundle" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + BuildableName = "mlchtCamera.app" + BlueprintName = "mlchtCamera" + ReferencedContainer = "container:mlchtCamera.xcodeproj"> + buildConfiguration = "Internal"> diff --git a/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtWidgetBundleWatch.xcscheme b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtWidgetBundleWatch.xcscheme new file mode 100644 index 0000000..2be6a12 --- /dev/null +++ b/mlchtCamera.xcodeproj/xcshareddata/xcschemes/mlchtWidgetBundleWatch.xcscheme @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Malachite/AppDelegate.swift b/mlchtCamera/AppDelegate.swift similarity index 100% rename from Malachite/AppDelegate.swift rename to mlchtCamera/AppDelegate.swift diff --git a/Malachite/Assets/AppIcon.icon/Assets/0.5.png b/mlchtCamera/Assets/AppIcon.icon/Assets/0.5.png similarity index 100% rename from Malachite/Assets/AppIcon.icon/Assets/0.5.png rename to mlchtCamera/Assets/AppIcon.icon/Assets/0.5.png diff --git a/Malachite/Assets/AppIcon.icon/Assets/0.75.png b/mlchtCamera/Assets/AppIcon.icon/Assets/0.75.png similarity index 100% rename from Malachite/Assets/AppIcon.icon/Assets/0.75.png rename to mlchtCamera/Assets/AppIcon.icon/Assets/0.75.png diff --git a/Malachite/Assets/AppIcon.icon/Assets/1.png b/mlchtCamera/Assets/AppIcon.icon/Assets/1.png similarity index 100% rename from Malachite/Assets/AppIcon.icon/Assets/1.png rename to mlchtCamera/Assets/AppIcon.icon/Assets/1.png diff --git a/Malachite/Assets/AppIcon.icon/Assets/Circle.svg b/mlchtCamera/Assets/AppIcon.icon/Assets/Circle.svg similarity index 100% rename from Malachite/Assets/AppIcon.icon/Assets/Circle.svg rename to mlchtCamera/Assets/AppIcon.icon/Assets/Circle.svg diff --git a/Malachite/Assets/AppIcon.icon/Assets/SpokesAndCircle.svg b/mlchtCamera/Assets/AppIcon.icon/Assets/SpokesAndCircle.svg similarity index 100% rename from Malachite/Assets/AppIcon.icon/Assets/SpokesAndCircle.svg rename to mlchtCamera/Assets/AppIcon.icon/Assets/SpokesAndCircle.svg diff --git a/Malachite/Assets/AppIcon.icon/icon.json b/mlchtCamera/Assets/AppIcon.icon/icon.json similarity index 100% rename from Malachite/Assets/AppIcon.icon/icon.json rename to mlchtCamera/Assets/AppIcon.icon/icon.json diff --git a/Malachite/Assets/Assets.xcassets/AccentColor.colorset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/AccentColor.colorset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Dark.png b/mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Dark.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Dark.png rename to mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Dark.png diff --git a/Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Tinted.png b/mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Tinted.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Tinted.png rename to mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon-Tinted.png diff --git a/Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon.png b/mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon.png rename to mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIcon.png diff --git a/Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIconWatch.png b/mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIconWatch.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIconWatch.png rename to mlchtCamera/Assets/Assets.xcassets/AppIcon.appiconset/MalachiteIconWatch.png diff --git a/Malachite/Assets/Assets.xcassets/Contents.json b/mlchtCamera/Assets/Assets.xcassets/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/WidgetBackground.colorset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/WidgetBackground.colorset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/WidgetBackground.colorset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/WidgetBackground.colorset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/asb_approved.appiconset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/asb_approved.appiconset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/asb_approved.appiconset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/asb_approved.appiconset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/asb_approved.appiconset/asbapproved.png b/mlchtCamera/Assets/Assets.xcassets/asb_approved.appiconset/asbapproved.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/asb_approved.appiconset/asbapproved.png rename to mlchtCamera/Assets/Assets.xcassets/asb_approved.appiconset/asbapproved.png diff --git a/Malachite/Assets/Assets.xcassets/asentientbot.imageset/57969415.jpeg b/mlchtCamera/Assets/Assets.xcassets/asentientbot.imageset/57969415.jpeg similarity index 100% rename from Malachite/Assets/Assets.xcassets/asentientbot.imageset/57969415.jpeg rename to mlchtCamera/Assets/Assets.xcassets/asentientbot.imageset/57969415.jpeg diff --git a/Malachite/Assets/Assets.xcassets/asentientbot.imageset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/asentientbot.imageset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/asentientbot.imageset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/asentientbot.imageset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/crystall1nedev.imageset/55281754.png b/mlchtCamera/Assets/Assets.xcassets/crystall1nedev.imageset/55281754.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/crystall1nedev.imageset/55281754.png rename to mlchtCamera/Assets/Assets.xcassets/crystall1nedev.imageset/55281754.png diff --git a/Malachite/Assets/Assets.xcassets/crystall1nedev.imageset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/crystall1nedev.imageset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/crystall1nedev.imageset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/crystall1nedev.imageset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/icon.imageset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/icon.imageset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/icon.imageset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/icon.imageset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/icon.imageset/MalachiteIconMasked_Darwin24.png b/mlchtCamera/Assets/Assets.xcassets/icon.imageset/MalachiteIconMasked_Darwin24.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/icon.imageset/MalachiteIconMasked_Darwin24.png rename to mlchtCamera/Assets/Assets.xcassets/icon.imageset/MalachiteIconMasked_Darwin24.png diff --git a/Malachite/Assets/Assets.xcassets/icon26.imageset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/icon26.imageset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/icon26.imageset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/icon26.imageset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/icon26.imageset/MalachiteIconMasked_Darwin25.png b/mlchtCamera/Assets/Assets.xcassets/icon26.imageset/MalachiteIconMasked_Darwin25.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/icon26.imageset/MalachiteIconMasked_Darwin25.png rename to mlchtCamera/Assets/Assets.xcassets/icon26.imageset/MalachiteIconMasked_Darwin25.png diff --git a/Malachite/Assets/Assets.xcassets/thatsniceguy.appiconset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/thatsniceguy.appiconset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/thatsniceguy.appiconset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/thatsniceguy.appiconset/Contents.json diff --git a/Malachite/Assets/Assets.xcassets/thatsniceguy.appiconset/Icon1024x1024.png b/mlchtCamera/Assets/Assets.xcassets/thatsniceguy.appiconset/Icon1024x1024.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/thatsniceguy.appiconset/Icon1024x1024.png rename to mlchtCamera/Assets/Assets.xcassets/thatsniceguy.appiconset/Icon1024x1024.png diff --git a/Malachite/Assets/Assets.xcassets/thatstella7922.imageset/10524728.png b/mlchtCamera/Assets/Assets.xcassets/thatstella7922.imageset/10524728.png similarity index 100% rename from Malachite/Assets/Assets.xcassets/thatstella7922.imageset/10524728.png rename to mlchtCamera/Assets/Assets.xcassets/thatstella7922.imageset/10524728.png diff --git a/Malachite/Assets/Assets.xcassets/thatstella7922.imageset/Contents.json b/mlchtCamera/Assets/Assets.xcassets/thatstella7922.imageset/Contents.json similarity index 100% rename from Malachite/Assets/Assets.xcassets/thatstella7922.imageset/Contents.json rename to mlchtCamera/Assets/Assets.xcassets/thatstella7922.imageset/Contents.json diff --git a/Malachite/Assets/MalachiteIconMasked_Darwin24.png b/mlchtCamera/Assets/MalachiteIconMasked_Darwin24.png similarity index 100% rename from Malachite/Assets/MalachiteIconMasked_Darwin24.png rename to mlchtCamera/Assets/MalachiteIconMasked_Darwin24.png diff --git a/Malachite/Assets/MalachiteIconMasked_Darwin25.png b/mlchtCamera/Assets/MalachiteIconMasked_Darwin25.png similarity index 100% rename from Malachite/Assets/MalachiteIconMasked_Darwin25.png rename to mlchtCamera/Assets/MalachiteIconMasked_Darwin25.png diff --git a/Malachite/CaptureBundle/Info.plist b/mlchtCamera/CaptureBundle/Info.plist similarity index 100% rename from Malachite/CaptureBundle/Info.plist rename to mlchtCamera/CaptureBundle/Info.plist diff --git a/Malachite/CaptureBundle/MalachiteCaptureBundle.swift b/mlchtCamera/CaptureBundle/MalachiteCaptureBundle.swift similarity index 76% rename from Malachite/CaptureBundle/MalachiteCaptureBundle.swift rename to mlchtCamera/CaptureBundle/MalachiteCaptureBundle.swift index 8dcd335..e6e4477 100755 --- a/Malachite/CaptureBundle/MalachiteCaptureBundle.swift +++ b/mlchtCamera/CaptureBundle/MalachiteCaptureBundle.swift @@ -21,7 +21,7 @@ struct MalachiteCaptureBundle: LockedCameraCaptureExtension { } struct MalachiteCaptureBundleViewFinder: UIViewControllerRepresentable { - typealias UIViewControllerType = MalachiteView + typealias UIViewControllerType = CameraView // Apple's sample LockedCameraCapture code let session: LockedCameraCaptureSession @@ -31,10 +31,10 @@ struct MalachiteCaptureBundleViewFinder: UIViewControllerRepresentable { self.session = session } - func makeUIViewController(context: Self.Context) -> MalachiteView { - return MalachiteView() + func makeUIViewController(context: Self.Context) -> CameraView { + return CameraView() } - func updateUIViewController(_ uiViewController: MalachiteView, context: Self.Context) { + func updateUIViewController(_ uiViewController: CameraView, context: Self.Context) { } } diff --git a/mlchtCamera/CaptureBundle/mlchtCaptureBundle.entitlements b/mlchtCamera/CaptureBundle/mlchtCaptureBundle.entitlements new file mode 100644 index 0000000..b85694e --- /dev/null +++ b/mlchtCamera/CaptureBundle/mlchtCaptureBundle.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.hardened-process + + com.apple.security.hardened-process.checked-allocations + + com.apple.security.hardened-process.dyld-ro + + com.apple.security.hardened-process.enhanced-security-version-string + 1 + com.apple.security.hardened-process.hardened-heap + + com.apple.security.hardened-process.platform-restrictions-string + 2 + + diff --git a/Malachite/Codesigning.example.xcconfig b/mlchtCamera/Codesigning.example.xcconfig similarity index 88% rename from Malachite/Codesigning.example.xcconfig rename to mlchtCamera/Codesigning.example.xcconfig index 7b1a4e5..f589c5a 100644 --- a/Malachite/Codesigning.example.xcconfig +++ b/mlchtCamera/Codesigning.example.xcconfig @@ -12,4 +12,4 @@ CODE_SIGN_STYLE = Automatic; // https://developer.apple.com/account DEVELOPMENT_TEAM = 95J8WZ4TN8; // Change this to anything you want. -APP_PREFIX = dev.crystll1ne; +APP_PREFIX = dev.crystll1ne.mlchtcamera; diff --git a/Malachite/Info.plist b/mlchtCamera/Info.plist similarity index 81% rename from Malachite/Info.plist rename to mlchtCamera/Info.plist index 8a3d3d1..518f41f 100755 --- a/Malachite/Info.plist +++ b/mlchtCamera/Info.plist @@ -8,12 +8,12 @@ CFBundleTypeRole Editor CFBundleURLIconFile - AppIcon + MalachiteIconMasked_Darwin25 CFBundleURLName - dev.crystall1ne.Malachite + dev.crystall1ne.mlcht CFBundleURLSchemes - malachite + mlcht @@ -36,5 +36,10 @@ UIFileSharingEnabled + UILaunchScreen + + UIImageName + + diff --git a/Malachite/Localizable.xcstrings b/mlchtCamera/Localizable.xcstrings similarity index 71% rename from Malachite/Localizable.xcstrings rename to mlchtCamera/Localizable.xcstrings index 2910c47..bec90f1 100755 --- a/Malachite/Localizable.xcstrings +++ b/mlchtCamera/Localizable.xcstrings @@ -96,7 +96,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Bringing macro camera control back to you." + "value" : "Pro camera features without the pro price." } } } @@ -126,7 +126,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Malachite’s story" + "value" : "mlchtCamera’s story" } } } @@ -147,7 +147,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Malachite started as an app to help my love work on printed circuit boards with better clarity and manual controls that the stock iOS camera app can't provide, and it grew once my Discord community stood by, actually using it and suggesting new features. It's the first real test of my skills in Swift, and leading my own public project, and my goal is to now provide a free, all-inclusive experience to amazing macro photography - powered by your iPhone, iPad, or iPod touch." + "value" : "mlchtCamera started as an app to help my love work on printed circuit boards with better clarity and manual controls that the stock iOS camera app can't provide, and it grew once my Discord community stood by, actually using it and suggesting new features. It's the first real test of my skills in Swift, and leading my own public project, and my goal is to now provide a free, all-inclusive experience to amazing photography - powered by your iPhone, iPad, or iPod touch." } } } @@ -164,7 +164,7 @@ } }, "alert.button.ok" : { - "comment" : "Default action", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -186,7 +186,7 @@ } }, "alert.button.report" : { - "comment" : "Default action", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -202,7 +202,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "In order to change Malachite’s settings, this device will need to be unlocked." + "value" : "In order to change mlchtCamera’s settings, this device will need to be unlocked." } } } @@ -213,7 +213,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Camera switching is only supported on devices with multiple rear cameras." + "value" : "Camera switching is only supported on devices with multiple cameras." } } } @@ -235,7 +235,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "This device does not have a flashlight to turn on." + "value" : "The flashlight couldn’t be turned on for this camera." } } } @@ -268,7 +268,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Malachite requires photo library access in order to capture images. Go to Settings > Apps > Malachite to enable this permission." + "value" : "mlchtCamera requires photo library access in order to capture images. Go to Settings > Apps > mlcht to enable this permission." } } } @@ -290,7 +290,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unable to switch cameras" + "value" : "Can’t switch cameras" } } } @@ -301,7 +301,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unable to adjust exposure" + "value" : "Exposure locked" } } } @@ -312,7 +312,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unable to turn on flashlight" + "value" : "Can’t turn on flashlight" } } } @@ -323,7 +323,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unable to adjust focus" + "value" : "Focus locked" } } } @@ -334,7 +334,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Malachite quit unexpectedly." + "value" : "mlchtCamera quit unexpectedly." } } } @@ -345,7 +345,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unable to capture images" + "value" : "Can’t capture images" } } } @@ -355,7 +355,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Malachite" + "value" : "mlchtCamera" } } } @@ -365,7 +365,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Open Malachite" + "value" : "Open mlchtCamera" } } } @@ -375,7 +375,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Capture with Malachite" + "value" : "Capture with mlchtCamera" } } } @@ -385,7 +385,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Add to quickly launch malachite from your control center, lock screen, or Action Button." + "value" : "Add to quickly launch mlchtCamera from your control center, lock screen, or Action Button." } } } @@ -395,28 +395,47 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Tap to open Malachite directly from your lock screen." + "value" : "Tap to open mlchtCamera directly from your lock screen." } } } }, - "Built by %@ on %@" : { + "compatibility.header.cameras" : { "localizations" : { "en" : { "stringUnit" : { - "state" : "new", - "value" : "Built by %1$@ on %2$@" + "state" : "translated", + "value" : "Camera support" } } - }, - "shouldTranslate" : false + } + }, + "compatibility.header.format" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Format support" + } + } + } + }, + "compatibility.header.resolution" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resolution support" + } + } + } }, "compatibility.note" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "The following screen shows what features of Malachite your current device can take advantage of. " + "value" : "The following screen shows what features of mlchtCamera your current device can take advantage of. " } } } @@ -471,7 +490,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ultra wide supports 12MP capture" + "value" : "Ultra Wide supports 12MP capture" } } } @@ -482,7 +501,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ultra wide does not support 12MP capture" + "value" : "Ultra Wide does not support 12MP capture" } } } @@ -493,7 +512,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Wide angle supports 12MP capture" + "value" : "Main supports 12MP capture" } } } @@ -504,7 +523,29 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Wide angle does not support 12MP capture" + "value" : "Main does not support 12MP capture" + } + } + } + }, + "compatibility.title.48mp.telephoto" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fusion Telephoto supports 48MP capture" + } + } + } + }, + "compatibility.title.48mp.telephoto.unsupported" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Telephoto does not support 48MP capture" } } } @@ -515,7 +556,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ultra wide supports 48MP capture" + "value" : "Fusion Ultra Wide supports 48MP capture" } } } @@ -526,7 +567,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ultra wide does not support 48MP capture" + "value" : "Ultra Wide does not support 48MP capture" } } } @@ -537,7 +578,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Wide angle supports 48MP capture" + "value" : "Fusion Main supports 48MP capture" } } } @@ -548,7 +589,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Wide angle does not support 48MP capture" + "value" : "Main does not support 48MP capture" } } } @@ -575,24 +616,24 @@ } } }, - "compatibility.title.heif" : { + "compatibility.title.heic" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Device supports encoding in HEIF" + "value" : "Device supports encoding in HEIC" } } } }, - "compatibility.title.heif.unsupported" : { + "compatibility.title.heic.unsupported" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Device does not support HEIF encoding" + "value" : "Device does not support HEIC encoding" } } } @@ -619,7 +660,18 @@ } } }, - "compatibility.title.no.telephoto.unsupported" : { + "compatibility.title.telephoto" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Telephoto camera detected" + } + } + } + }, + "compatibility.title.telephoto.unsupported" : { "extractionState" : "manual", "localizations" : { "en" : { @@ -630,30 +682,331 @@ } } }, - "compatibility.title.no.ultrawide.unsupported" : { + "compatibility.title.ultrawide" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ultra Wide camera detected" + } + } + } + }, + "compatibility.title.ultrawide.unsupported" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Ultra Wide camera detected" + } + } + } + }, + "compatibility.title.wide" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "No ultra wide camera detected" + "value" : "Main camera detected" } } } }, - "compatibility.title.no.wide.unsupported" : { + "compatibility.title.wide.unsupported" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "No wide angle camera detected" + "value" : "No Main camera detected" + } + } + } + }, + "developer.detail.debug.breakapp" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: UI testing. Disables the ability to do anything other than open Settings." + } + } + } + }, + "developer.detail.debug.erase.gamekit" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: Erases all local GameKit data for the current user." + } + } + } + }, + "developer.detail.debug.erase.preferences" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: Erases preferences.plist in mlchtCamera’s home directory." + } + } + } + }, + "developer.detail.debug.logging.imageprops" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: Prints all metadata and properties taken with the image." + } + } + } + }, + "developer.detail.debug.logging.preferences" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: Print preferences.plist to unified logging system." + } + } + } + }, + "developer.detail.debug.logging.unified" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: This option controls whether or not to enable the debugNSLog() function. INTERNAL builds cannot disable this." + } + } + } + }, + "developer.footer.debug" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG: Options meant for debugging purposes. " + } + } + } + }, + "developer.footer.device" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device information is shown for informational and debugging purposes only and does not leave your device unless you choose to export a log file from this screen." + } + } + } + }, + "developer.header.debug" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug settings" + } + } + } + }, + "developer.header.device" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device information" + } + } + } + }, + "developer.header.info" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Build information" + } + } + } + }, + "developer.header.internal" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Internal settings" + } + } + } + }, + "developer.option.debug.breakapp" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Break the app" + } + } + } + }, + "developer.option.debug.erase.gamekit" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erase GameKit data" + } + } + } + }, + "developer.option.debug.erase.preferences" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erase preferences.plist" + } + } + } + }, + "developer.option.debug.forcecheck" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Force check compatibility" + } + } + } + }, + "developer.option.debug.logging.imageprops" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log image properties" + } + } + } + }, + "developer.option.debug.logging.preferences" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log preferences.plist to ULS" + } + } + } + }, + "developer.option.debug.logging.unified" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "More verbose logging" + } + } + } + }, + "developer.option.device_build" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OS build number" + } + } + } + }, + "developer.option.device_model" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device model" + } + } + } + }, + "developer.option.device_version" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OS version" + } + } + } + }, + "developer.option.version_branch" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Branch" + } + } + } + }, + "developer.option.version_date" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date" + } + } + } + }, + "developer.option.version_hash" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commit hash" + } + } + } + }, + "developer.option.version_host" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hostname" + } + } + } + }, + "developer.option.version_type" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuration" + } + } + } + }, + "developer.option.version_user" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } } } }, "flash.off" : { - "comment" : "Off", "localizations" : { "en" : { "stringUnit" : { @@ -664,7 +1017,6 @@ } }, "flash.on" : { - "comment" : "On", "localizations" : { "en" : { "stringUnit" : { @@ -674,32 +1026,72 @@ } } }, - "settings.detail.debug.erase.gamekit" : { + "internal.option.blockaccidentalgestures" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Block accidental gestures" + } + } + } + }, + "internal.option.cameracontrol" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Camera Control overlay" + } + } + } + }, + "internal.option.location" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable geotagging" + } + } + } + }, + "internal.option.settingsgesture" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Swipe up for Settings" + } + } + } + }, + "internal.option.settingsgesture.1" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "DEBUG: This option destroys all GameKit related data for the current user. " + "value" : "1 finger" } } } }, - "settings.detail.debug.erase.userdefaults" : { + "internal.option.settingsgesture.2" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "DEBUG: This option destroys all UserDefaults keys." + "value" : "2 fingers" } } } }, - "settings.detail.debug.logging.userdefaults" : { + "internal.option.settingsgesture.3" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "DEBUG: This option dumps UserDefaults on launch." + "value" : "3 fingers" } } } @@ -799,7 +1191,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable this if you want to disable Malachite’s haptic feedback on buttons and gestures." + "value" : "Enable this if you want to disable mlchtCamera’s haptic feedback on buttons and gestures." } } } @@ -819,7 +1211,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable this to hide the user interface when Malachite opens, useful for using the app as a magnifier." + "value" : "Enable this to hide the user interface when mlchtCamera opens, useful for using the app as a magnifier." } } } @@ -849,7 +1241,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable this if you want to enable Malachite’s watermarking feature. Great for adding credits or details about the camera." + "value" : "Enable this if you want to enable mlchtCamera's watermarking feature. Great for adding credits or details about the camera." } } } @@ -864,23 +1256,13 @@ } } }, - "settings.footer.debug" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "DEBUG: These options are meant for debugging purposes. " - } - } - } - }, "settings.footer.photo" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "These options control how Malachite captures and saves images." + "value" : "These options control how mlchtCamera captures and saves images." } } } @@ -896,13 +1278,13 @@ } } }, - "settings.footer.photo.heif" : { + "settings.footer.photo.heic" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "The HEIF file format requires a device with the Apple A10 Fusion chip or later.\\n" + "value" : "The HEIC file format requires a device with the Apple A10 Fusion chip or later." } } } @@ -912,7 +1294,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "These options control the Malachite’s camera preview." + "value" : "These options control the mlchtCamera’s camera preview." } } } @@ -932,7 +1314,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "These options control various aspects of Malachite’s user interface." + "value" : "These options control various aspects of mlchtCamera's user interface." } } } @@ -942,17 +1324,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "These options control Malachite’s watermarking feature." - } - } - } - }, - "settings.header.debug" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Debug settings" + "value" : "These options control mlchtCamera's watermarking feature." } } } @@ -1007,46 +1379,6 @@ } } }, - "settings.option.debug.breakapp" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Break the app" - } - } - } - }, - "settings.option.debug.erase.gamekit" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Erase all GameKit data" - } - } - } - }, - "settings.option.debug.erase.userdefaults" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Erase preferences" - } - } - } - }, - "settings.option.debug.logging.userdefaults" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Log preferences to console" - } - } - } - }, "settings.option.photo.continuous" : { "localizations" : { "en" : { @@ -1067,12 +1399,12 @@ } } }, - "settings.option.photo.fileformat.heif" : { + "settings.option.photo.fileformat.heic" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "HEIF" + "value" : "HEIC" } } } @@ -1337,162 +1669,168 @@ } } }, - "settings.option.ui.settingsgesture" : { + "settings.option.ui.tapgesture" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Swipe up for Settings" + "value" : "Customize tap and hold" } } } }, - "settings.option.ui.settingsgesture.1" : { + "settings.option.watermark.enable" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "1 finger" + "value" : "Image watermarking" } } } }, - "settings.option.ui.settingsgesture.2" : { + "settings.option.watermark.text" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "2 fingers" + "value" : "Text to watermark" } } } }, - "settings.option.ui.settingsgesture.3" : { + "settings.option.watermark.text.placeholder" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "3 fingers" + "value" : "Shot on mlchtCamera" } } } }, - "settings.option.ui.tapgesture" : { + "uibutton.close.title" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Customize tap and hold" + "value" : "Close" } } } }, - "settings.option.watermark.enable" : { + "uibutton.exposure.title" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Image watermarking" + "value" : "Exposure" } } } }, - "settings.option.watermark.text" : { + "uibutton.flash.title" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Text to watermark" + "value" : "Flash brightness" } } } }, - "settings.option.watermark.text.placeholder" : { + "uibutton.focus.title" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Shot on Malachite" + "value" : "Focus" } } } }, - "uibutton.close.title" : { + "uibutton.save.title" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Close" + "value" : "Save to Photos" } } } }, - "uibutton.exposure.title" : { + "uibutton.share.title" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Exposure" + "value" : "Share" } } } }, - "uibutton.focus.title" : { + "view.detail.about" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Focus" + "value" : "Takes you to the About page for mlchtCamera." } } } }, - "uibutton.save.title" : { + "view.detail.compatibility" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Save to Photos" + "value" : "Lists various features and capabilities of mlchtCamera that your device can take advantage of, and ones it can't." } } } }, - "uibutton.share.title" : { + "view.detail.developer" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Share" + "value" : "Extra settings and information, intended for people working on mlchtCamera’s code." } } } }, - "view.detail.about" : { + "view.title.about" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Takes you to the About page for Malachite." + "value" : "About mlchtCamera" } } } }, - "view.title.about" : { + "view.title.compatibility" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "About Malachite" + "value" : "Compatibility" } } } }, - "view.title.compatibility" : { + "view.title.developer" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Compatibility" + "value" : "Developer" } } } diff --git a/mlchtCamera/SceneDelegate.swift b/mlchtCamera/SceneDelegate.swift new file mode 100755 index 0000000..3a950f9 --- /dev/null +++ b/mlchtCamera/SceneDelegate.swift @@ -0,0 +1,35 @@ +// +// SceneDelegate.swift +// Malachite +// +// Created by Eva Isabella Luna on 11/25/23. +// + +import UIKit +import WatchConnectivity + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + let utilities = MalachiteClassesObject() + + var window: UIWindow? + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + let initialization = Init(utilities: utilities) + let rootVC = CameraView() + + initialization.initMalachite() + rootVC.utilities = utilities + + window = UIWindow(windowScene: windowScene) + window?.rootViewController = rootVC + window?.makeKeyAndVisible() + } + + func sceneDidBecomeActive(_ scene: UIScene) { + utilities.watch.notifyOfForegroundChange() + } + + func sceneWillResignActive(_ scene: UIScene) { + utilities.watch.notifyOfForegroundChange() + } +} diff --git a/mlchtCamera/Utilities/CameraUtils/Camera+Bringup.swift b/mlchtCamera/Utilities/CameraUtils/Camera+Bringup.swift new file mode 100644 index 0000000..eadd9f9 --- /dev/null +++ b/mlchtCamera/Utilities/CameraUtils/Camera+Bringup.swift @@ -0,0 +1,72 @@ +// +// Camera+Bringup.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/30/25. +// + +import AVFoundation +import Foundation +import Photos + +extension Camera { + class Bringup { + /// An existing instance of the ``Camera`` class. + var parent: Camera + + /// Initailizer function for the ``Camera/Bringup`` class. + init( parent: Camera ) { self.parent = parent } + + /** + Returns an array of `AVCaptureDevice` objects to use when attaching cameras to Malachite's `AVCaptureSession`. + + If camera permissions aren't granted, or the current device doesn't have any cameras to add to this array, + this function returns `[]`. + */ + func createCameraArray() -> [AVCaptureDevice] { + if !parent.permissions.cameraGranted { return [] } + parent.utilities.debugNSLog("[Camera Initialization] Discovering available cameras") + var camerasToDiscover: [AVCaptureDevice.DeviceType] = [] + var camerasFound: [AVCaptureDevice] = [] + if #available(iOS 17.0, *) { camerasToDiscover = [.builtInUltraWideCamera, .builtInWideAngleCamera, .builtInTelephotoCamera ] } + else { camerasToDiscover = [.builtInUltraWideCamera, .builtInWideAngleCamera, .builtInTelephotoCamera] } + + let currentProcess = ProcessInfo() + AVCaptureDevice.DiscoverySession.init(deviceTypes: camerasToDiscover, mediaType: .video, position: (currentProcess.isiOSAppOnMac || currentProcess.isMacCatalystApp) ? .unspecified : .back).devices.forEach { device in + if parent.utilities.preferences.compatibility.device.changed { parent.compatibility.checkCameraCapabilities(device: device) } + camerasFound.append(device) + parent.utilities.debugNSLog("[Camera Initialization] \(device.deviceType.rawValue) available") + } + + if !currentProcess.isiOSAppOnMac && !currentProcess.isMacCatalystApp { + AVCaptureDevice.DiscoverySession.init(deviceTypes: camerasToDiscover, mediaType: .video, position: .front).devices.forEach { device in + if parent.utilities.preferences.compatibility.device.changed { parent.compatibility.checkCameraCapabilities(device: device) } + camerasFound.append(device) + parent.utilities.debugNSLog("[Camera Initialization] \(device.deviceType.rawValue) available") + } + } + + return camerasFound + } + + /** + Returns an `AVCapturePhotoOutput` object to use when taking a photo with Malachite's `AVCaptureSession`. + + If photo library permissions aren't granted, this function returns before any `AVCapturePhotoOutput` + initialization occurs. + */ + func addPhotoOutput(session: AVCaptureSession) { + if !parent.permissions.photosGranted { return } + if !session.outputs.contains(parent.output) { + parent.utilities.debugNSLog("[Camera Initialization] Running AVCapturePhotoOutput initialization steps") + if #unavailable(iOS 16.0) { parent.output.isHighResolutionCaptureEnabled = true } + parent.output.maxPhotoQualityPrioritization = .quality + session.sessionPreset = AVCaptureSession.Preset.photo + session.addOutput(parent.output) + } else { + parent.utilities.debugNSLog("[Camera Initialization] AVCapturePhotoOutput already initialized, continuing") + } + } + } +} + diff --git a/mlchtCamera/Utilities/CameraUtils/Camera+Input.swift b/mlchtCamera/Utilities/CameraUtils/Camera+Input.swift new file mode 100644 index 0000000..a83fd08 --- /dev/null +++ b/mlchtCamera/Utilities/CameraUtils/Camera+Input.swift @@ -0,0 +1,165 @@ +// +// Camera+Input.swift +// Malachite +// +// Created by Eva Isabella Luna on 9/11/25. +// + +import AVFoundation +import Foundation + +extension Camera { + class Input { + /// An existing instance of the ``Camera`` class. + var parent: Camera + + /// Initailizer function for the ``Camera/Input`` class. + init( parent: Camera ) { self.parent = parent } + + /** + Changes the current device providing input to ``Camera/session``. + + A quick rundown of this function: + - Checks whether or not there is a camera available for switching using ``selectDeviceForSwitch()`` + - Checks whether or not there is a camera already providing an input to ``Camera.session``, and removing it + - Configures the currently active format on the new camera to the highest quality and resolution possible + - Sends notifications about unsupported features such as focus or exposure with ``handleUnsupportedFeaturesOnSwitch(supported:notification:)`` + - Creates and attaches an input with the ``Camera/device`` object to ``Camera/session`` + */ + @objc func runInputSwitch() { + parent.device = selectDeviceForSwitch() + guard let device = parent.device else { return } + + parent.session.beginConfiguration() + let sessionIsEmpty = parent.session.inputs.isEmpty + parent.utilities.debugNSLog("[Camera Input] Getting ready to configure session") + + if !sessionIsEmpty { + parent.utilities.debugNSLog("[Camera Input] Removing currently active camera input") + parent.session.removeInput(parent.session.inputs[0]) + } + + do { + try device.lockForConfiguration() + defer { device.unlockForConfiguration() } + parent.utilities.debugNSLog("[Camera Input] Selected input: \(String(describing: device.formats[(device.formats.count) - 1]))") + device.activeFormat = (device.formats[(device.formats.count) - 1]) + parent.utilities.function.continuousAEAF(device: device) + + handleUnsupportedFeaturesOnSwitch(supported: device.isLockingFocusWithCustomLensPositionSupported, + notification: MalachiteFunctionUtils.Notifications.unsupportedLensPositionNotification.name) + + handleUnsupportedFeaturesOnSwitch(supported: device.isExposureModeSupported(.custom), + notification: MalachiteFunctionUtils.Notifications.unsupportedISOValueNotification.name) + + self.setHDREnabledOnDevice(device: device) + } catch { + parent.utilities.debugNSLog("[Camera Input] Error adjusting device properties: \(error.localizedDescription)") + } + + + parent.utilities.debugNSLog("[Camera Input] Attempting to attach device input to session") + var input: AVCaptureDeviceInput? + do { input = try AVCaptureDeviceInput(device: device) } + catch { print(error) } + guard let input = input else { return } + + parent.utilities.debugNSLog("[Camera Input] Attached input, finishing configuration") + if parent.session.canAddInput(input) && !parent.session.inputs.contains(input) { parent.session.addInput(input) } + parent.setupPhotoOutput() + if #available(iOS 16.0, *) { switchInputMegapixels(device: device, photoOutput: parent.output) } + parent.session.commitConfiguration() + + if #available(iOS 16.0, *) { + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.megaPixelSwitchNotification.name, object: nil) + } + } + + public func handleUnsupportedFeaturesOnSwitch(supported: Bool, notification: NSNotification.Name) { + if !supported { NotificationCenter.default.post(name: notification, object: nil) } + } + + /** + Returns a new `AVCaptureDevice` object to be used for camera switching. + + > Warning: Don't call this function manually if using ``runInputSwitch()`` - it's already called there. + + > Info: The camera that's chosen depends on how the user initiated this call. If they used the Camera Control on iPhone + 16 and later, this function switches to the specific camera they chose in the overlay. Otherwise, this function simply chooses + the next available camera in ``Camera/cameras``, or the first if the existing camera is at the end of the array. + */ + public func selectDeviceForSwitch() -> AVCaptureDevice? { + if parent.cameras.count > 0 { + if parent.index != nil { return parent.cameras[parent.index!] } else { + if parent.session.inputs.isEmpty { + return parent.cameras.first + } else { + if let devicePosition = parent.cameras.firstIndex(of: parent.device!) { + return parent.cameras[(devicePosition == (parent.cameras.count - 1)) ? 0 : devicePosition + 1] + } + } + } + } + return nil + } + + /** + Enables or disables HDR on the passed `AVCaptureDevice` + + MalachiteKit's HDR implementation is built on Apple's APIs from iOS 14.1 on iPhone 12 and later. The user + is also provided with the facility to disable HDR should they need or want to. + + > Info: Supporting HDR on older iPhone models is being researched and will be available in a future release. + */ + public func setHDREnabledOnDevice(device: AVCaptureDevice) { + parent.compatibility.checkDeviceForHDRCompatibility(device: device) + + parent.utilities.debugNSLog("[Camera Input] Checking if we should enable HDR: supportedByDevice: \(parent.utilities.preferences.compatibility.hdr), enabledInPreferences: \(parent.utilities.preferences.capture.hdr)") + + device.automaticallyAdjustsVideoHDREnabled = false + + if parent.utilities.preferences.compatibility.hdr { device.isVideoHDREnabled = parent.utilities.preferences.capture.hdr } + + parent.utilities.debugNSLog("[Camera Input]" + (device.isVideoHDREnabled ? "Disabling HDR" : " Enabling HDR")) + + if device.activeFormat.isGlobalToneMappingSupported { device.isGlobalToneMappingEnabled = false } + } + + /** + Changes the maxPhotoDimensions property on the passed `AVCapturePhotoOutput`. + + MalachiteKit supports 8MP, 12MP, and 48MP cameras. See Apple's Tech Specs page for information on the capabilities + of specific devices. + */ + @available(iOS 16.0, *) + @objc public func switchInputMegapixels(device: AVCaptureDevice, photoOutput: AVCapturePhotoOutput) { + guard device.position == .back else { return } + + let maxDimensions = device.activeFormat.supportedMaxPhotoDimensions[device.activeFormat.supportedMaxPhotoDimensions.count - 1] + + var mpSetting = Int() + + switch device.deviceType { + case .builtInUltraWideCamera: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.ultrawide + case .builtInWideAngleCamera: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.wideangle + case .builtInTelephotoCamera: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.telephoto + default: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.wideangle + } + + switch mpSetting { + case 48: + parent.utilities.debugNSLog("[Camera Input] Switching \(device.deviceType.rawValue) to 48MP mode") + if maxDimensions.width == 8064 && maxDimensions.height == 6048 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 8064, height: 6048) } + case 12: + parent.utilities.debugNSLog("[Camera Input] Switching \(device.deviceType.rawValue) to 12MP mode") + if maxDimensions.width == 4032 && maxDimensions.height == 3024 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 4032, height: 3024) } + default: + parent.utilities.debugNSLog("[Camera Input] Switching \(device.deviceType.rawValue) to 8MP mode") + } + } + } +} diff --git a/mlchtCamera/Utilities/CameraUtils/Camera+Permissions.swift b/mlchtCamera/Utilities/CameraUtils/Camera+Permissions.swift new file mode 100644 index 0000000..e6f780e --- /dev/null +++ b/mlchtCamera/Utilities/CameraUtils/Camera+Permissions.swift @@ -0,0 +1,81 @@ +// +// Camera+Permissions.swift +// Malachite +// +// Created by Eva Isabella Luna on 9/21/25. +// + +import AVFoundation +import Foundation +import Photos + +extension Camera { + class Permissions { + /// An instance of ``MalachiteClassesObject`` for reuse across the app. + private var utilities: MalachiteClassesObject + + /// Initailizer function for the ``Camera/Permissions`` class. + init( utilities: MalachiteClassesObject ) { self.utilities = utilities } + + /// Whether or not the user has granted permission to use the camera system. + var cameraGranted = false + /// Whether or not the user has granted permission to add to their photo library. + var photosGranted = false + + /// Requests camera and photo library permissions and updates ``cameraGranted`` and ``photosGranted`` + @MainActor + func requestPermissions() async { + let cameraOK = await self.createRequestToUseCamera() + self.cameraGranted = cameraOK + + let photosOK = await self.createRequestToAddPhotos() + self.photosGranted = photosOK + + utilities.debugNSLog("[Camera Permissions] Results — camera=\(cameraOK), photos(add-only)=\(photosOK)") + } + + /// Requests the ability to use the camera. + func createRequestToUseCamera() async -> Bool { + let status = AVCaptureDevice.authorizationStatus(for: .video) + switch status { + case .notDetermined: + let granted = await AVCaptureDevice.requestAccess(for: .video) + utilities.debugNSLog("[Camera Permissions] Camera access requested: granted=\(granted)") + return granted + case .authorized: + utilities.debugNSLog("[Camera Permissions] Camera access already authorized") + return true + case .restricted, .denied: + utilities.debugNSLog("[Camera Permissions] Camera access restricted/denied") + return false + @unknown default: + utilities.debugNSLog("[Camera Permissions] Camera access unknown status: \(status.rawValue)") + return false + } + } + + /// Requests the ability to add photos to the user's library. + func createRequestToAddPhotos() async -> Bool { + let status = PHPhotoLibrary.authorizationStatus(for: .addOnly) + switch status { + case .notDetermined: + let newStatus = await PHPhotoLibrary.requestAuthorization(for: .addOnly) + let granted = (newStatus == .authorized) + utilities.debugNSLog("[Camera Permissions] Photo library (add-only) requested: granted=\(granted)") + return granted + case .authorized: + utilities.debugNSLog("[Camera Permissions] Photo library (add-only) already authorized") + return true + case .limited: + utilities.debugNSLog("[Camera Permissions] Photo library (add-only) limited — treating as not granted") + return false + case .denied, .restricted: + utilities.debugNSLog("[Camera Permissions] Photo library (add-only) restricted/denied") + return false + @unknown default: + utilities.debugNSLog("[Camera Permissions] Photo library (add-only) unknown status: \(status.rawValue)") + return false + } + } + } +} diff --git a/mlchtCamera/Utilities/CameraUtils/Camera.swift b/mlchtCamera/Utilities/CameraUtils/Camera.swift new file mode 100644 index 0000000..078855e --- /dev/null +++ b/mlchtCamera/Utilities/CameraUtils/Camera.swift @@ -0,0 +1,124 @@ +// +// Camera.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/29/25. +// + +import AVFoundation +import Foundation +import Photos + +class Camera: NSObject { + /// An instance of ``MalachiteClassesObject`` for use in this class and its children. + var utilities: MalachiteClassesObject + + /// A DispatchQueue for use in this class, interacting with its objects, and its children. + var queue = DispatchQueue(label: "malachitekit.camera.sessionqueue") + + /// An instance of ``Bringup`` for use in this class. + var bringup: Camera.Bringup! + /// An instance of ``Input`` for use in this class. + var input: Camera.Input! + /// An instance of ``Permissions`` for use in this class. + var permissions: Camera.Permissions! + /// An instance of ``Compatibility`` for use in this class and its children. + var compatibility: Compatibility! + + /** + MalachiteKit's main capture session. + + This object provides the client with an `AVCaptureSession` that serves as the main interface for the application. It is stored + in this variable for later referencing and usage. + */ + var session = AVCaptureSession() + /** + MalachiteKit's main photo output object. + + During initalization of the ``Camera`` class, and after verifying that the application has permissions to use the camera and save + to the user's library, this object is added to ``session`` and stored for later referencing and usage. + */ + var output = AVCapturePhotoOutput() + /** + An array of `AVCaptureDevice` objects used for camera selection and switching. + + During initalization of the ``Camera`` class, and after verifying that the application has permissions to use the camera, this object + will be populated by ``Bringup/createCameraArray()``. + */ + var cameras: [ AVCaptureDevice ]! + + /** + The device currently selected for use, or in use, by ``session``. + + During camera switching in ``Input/runInputSwitch()``, this variable is set to the `AVCaptureDevice` that MalachiteKit + switches to. This variable enables the ability to read and write properties on the current camera device. + + > Warning: Don't replace the `AVCaptureDevice` object on this variable directly, and let ``Input/runInputSwitch()`` handle that action. + Modifying this variable directly can lead to changes being lost when the user switches between cameras. + */ + var device: AVCaptureDevice? + + /** + An integer variable used to store the location of ``device`` in ``cameras``. + + This variable helps to keep the Camera Control in sync with ``CameraView/ControlLayer/camera`` + > Warning: Don't modify this variable directly, as switching cameras with the Camera Control will overwrite its value. + */ + var index: Int? + + /** + Initailizer function for the ``Camera`` class. + + A quick rundown of the initialization happening in this class: + - ``utilities`` is set to the ``MalachiteClassesObject`` passed in the initalizer. + - An instance of the ``Permissions`` class is created to prepare for handling permission requests, and usage inside of children. + - An instance of the ``Compatibility`` class is created for usage inside of children. + + Additionally, the initalizer for the ``Camera`` class has an async task in it: + - Check and wait for the result of ``Permissions/requestPermissions()``. + - If camera permissions are granted, run ``Camera/setupChildren()`` to finish bringing up cameras. + - If photo library permissions are granted, run ``Camera/setupPhotoOutput()`` to finish bringing up the photo output. + */ + init( + utilities: MalachiteClassesObject + ) { + self.utilities = utilities + self.permissions = Permissions(utilities: utilities) + self.compatibility = Compatibility(utilities: utilities) + super.init() + + Task { + @MainActor in await self.permissions.requestPermissions() + if self.permissions.cameraGranted { + setupChildren() + } else { + utilities.debugNSLog("[Permissions] Setup skipped due to no camera access.") + } + if self.permissions.photosGranted { + setupPhotoOutput() + } else { + utilities.debugNSLog("[Permissions] Setup skipped due to no camera access.") + } + } + } + + /** + Sets up child classes required for MalachiteKit's core functionality. + + > Warning: Don't call this function manually, as it's already called when initalizing the ``Camera`` class. + > Info: To avoid race conditions, you can observe ``MalachiteFunctionUtils/Notifications/cameraClassNotification`` + and run code when a notification is posted to it. + */ + public func setupChildren() { + self.bringup = Bringup(parent: self) + self.setupCameraArray() + self.input = Input(parent: self) + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.cameraClassNotification.name, object: nil) + } + + /// Assigns ``cameras`` to the output value of ``Bringup/createCameraArray()`` + public func setupCameraArray() { self.cameras = self.bringup.createCameraArray() } + + /// Assigns ``output`` using ``Bringup/addPhotoOutput(session:)`` + public func setupPhotoOutput() { self.bringup.addPhotoOutput(session: self.session) } +} diff --git a/mlchtCamera/Utilities/CompatibilityUtils/Compatibility.swift b/mlchtCamera/Utilities/CompatibilityUtils/Compatibility.swift new file mode 100644 index 0000000..a34e44d --- /dev/null +++ b/mlchtCamera/Utilities/CompatibilityUtils/Compatibility.swift @@ -0,0 +1,96 @@ +// +// Compatibility.swift +// Malachite +// +// Created by Eva Isabella Luna on 9/21/25. +// + +import AVFoundation +import Foundation + +class Compatibility { + private var utilities: MalachiteClassesObject + + init( utilities: MalachiteClassesObject ) { + self.utilities = utilities + } + + /// Determines if the passed device's activeFormat supports HDR. + public func checkDeviceForHDRCompatibility(device: AVCaptureDevice) { + if utilities.preferences.compatibility.hdr != device.activeFormat.isVideoHDRSupported { + utilities.debugNSLog("[Compatibility] HDR compatibility has changed on the current camera's active format.") + utilities.preferences.compatibility.hdr = device.activeFormat.isVideoHDRSupported + } + } + + /// Checks whether or not the current device is the same device as previously recorded in preferences. + public func isSameDevice() { + if utilities.preferences.compatibility.device.changed { utilities.preferences.compatibility.device.changed = false } + if (utilities.preferences.compatibility.device.model == utilities.preferences.ext.deviceModel()) && + !utilities.preferences.debug.compatibility.forcecheck { + utilities.internalNSLog("[Initialization] This is the same device, can skip compatibility checks.") + return + } + + utilities.internalNSLog("[Initialization] This is a new device, rechecking compatibility.") + utilities.preferences.compatibility.device.model = utilities.preferences.ext.deviceModel() + utilities.preferences.compatibility.device.changed = true + } + + /** + Checks whether or not the current device is capable of encoding High Efficiency Image Format. + + If the device doesn't support HEIC, the option is disabled in preferences to prevent crashes. + + HEIC is supported on Apple devices with the A10 Fusion chip or later. + */ + func checkDeviceForHEICCompatibility() { + if !utilities.preferences.debug.compatibility.forcecheck && + utilities.preferences.compatibility.device.changed { return } + + let supportedTypeIdentifiers = CGImageDestinationCopyTypeIdentifiers() as NSArray + if utilities.preferences.compatibility.jpeg != supportedTypeIdentifiers.contains("public.jpeg") { + utilities.debugNSLog("[Compatibility] JPEG compatibility has changed on this device.") + utilities.preferences.compatibility.jpeg = supportedTypeIdentifiers.contains("public.jpeg") + } + if utilities.preferences.compatibility.heic != supportedTypeIdentifiers.contains("public.heic") { + utilities.debugNSLog("[Compatibility] HEIC compatibility has changed on this device.") + utilities.preferences.compatibility.heic = supportedTypeIdentifiers.contains("public.heic") + } + } + + /// Determines what resolutions that the passed ``AVCaptureDevice`` is capable of shooting. + func checkCameraCapabilities(device: AVCaptureDevice) { + // Front camera resolution control is not supported as of now + // Even the iPhone 17 seems to only go up to 12MP. + guard device.position == .back else { return } + + var tmpDictionary = Dictionary() + for format in device.formats { + var maxDimensions: CMVideoDimensions + if #available (iOS 16.0, *) { + maxDimensions = format.supportedMaxPhotoDimensions[format.supportedMaxPhotoDimensions.count - 1] + } else { + maxDimensions = format.highResolutionStillImageDimensions + } + if format == device.formats[0] { utilities.debugNSLog("[Compatibility] Querying supported modes of \(device.deviceType.rawValue)") } + if maxDimensions.width == 3264 && maxDimensions.height == 2448 { tmpDictionary["8"] = true } + if maxDimensions.width == 4032 && maxDimensions.height == 3024 { tmpDictionary["12"] = true } + if maxDimensions.width == 8064 && maxDimensions.height == 6048 { tmpDictionary["48"] = true + } + } + + switch device.deviceType { + case .builtInUltraWideCamera: + utilities.preferences.compatibility.ultrawide = tmpDictionary + case .builtInWideAngleCamera: + utilities.preferences.compatibility.wideangle = tmpDictionary + case .builtInTelephotoCamera: + utilities.preferences.compatibility.telephoto = tmpDictionary + default: + break + } + + utilities.debugNSLog("[Compatibility] \(device.deviceType.rawValue): \(tmpDictionary)") + } +} diff --git a/mlchtCamera/Utilities/GameUtils/Game+View.swift b/mlchtCamera/Utilities/GameUtils/Game+View.swift new file mode 100644 index 0000000..89f096b --- /dev/null +++ b/mlchtCamera/Utilities/GameUtils/Game+View.swift @@ -0,0 +1,27 @@ +// +// Game+View.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/29/25. +// + +import Foundation +import UIKit + +extension MalachiteGameUtils { + func setupGameKitAlert() -> UIAlertController { + let alert = UIAlertController(title: "alert.title.gamekit".localized, message: "alert.detail.gamekit".localized, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "alert.button.reopen".localized, style: .default, handler: { _ in exit(11) })) + alert.addAction(UIAlertAction(title: "alert.button.report".localized, style: .default, handler: { _ in + guard let url = URL(string: "https://www.youtube.com/watch?v=At8v_Yc044Y") else { return } +#if MAIN_APP + UIApplication.shared.open(url, options: [:], completionHandler: nil) +#endif + })) + alert.addAction(UIAlertAction(title: "alert.button.ignore".localized, style: .default, handler: { _ in + MalachitePreferencesUtils.shared.preferences.general.gamekit.alerted = false + })) + + return alert + } +} diff --git a/Malachite/Utilities/MalachiteGameUtils.swift b/mlchtCamera/Utilities/GameUtils/MalachiteGameUtils.swift similarity index 94% rename from Malachite/Utilities/MalachiteGameUtils.swift rename to mlchtCamera/Utilities/GameUtils/MalachiteGameUtils.swift index f29f573..6b181bc 100755 --- a/Malachite/Utilities/MalachiteGameUtils.swift +++ b/mlchtCamera/Utilities/GameUtils/MalachiteGameUtils.swift @@ -41,6 +41,15 @@ public class MalachiteGameUtils : NSObject, GKGameCenterControllerDelegate { leaderboards.loadLeaderboards() } } + + /// Function to change the GameKit enabled state. + @objc func changeGameCenterEnabled() { + DispatchQueue.global(qos: .background).async { [self] in + if MalachitePreferencesUtils.shared.preferences.general.gamekit.enabled { + setupGameCenter() + } + } + } } public class MalachiteGameAchievementUtils : NSObject { diff --git a/mlchtCamera/Utilities/InitUtils/Init+Debug.swift b/mlchtCamera/Utilities/InitUtils/Init+Debug.swift new file mode 100644 index 0000000..e36f1c3 --- /dev/null +++ b/mlchtCamera/Utilities/InitUtils/Init+Debug.swift @@ -0,0 +1,24 @@ +// +// Init+Debug.swift +// Malachite +// +// Created by Eva Isabella Luna on 9/4/25. +// + +extension Init { + class Debug { + private var utilities: MalachiteClassesObject + + init( utilities: MalachiteClassesObject ) { self.utilities = utilities } + + /// Prints the contents of preferences.plist at app launch. + public func printPreferences() { + if utilities.preferences.debug.logging.preferences { MalachitePreferencesUtils().printPreferences() } + } + + /// Runs all of the initialization functions defined in this class. + public func initMalachite() { + printPreferences() + } + } +} diff --git a/mlchtCamera/Utilities/InitUtils/Init+Internal.swift b/mlchtCamera/Utilities/InitUtils/Init+Internal.swift new file mode 100644 index 0000000..0b3529e --- /dev/null +++ b/mlchtCamera/Utilities/InitUtils/Init+Internal.swift @@ -0,0 +1,25 @@ +// +// Init+Internal.swift +// Malachite +// +// Created by Eva Isabella Luna on 9/4/25. +// + +extension Init { + class Internal { + private var utilities: MalachiteClassesObject + + init( utilities: MalachiteClassesObject ) { self.utilities = utilities } + + /// Checks whether or not to print a small message about subscribing to Eva's Patreon in the logs. + public func isEvaBuild() { + if utilities.versionUser == "evaluna" || utilities.versionHost == "Xcode Cloud" { return } + utilities.internalNSLog("[Initialization] plz subscribe to patreon: https://patreon.com/crystall1nedev") + } + + /// Runs all of the initialization functions defined in this class. + public func initMalachite() { + isEvaBuild() + } + } +} diff --git a/mlchtCamera/Utilities/InitUtils/Init.swift b/mlchtCamera/Utilities/InitUtils/Init.swift new file mode 100644 index 0000000..fcf17ec --- /dev/null +++ b/mlchtCamera/Utilities/InitUtils/Init.swift @@ -0,0 +1,59 @@ +// +// Init.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/31/25. +// + +import AVFoundation +import Foundation + +class Init { + private var utilities: MalachiteClassesObject + private var debug: Init.Debug + private var intrnl: Init.Internal + private var compatibility: Compatibility + + init( utilities: MalachiteClassesObject ) { + self.utilities = utilities + self.debug = Debug(utilities: utilities) + self.intrnl = Internal(utilities: utilities) + self.compatibility = Compatibility(utilities: utilities) + } + + /// Prints a message about Malachite starting. + public func startupLog() { utilities.debugNSLog("[Initialization] Starting up Malachite") } + + /// Prints a message about what build type the current installation of Malachite was compiled with. + public func versionTypeCheck() { + if utilities.versionType == "INTERNAL" { + utilities.internalNSLog("[Initialization] Running an INTERNAL build") + } else if utilities.versionType == "DEBUG" { + utilities.debugNSLog("[Initialization] Running a DEBUG build") + } else if utilities.versionType == "RELEASE" { + utilities.NSLog("[Initialization] Running a RELEASE build") + } + } + + /// Checks whether or not Malachite is initializing from the main app or an App Extension. + public func appExtensionCheck() { +#if APP_EXTENSION + utilities.debugNSLog("[Initialization] Running out of an app extension.") +#elseif MAIN_APP + utilities.debugNSLog("[Initialization] Running out of the main app.") +#endif + } + + /// Runs all of the initialization functions defined in this class. + public func initMalachite() { + startupLog() + versionTypeCheck() + appExtensionCheck() + + if utilities.versionType == "DEBUG" || utilities.versionType == "INTERNAL" { debug.initMalachite() } + if utilities.versionType == "INTERNAL" { intrnl.initMalachite() } + + self.compatibility.isSameDevice() + self.compatibility.checkDeviceForHEICCompatibility() + } +} diff --git a/mlchtCamera/Utilities/LocationUtils/Location.swift b/mlchtCamera/Utilities/LocationUtils/Location.swift new file mode 100644 index 0000000..2103535 --- /dev/null +++ b/mlchtCamera/Utilities/LocationUtils/Location.swift @@ -0,0 +1,35 @@ +// +// Location.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/17/26. +// + +import CoreLocation + +class Location: NSObject, CLLocationManagerDelegate { + /// An instance of ``MalachiteClassesObject`` for use in this class and its children. + var utilities: MalachiteClassesObject + + public var location = CLLocationManager() + + public var locationEnabled: Bool { + get { return location.authorizationStatus == .authorizedAlways || location.authorizationStatus == .authorizedWhenInUse } + } + + init( + utilities: MalachiteClassesObject + ) { + self.utilities = utilities + super.init() + + self.location.delegate = self + self.location.desiredAccuracy = kCLLocationAccuracyBest + self.location.requestWhenInUseAuthorization() + } + + public func startLocationServices() { + self.location.startUpdatingLocation() + self.location.startUpdatingHeading() + } +} diff --git a/Malachite/Utilities/MalachiteFunctionUtils.swift b/mlchtCamera/Utilities/MalachiteFunctionUtils.swift similarity index 59% rename from Malachite/Utilities/MalachiteFunctionUtils.swift rename to mlchtCamera/Utilities/MalachiteFunctionUtils.swift index 5cd61ae..260318c 100755 --- a/Malachite/Utilities/MalachiteFunctionUtils.swift +++ b/mlchtCamera/Utilities/MalachiteFunctionUtils.swift @@ -13,11 +13,10 @@ import UIKit public class MalachiteFunctionUtils : NSObject { /// An array that returns the available image capture types supported by the camera. private let supportedImageCaptureTypes = CGImageDestinationCopyTypeIdentifiers() as NSArray - /// A `Bool` that determines whether or not the device supports HDR. - public var supportsHDR = false /// An `enum` that contains Notification names. public enum Notifications: String, NotificationName { + case cameraClassNotification case aspectFillNotification case exposureLimitNotification case stabilizerNotification @@ -31,27 +30,15 @@ public class MalachiteFunctionUtils : NSObject { case settingsGestureNotification } - /// Function that determines if the device supports HDR. - public func deviceFormatSupportsHDR(device hdrDevice: AVCaptureDevice) { - if hdrDevice.activeFormat.isVideoHDRSupported == true { - MalachitePreferencesUtils.shared.preferences.compatibility.hdr = true - self.supportsHDR = true - } - } - - /// Function that determines if the device supports HEIC. - public func supportsHEIC() -> Bool { - MalachitePreferencesUtils.shared.preferences.compatibility.jpeg = true - if supportedImageCaptureTypes.contains("public.heic") { - MalachitePreferencesUtils.shared.preferences.compatibility.heic = true - return true - } - - return false + /// Function to enable or disable the idle timer. + @objc func changeIdleTimerState() { + #if MAIN_APP + UIApplication.shared.isIdleTimerDisabled = MalachitePreferencesUtils.shared.preferences.userInterface.idleTimerDisabled + #endif } /// Function that handles pinch to zoom. - public func zoom(sender pinch: UIPinchGestureRecognizer, floater float: CGFloat, captureDevice device: inout AVCaptureDevice, lastZoomFactor zoomFactor: inout CGFloat, hapticClass haptic: MalachiteHapticUtils) { + public func zoom(sender pinch: UIPinchGestureRecognizer, floater float: inout CGFloat, captureDevice device: AVCaptureDevice, lastZoomFactor zoomFactor: inout CGFloat, hapticClass haptic: MalachiteHapticUtils) { func minMaxZoom(_ factor: CGFloat) -> CGFloat { return min(min(max(factor, 1.0), CGFloat(MalachitePreferencesUtils.shared.preferences.capture.maximumZoom)), device.activeFormat.videoMaxZoomFactor) } @@ -67,23 +54,31 @@ public class MalachiteFunctionUtils : NSObject { } } - update(scale: minMaxZoom(float * zoomFactor)) + MalachiteClassesObject().debugNSLog("[Zoom] Pinch scale: \(pinch.scale), float: \(float), zoomFactor: \(zoomFactor)") + if pinch.scale != 1.0 { float = pinch.scale * zoomFactor } + let newScaleFactor = minMaxZoom(float) + update(scale: newScaleFactor) switch pinch.state { case .began: haptic.triggerMediumHaptic() fallthrough case .changed: - update(scale: minMaxZoom(float * zoomFactor)) + let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor) + float = minMaxZoom(newScaleFactor) + update(scale: newScaleFactor) case .ended: - update(scale: minMaxZoom(float * zoomFactor)) + let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor) + zoomFactor = minMaxZoom(newScaleFactor) + float = minMaxZoom(newScaleFactor) + update(scale: zoomFactor) haptic.triggerMediumHaptic() default: break } } /// Function that handles autofocus and autoexposure - public func pointOfInterestAEAF(sender: UILongPressGestureRecognizer, captureDevice device: inout AVCaptureDevice, button: UIButton, viewForScale view: UIView, hapticClass haptic: MalachiteHapticUtils) { + public func pointOfInterestAEAF(sender: UILongPressGestureRecognizer, captureDevice device: AVCaptureDevice, button: UIButton, viewForScale view: UIView, hapticClass haptic: MalachiteHapticUtils) { let point = sender.location(in: view) if sender.state == UIGestureRecognizer.State.began { haptic.triggerNotificationHaptic(type: .success) @@ -174,7 +169,7 @@ public class MalachiteFunctionUtils : NSObject { } /// Function that handles toggling the flashlight's on state. - public func toggleFlash(captureDevice device: inout AVCaptureDevice, flashlightButton button: UIButton, floater float: Float?, isFlashOn: inout Bool) { + public func toggleFlash(captureDevice device: AVCaptureDevice, flashlightButton button: UIButton, floater float: Float?, isFlashOn: inout Bool) { if device.hasTorch { var buttonImage = UIImage() do { @@ -224,110 +219,8 @@ public class MalachiteFunctionUtils : NSObject { } } - /// Function that handles connecting and disconnecting cameras, and changing format properties. - public func switchInput(session: inout AVCaptureSession, cameras: [AVCaptureDevice], device: inout AVCaptureDevice?, output: inout AVCapturePhotoOutput, input: inout AVCaptureDeviceInput?, button: UIButton, firstRun: inout Bool){ - DispatchQueue.main.async { button.isUserInteractionEnabled = false } - MalachiteClassesObject().debugNSLog("[Camera Input] Getting ready to configure session") - - if !firstRun { - MalachiteClassesObject().debugNSLog("[Camera Input] Removing currently active camera input") - session.removeInput(input!) - } else { - if !cameras.isEmpty { device = cameras.first } - } - - if firstRun { - for camera in cameras { - var tmpDictionary = Dictionary() - for format in camera.formats { - var maxDimensions: CMVideoDimensions - if #available (iOS 16.0, *) { - maxDimensions = format.supportedMaxPhotoDimensions[format.supportedMaxPhotoDimensions.count - 1] - } else { - maxDimensions = format.highResolutionStillImageDimensions - } - if format == camera.formats[0] { MalachiteClassesObject().debugNSLog("[Camera Input] Querying supported modes of \(camera.deviceType.rawValue)") } - if maxDimensions.width == 3264 && maxDimensions.height == 2448 { tmpDictionary["8"] = true } - if maxDimensions.width == 4032 && maxDimensions.height == 3024 { tmpDictionary["12"] = true } - if maxDimensions.width == 8064 && maxDimensions.height == 6048 { tmpDictionary["48"] = true } - switch camera.deviceType { - case .builtInUltraWideCamera: - MalachitePreferencesUtils.shared.preferences.compatibility.ultrawide = tmpDictionary - case .builtInWideAngleCamera: - MalachitePreferencesUtils.shared.preferences.compatibility.wideangle = tmpDictionary - case .builtInTelephotoCamera: - MalachitePreferencesUtils.shared.preferences.compatibility.telephoto = tmpDictionary - default: - break - } - } - } - } - - firstRun = false - - deviceFormatSupportsHDR(device: device!) - - do { - try device?.lockForConfiguration() - defer { device?.unlockForConfiguration() } - MalachiteClassesObject().debugNSLog("[Camera Input] Selected input: \(String(describing: device?.formats[(device?.formats.count)! - 1]))") - device?.activeFormat = (device?.formats[(device?.formats.count)! - 1])! - continuousAEAF(device: device!) - - guard let focus = device?.isLockingFocusWithCustomLensPositionSupported else { return } - if !focus { NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.unsupportedLensPositionNotification.name, object: nil) } - - guard let exposure = device?.isExposureModeSupported(.custom) else { return } - if !exposure { NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.unsupportedISOValueNotification.name, object: nil) } - - device?.automaticallyAdjustsVideoHDREnabled = false - - if MalachiteClassesObject().preferences.capture.hdr { - if self.supportsHDR { - MalachiteClassesObject().debugNSLog("[Camera Input] Force enabled HDR on camera") - if device?.activeFormat.isVideoHDRSupported == true { - device?.isVideoHDREnabled = true - } else { - MalachiteClassesObject().debugNSLog("[Camera Input] Current capture mode doesn't support HDR, it needs to be disabled") - MalachiteClassesObject().preferences.capture.hdr = false - } - } else { - MalachiteClassesObject().debugNSLog("[Camera Input] HDR enabled on a device that doesn't support it") - MalachiteClassesObject().preferences.capture.hdr = false - } - } else { - MalachiteClassesObject().debugNSLog("[Camera Input] Force disabled HDR on camera") - if device?.activeFormat.isGlobalToneMappingSupported == true { - device?.isGlobalToneMappingEnabled = false - } - if device?.activeFormat.isVideoHDRSupported == true { - device?.isVideoHDREnabled = false - } - } - } catch { - MalachiteClassesObject().debugNSLog("[Camera Input] Error adjusting device properties: \(error.localizedDescription)") - } - - - MalachiteClassesObject().debugNSLog("[Camera Input] Attempting to attach device input to session") - do { input = try AVCaptureDeviceInput(device: device!) } - catch { - print(error) - } - - MalachiteClassesObject().debugNSLog("[Camera Input] Attached input, finishing configuration") - if session.canAddInput(input!) { session.addInput(input!) } - switchInputMegapixels(device: device!, photoOutput: output) - if !Thread.isMainThread { - DispatchQueue.main.async { button.isUserInteractionEnabled = true } - } else { - button.isUserInteractionEnabled = true - } - } - @available(iOS 18.0, *) - public func addControlsToSession(session: inout AVCaptureSession, controls: [AVCaptureControl]) { + public func addControlsToSession(session: AVCaptureSession, controls: [AVCaptureControl]) { guard session.supportsControls else { return } session.beginConfiguration() @@ -346,42 +239,41 @@ public class MalachiteFunctionUtils : NSObject { session.commitConfiguration() } + @available(iOS 16.0, *) @objc public func switchInputMegapixels(device: AVCaptureDevice, photoOutput: AVCapturePhotoOutput) { - if #available(iOS 16.0, *) { - let maxDimensions = device.activeFormat.supportedMaxPhotoDimensions[device.activeFormat.supportedMaxPhotoDimensions.count - 1] - - var mpSetting = Int() - - switch device.deviceType { - case .builtInUltraWideCamera: - mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.ultrawide - case .builtInWideAngleCamera: - mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.wideangle - case .builtInTelephotoCamera: - mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.telephoto - default: - mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.wideangle - } - - switch mpSetting { - case 48: - MalachiteClassesObject().debugNSLog("[INTERNAL] Switching \(device.deviceType.rawValue) to 48MP mode") - if maxDimensions.width == 8064 && maxDimensions.height == 6048 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 8064, height: 6048) } - case 12: - MalachiteClassesObject().debugNSLog("[INTERNAL] Switching \(device.deviceType.rawValue) to 12MP mode") - if maxDimensions.width == 4032 && maxDimensions.height == 3024 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 4032, height: 3024) } - default: - MalachiteClassesObject().debugNSLog("[INTERNAL] Switching \(device.deviceType.rawValue) to 8MP mode") - if maxDimensions.width == 3264 && maxDimensions.height == 2448 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 3264, height: 2448) } - } + let maxDimensions = device.activeFormat.supportedMaxPhotoDimensions[device.activeFormat.supportedMaxPhotoDimensions.count - 1] + + var mpSetting = Int() + + switch device.deviceType { + case .builtInUltraWideCamera: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.ultrawide + case .builtInWideAngleCamera: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.wideangle + case .builtInTelephotoCamera: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.telephoto + default: + mpSetting = MalachitePreferencesUtils.shared.preferences.capture.mp.wideangle } + switch mpSetting { + case 48: + MalachiteClassesObject().debugNSLog("[INTERNAL] Switching \(device.deviceType.rawValue) to 48MP mode") + if maxDimensions.width == 8064 && maxDimensions.height == 6048 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 8064, height: 6048) } + case 12: + MalachiteClassesObject().debugNSLog("[INTERNAL] Switching \(device.deviceType.rawValue) to 12MP mode") + if maxDimensions.width == 4032 && maxDimensions.height == 3024 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 4032, height: 3024) } + default: + MalachiteClassesObject().debugNSLog("[INTERNAL] Switching \(device.deviceType.rawValue) to 8MP mode") + if maxDimensions.width == 3264 && maxDimensions.height == 2448 { photoOutput.maxPhotoDimensions = CMVideoDimensions(width: 3264, height: 2448) } + } } /// Function that handles taking images on `AVCapturePhotoOutput`. public func captureImage(output photoOutput: AVCapturePhotoOutput, viewForBounds view: UIView, captureDelegate delegate: AVCapturePhotoCaptureDelegate) -> AVCapturePhotoOutput { + if photoOutput.connections.count < 1 { return photoOutput } var format = [String: Any]() - if MalachiteClassesObject().preferences.compatibility.heic && supportsHEIC() { + if MalachiteClassesObject().preferences.compatibility.heic { format = [AVVideoCodecKey : AVVideoCodecType.hevc] } else { format = [AVVideoCodecKey : AVVideoCodecType.jpeg] @@ -407,7 +299,7 @@ public class MalachiteFunctionUtils : NSObject { } /// Function that handles manual focus. - public func manualFocus(captureDevice device: inout AVCaptureDevice, sender: UISlider, floater float: Float) { + public func manualFocus(captureDevice device: AVCaptureDevice, sender: UISlider, floater float: Float) { do { try device.lockForConfiguration() } catch { @@ -421,7 +313,7 @@ public class MalachiteFunctionUtils : NSObject { } /// Function that handles manual ISO. - public func manualExposure(captureDevice device: inout AVCaptureDevice, sender: UISlider) { + public func manualExposure(captureDevice device: AVCaptureDevice, sender: UISlider) { let minISO = device.activeFormat.minISO let maxISO = device.activeFormat.maxISO diff --git a/Malachite/Utilities/MalachiteHapticUtils.swift b/mlchtCamera/Utilities/MalachiteHapticUtils.swift similarity index 100% rename from Malachite/Utilities/MalachiteHapticUtils.swift rename to mlchtCamera/Utilities/MalachiteHapticUtils.swift diff --git a/Malachite/Utilities/MalachiteIntentUtils.swift b/mlchtCamera/Utilities/MalachiteIntentUtils.swift similarity index 88% rename from Malachite/Utilities/MalachiteIntentUtils.swift rename to mlchtCamera/Utilities/MalachiteIntentUtils.swift index 124e6c4..1c59c62 100755 --- a/Malachite/Utilities/MalachiteIntentUtils.swift +++ b/mlchtCamera/Utilities/MalachiteIntentUtils.swift @@ -21,7 +21,7 @@ struct MalachiteLaunchIntent: AppIntent { #if os(iOS) @available(iOS 18.0, *) struct MalachiteCaptureIntent: CameraCaptureIntent { - typealias AppContext = MalachitePreferences + typealias AppContext = MalachiteContext static let title: LocalizedStringResource = "appname.open" static let description = IntentDescription("appname.open.description") @@ -31,4 +31,9 @@ struct MalachiteCaptureIntent: CameraCaptureIntent { return .result() } } + +@available(iOS 18.0, *) +struct MalachiteContext: Codable { + // TODO +} #endif diff --git a/Malachite/Utilities/MalachiteTooltipUtils.swift b/mlchtCamera/Utilities/MalachiteTooltipUtils.swift similarity index 100% rename from Malachite/Utilities/MalachiteTooltipUtils.swift rename to mlchtCamera/Utilities/MalachiteTooltipUtils.swift diff --git a/Malachite/Utilities/MalachiteUtils.swift b/mlchtCamera/Utilities/MalachiteUtils.swift similarity index 74% rename from Malachite/Utilities/MalachiteUtils.swift rename to mlchtCamera/Utilities/MalachiteUtils.swift index 31a704a..f052be5 100755 --- a/Malachite/Utilities/MalachiteUtils.swift +++ b/mlchtCamera/Utilities/MalachiteUtils.swift @@ -27,9 +27,11 @@ public class MalachiteClassesObject : NSObject { public let tooltips = MalachiteTooltipUtils() /// An instance of ``MalachiteGameUtils`` public let games = MalachiteGameUtils() + /// An instance of ``Watch`` + public let watch = Watch() /// Private static storage for the session queue. - @available(iOS 17.0, *) + @available(iOS 18.0, *) private static var _sessionQueue: DispatchSerialQueue = DispatchSerialQueue(label: "dev.crystll1ne.Malachite.controlsSessionQueue") /// A session queue used to absorb Camera Control states. @available(iOS 18.0, *) @@ -45,6 +47,8 @@ public class MalachiteClassesObject : NSObject { public let versionFixer = "0" /// A variable that can be used to pull the git commit hash from the Info.plist public let versionHash = Bundle.main.object(forInfoDictionaryKey: "CFBuildHash") as? String ?? "undefined" + /// A variable that can be used to pull the git branch from the Info.plist + public let versionBranch = Bundle.main.object(forInfoDictionaryKey: "CFBuildBran") as? String ?? "undefined" /// A variable that can be used to pull the build time from the Info.plist public let versionDate = Bundle.main.object(forInfoDictionaryKey: "CFBuildDate") as? String ?? "undefined" /// A variable that can be used to identify the variant of the build from the Info.plist @@ -59,21 +63,22 @@ public class MalachiteClassesObject : NSObject { /// A function to only log in INTERNAL builds public func internalNSLog(_ format: String, file: String = #file, line: Int = #line, function: String = #function) { - if self.versionType == "INTERNAL" { - Foundation.NSLog("[\(file):\(line)] [\(function)] [INTERNAL] \(format)") - } + if self.versionType == "INTERNAL" { Foundation.NSLog("[\(file):\(line)] [\(function)] \(format)") } } - /// A function to only log in DEBUG and INTERNAL builds + /// A function that calls ``debugNSLog`` on DEBUG and ``internalNSLog`` on INTERNAL. public func debugNSLog(_ format: String, file: String = #file, line: Int = #line, function: String = #function) { - if self.versionType == "DEBUG" || self.versionType == "INTERNAL" { + if self.versionType == "INTERNAL" { self.internalNSLog(format, file: file, line: line, function: function) } + else if (self.versionType == "DEBUG" && self.preferences.debug.logging.unified) { Foundation.NSLog("[\(NSString(string: file).lastPathComponent):\(line)] [\(function)] \(format)") } } - /// Literally just regular NSLog, here for consistency - public func NSLog(_ format: String) { - Foundation.NSLog(format) + /// A function that calls regular NSLog on RELEASE, ``debugNSLog`` on DEBUG, and ``internalNSLog`` on INTERNAL. + public func NSLog(_ format: String, file: String = #file, line: Int = #line, function: String = #function) { + if self.versionType == "INTERNAL" { self.internalNSLog(format, file: file, line: line, function: function) } + else if self.versionType == "DEBUG" { self.debugNSLog(format, file: file, line: line, function: function) } + else { Foundation.NSLog(format) } } } diff --git a/Malachite/Utilities/Preferences/MalachitePreferences.swift b/mlchtCamera/Utilities/PreferenceUtils/MalachitePreferences.swift similarity index 50% rename from Malachite/Utilities/Preferences/MalachitePreferences.swift rename to mlchtCamera/Utilities/PreferenceUtils/MalachitePreferences.swift index eb12f11..f2a077c 100755 --- a/Malachite/Utilities/Preferences/MalachitePreferences.swift +++ b/mlchtCamera/Utilities/PreferenceUtils/MalachitePreferences.swift @@ -7,7 +7,6 @@ import Foundation import AppIntents -// TODO: Rename a bunch of these preferences to be more concise in their meaning struct MalachitePreferences_AppContext: Codable, IntentResult { var value: Never? @@ -20,6 +19,7 @@ struct MalachitePreferences: Codable { var compatibility: compatibilityPreferences struct compatibilityPreferences: Codable { + var device: devicePreferences var ultrawide: [ String : Bool ] var wideangle: [ String : Bool ] var telephoto: [ String : Bool ] @@ -28,17 +28,20 @@ struct MalachitePreferences: Codable { var raw: Bool var proraw: Bool var hdr: Bool + + struct devicePreferences: Codable { + var model: String + var changed: Bool + } } var general: generalPreferences struct generalPreferences: Codable { - var version: String - var prefsVersion: Int - var firstLaunch: Bool - var deviceModel: String - var photoCount: Int - var gamekit: gamekitPreferences + var version: String + var firstLaunch: Bool + var photoCount: Int + var gamekit: gamekitPreferences struct gamekitPreferences: Codable { var alerted: Bool @@ -99,79 +102,28 @@ struct MalachitePreferences: Codable { var debug: debugPreferences struct debugPreferences: Codable { + var compatibility: debug_compatibilityPreferences var logging: debug_loggingPreferences var breakApp: Bool struct debug_loggingPreferences: Codable { var preferences: Bool + var unified: Bool + var imageProps: Bool + } + + struct debug_compatibilityPreferences: Codable { + var forcecheck: Bool } } var evaintrnl: evaintrnlPreferences struct evaintrnlPreferences: Codable { + var blockAccidentalGestures: Bool var settingsGesture: Int + var cameraControlEnabled: Bool + var cameraControlOptions: [ String ] + var locationEnabled: Bool } } - -extension MalachitePreferences { - - var ext: Utils { return Utils() } - class Utils { - var gameKitButton = 0 - /// Shows the GameKit enable switch in About settings. - public func showGameKitOptionInAbout(in preferences: inout MalachitePreferences) -> Void { - MalachiteClassesObject().debugNSLog("04F807A163D50211A2456C3460EACFACCBC5BF436AFC268F0DBAA529") - if gameKitButton < 7 { - gameKitButton += 1 - } else { - preferences.general.gamekit.alerted = true - exit(11) - } - } - var dictionary: ELDictionary { return ELDictionary() } - class ELDictionary { - public func isValid(dictionary: Dictionary) -> Bool { - return dictionary["invalid"] as? Bool == false ? false : true - } - - public func getCount(dictionary: Dictionary) -> Int { - return dictionary.count - } - } - var deviceModel: DeviceModel { return DeviceModel() } - class DeviceModel { - public func get() -> String { - var systemInfo = utsname() - uname(&systemInfo) - let machineMirror = Mirror(reflecting: systemInfo.machine) - let identifier = machineMirror.children.reduce("") { identifier, element in - guard let value = element.value as? Int8, value != 0 else { return identifier } - return identifier + String(UnicodeScalar(UInt8(value))) - } - - return identifier - } - - public func isSameDevice(in preferences: inout MalachitePreferences) -> Bool { - if get() == preferences.general.deviceModel { return true } - return false - } - } - - public func runPhotoCounter() { - let value = MalachiteClassesObject().preferences.general.photoCount - if value < UINT64_MAX { // I still want to see someone reach this - MalachiteClassesObject().preferences.general.photoCount += 1 - } else { - MalachiteClassesObject().debugNSLog("[Preferences] what") - MalachiteClassesObject().preferences.general.photoCount = 0 - } - } - - public func resetPreferences() { - if MalachitePreferencesUtils().writePreferences(MalachitePreferencesUtils().initPreferences()) { print("[Preferences] Successfully wiped preferences. Relaunch to ensure.") } - } - } -} - diff --git a/Malachite/Utilities/Preferences/MalachitePreferencesUtils.swift b/mlchtCamera/Utilities/PreferenceUtils/MalachitePreferencesUtils.swift similarity index 83% rename from Malachite/Utilities/Preferences/MalachitePreferencesUtils.swift rename to mlchtCamera/Utilities/PreferenceUtils/MalachitePreferencesUtils.swift index 90641f0..72d21cc 100755 --- a/Malachite/Utilities/Preferences/MalachitePreferencesUtils.swift +++ b/mlchtCamera/Utilities/PreferenceUtils/MalachitePreferencesUtils.swift @@ -91,6 +91,8 @@ class MalachitePreferencesUtils { var currentPreferences = initPreferences() if let compatibilityPreferences = oldPreferences["compatibility"] as? [ String: AnyObject ] { + currentPreferences.compatibility.device.model = compatibilityPreferences["device"]?["model"] as? String ?? "Eva1,1" + currentPreferences.compatibility.device.changed = compatibilityPreferences["device"]?["changed"] as? Bool ?? false currentPreferences.compatibility.ultrawide = compatibilityPreferences["ultrawide"] as? [ String: Bool ] ?? [ "invalid" : false] currentPreferences.compatibility.wideangle = compatibilityPreferences["wideangle"] as? [ String: Bool ] ?? [ "invalid" : false] currentPreferences.compatibility.telephoto = compatibilityPreferences["telephoto"] as? [ String: Bool ] ?? [ "invalid" : false] @@ -103,7 +105,6 @@ class MalachitePreferencesUtils { if let generalPreferences = oldPreferences["general"] as? [ String: AnyObject ] { currentPreferences.general.firstLaunch = generalPreferences["firstLaunch"] as? Bool ?? false - currentPreferences.general.deviceModel = generalPreferences["deviceModel"] as? String ?? "Eva1,1" currentPreferences.general.photoCount = generalPreferences["photoCount"] as? Int ?? 0 currentPreferences.general.gamekit.alerted = generalPreferences["gamekit"]?["alerted"] as? Bool ?? false currentPreferences.general.gamekit.found = generalPreferences["gamekit"]?["found"] as? Bool ?? false @@ -113,7 +114,7 @@ class MalachitePreferencesUtils { if let previewPreferences = oldPreferences["preview"] as? [ String: AnyObject ] { currentPreferences.preview.aspect = previewPreferences["aspect"] as? Bool ?? false currentPreferences.preview.stablize = previewPreferences["stabilize"] as? Bool ?? false - currentPreferences.preview.fastPath = true + currentPreferences.preview.fastPath = previewPreferences["fastPath"] as? Bool ?? true } if let capturePreferences = oldPreferences["capture"] as? [ String: AnyObject ] { @@ -146,11 +147,18 @@ class MalachitePreferencesUtils { if let debugPreferences = oldPreferences["debug"] as? [ String: AnyObject ] { currentPreferences.debug.logging.preferences = debugPreferences["logging"]?["preferences"] as? Bool ?? false - currentPreferences.debug.breakApp = false + currentPreferences.debug.logging.unified = debugPreferences["logging"]?["unified"] as? Bool ?? true + currentPreferences.debug.logging.imageProps = debugPreferences["logging"]?["imageProps"] as? Bool ?? false + currentPreferences.debug.compatibility.forcecheck = debugPreferences["compatibility"]?["forcecheck"] as? Bool ?? false + currentPreferences.debug.breakApp = debugPreferences["breakApp"] as? Bool ?? false } if let evaintrnlPreferences = oldPreferences["evaintrnl"] as? [ String: AnyObject ] { + currentPreferences.evaintrnl.blockAccidentalGestures = evaintrnlPreferences["blockAccidentalGestures"] as? Bool ?? true currentPreferences.evaintrnl.settingsGesture = evaintrnlPreferences["settingsGesture"] as? Int ?? 2 + currentPreferences.evaintrnl.cameraControlEnabled = evaintrnlPreferences["cameraControlEnabled"] as? Bool ?? true + currentPreferences.evaintrnl.cameraControlOptions = evaintrnlPreferences["cameraControlOptions"] as? [ String ] ?? [ "zoom", "focus", "cameras", "flash", "flashLevel", "exposureBias" ] + currentPreferences.evaintrnl.locationEnabled = evaintrnlPreferences["locationEnabled"] as? Bool ?? false } return currentPreferences @@ -159,6 +167,9 @@ class MalachitePreferencesUtils { func initPreferences() -> MalachitePreferences { return MalachitePreferences( compatibility: MalachitePreferences.compatibilityPreferences( + device: MalachitePreferences.compatibilityPreferences.devicePreferences( + model: "Eva1,1", + changed: false), ultrawide: [ "invalid" : false ], wideangle: [ "invalid" : false ], telephoto: [ "invalid" : false ], @@ -170,9 +181,7 @@ class MalachitePreferencesUtils { ), general: MalachitePreferences.generalPreferences( version: Bundle.main.infoDictionary?["CFBundleVersion"] as! String, - prefsVersion: 6, firstLaunch: false, - deviceModel: "Eva1,1", photoCount: 0, gamekit: MalachitePreferences.generalPreferences.gamekitPreferences( alerted: false, @@ -214,13 +223,22 @@ class MalachitePreferencesUtils { hapticFeedback: false ), debug: MalachitePreferences.debugPreferences( + compatibility: MalachitePreferences.debugPreferences.debug_compatibilityPreferences( + forcecheck: false + ), logging: MalachitePreferences.debugPreferences.debug_loggingPreferences( - preferences: false + preferences: false, + unified: true, + imageProps: false, ), breakApp: false ), evaintrnl: MalachitePreferences.evaintrnlPreferences( - settingsGesture: 2 + blockAccidentalGestures: true, + settingsGesture: 2, + cameraControlEnabled: true, + cameraControlOptions: [ "zoom", "focus", "cameras", "flash", "flashLevel", "exposureBias" ], + locationEnabled: false ) ) } diff --git a/mlchtCamera/Utilities/PreferenceUtils/Preferences+Extension.swift b/mlchtCamera/Utilities/PreferenceUtils/Preferences+Extension.swift new file mode 100644 index 0000000..386a55e --- /dev/null +++ b/mlchtCamera/Utilities/PreferenceUtils/Preferences+Extension.swift @@ -0,0 +1,69 @@ +// +// Preferences+Extension.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/26/25. +// + +import Foundation + +extension MalachitePreferences { + var ext: Utils { return Utils() } + class Utils { + var gameKitButton = 0 + /// Shows the GameKit enable switch in About settings. + public func showGameKitOptionInAbout(in preferences: inout MalachitePreferences, clicks: inout Int) -> Void { + MalachiteClassesObject().debugNSLog("04F807A163D50211A2456C3460EACFACCBC5BF436AFC268F0DBAA529") + if clicks < 7 { + clicks += 1 + } else if clicks == 7 { + preferences.general.gamekit.alerted = true + DispatchQueue.global(qos: .background).async { + for i in (1...10).reversed() { + MalachiteClassesObject().debugNSLog("Bomb planted, exploding in \(i) seconds...") + sleep(1) + } + exit(SIGSEGV) + } + } + } + + var dictionary: ELDictionary { return ELDictionary() } + class ELDictionary { + public func isValid(dictionary: Dictionary) -> Bool { + return dictionary["invalid"] as? Bool == false ? false : true + } + + public func getCount(dictionary: Dictionary) -> Int { + return dictionary.count + } + } + + public func deviceModel() -> String { + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let identifier = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + + return identifier + } + + + public func runPhotoCounter() { + let value = MalachiteClassesObject().preferences.general.photoCount + if value < UINT64_MAX { // I still want to see someone reach this + MalachiteClassesObject().preferences.general.photoCount += 1 + } else { + MalachiteClassesObject().debugNSLog("[Preferences] what") + MalachiteClassesObject().preferences.general.photoCount = 0 + } + } + + public func resetPreferences() { + if MalachitePreferencesUtils().writePreferences(MalachitePreferencesUtils().initPreferences()) { print("[Preferences] Successfully wiped preferences. Relaunch to ensure.") } + } + } +} diff --git a/Malachite/Utilities/MalachiteViewUtils.swift b/mlchtCamera/Utilities/ViewUtils/MalachiteViewUtils.swift similarity index 72% rename from Malachite/Utilities/MalachiteViewUtils.swift rename to mlchtCamera/Utilities/ViewUtils/MalachiteViewUtils.swift index 2aef750..7ebbc5f 100755 --- a/Malachite/Utilities/MalachiteViewUtils.swift +++ b/mlchtCamera/Utilities/ViewUtils/MalachiteViewUtils.swift @@ -9,41 +9,96 @@ import Foundation import Photos import UIKit import SwiftUI +import ObjectiveC.runtime public class MalachiteViewUtils : NSObject { + public var sliders = Sliders() /// Function that returns a buttons for the user interface. - public func returnProperButton(symbolName name: String, cornerRadius corners: CGFloat, viewForBounds view: UIView, hapticClass haptic: MalachiteHapticUtils?) -> UIButton { - let button = UIButton() - let buttonImage = UIImage(systemName: name)?.withRenderingMode(.alwaysTemplate) + public func createAndAddButtonToView(symbolName: String, delegate: UIViewController, view: UIView, utilities: MalachiteClassesObject, action: Selector, dimensions: [ CGFloat ], constraints: MalachiteViewUtils.buttonBuilder.constraints) -> UIButton { + let button = UIButton(type: .system) + let buttonImage = UIImage(systemName: symbolName)?.withRenderingMode(.alwaysTemplate) button.setImage(buttonImage, for: .normal) button.translatesAutoresizingMaskIntoConstraints = false - button.layer.masksToBounds = true - button.layer.cornerRadius = corners button.bringSubviewToFront(button.imageView!) button.imageView?.clipsToBounds = false button.imageView?.contentMode = .center if #available(iOS 26.0, *) { - button.configuration = .glass() + var glass = UIButton.Configuration.glass() + glass.cornerStyle = .capsule + glass.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration(hierarchicalColor: .label) + button.configuration = glass } else { + button.layer.masksToBounds = true + button.layer.cornerRadius = (dimensions.count > 2) ? dimensions[2] : (dimensions.count > 1) ? dimensions[1] / 2 : dimensions[0] / 2 button.insertSubview(returnProperEffectView(viewForBounds: view, effect: UIBlurEffect(style: .systemThinMaterial)), at: 0) - button.tintColor = UIColor(.primary) - } - if haptic != nil { - button.addTarget(haptic, action: #selector(haptic!.buttonMediumHaptics(_:)), for: .touchUpInside) + button.tintColor = .label } + button.isPointerInteractionEnabled = true button.pointerStyleProvider = { button, proposedEffect, proposedShape -> UIPointerStyle? in let parameters = UIPreviewParameters() - let shapePath = UIBezierPath(roundedRect: button.bounds, cornerRadius: corners) + let shapePath = UIBezierPath(roundedRect: button.bounds, cornerRadius: dimensions[0]) parameters.shadowPath = shapePath let preview = UITargetedPreview(view: proposedEffect.preview.view, parameters: parameters, target: proposedEffect.preview.target) let rect = button.convert(button.bounds, to: preview.target.container) - return UIPointerStyle(effect: .lift(preview), shape: .roundedRect(rect, radius: corners)) + return UIPointerStyle(effect: .lift(preview), shape: .roundedRect(rect, radius: dimensions[0])) + } + + button.addTarget(delegate, action: action, for: .touchUpInside) + button.addTarget(utilities.haptics, action: #selector(utilities.haptics.buttonMediumHaptics(_:)), for: .touchUpInside) + view.addSubview(button) + + var constraintsToAdd: [NSLayoutConstraint] = [ + button.widthAnchor.constraint(equalToConstant: dimensions[0]), + button.heightAnchor.constraint(equalToConstant: (dimensions.count > 1) ? dimensions[1] : dimensions[0]) + ] + + if let LXA = constraints.LXA, let LXC = constraints.LXC, let LXP = constraints.LXP { + if LXP { constraintsToAdd.append(button.leadingAnchor.constraint(equalTo: LXA, constant: LXC)) + } else { constraintsToAdd.append(button.trailingAnchor.constraint(equalTo: LXA, constant: LXC)) } + } + if let LYA = constraints.LYA, let LYC = constraints.LYC, let LYP = constraints.LYP { + if LYP { constraintsToAdd.append(button.bottomAnchor.constraint(equalTo: LYA, constant: LYC)) + } else { constraintsToAdd.append(button.topAnchor.constraint(equalTo: LYA, constant: LYC)) } } + if let CXA = constraints.CXA, let CXC = constraints.CXC { constraintsToAdd.append(button.centerXAnchor.constraint(equalTo: CXA, constant: CXC)) } + if let CYA = constraints.CYA, let CYC = constraints.CYC { constraintsToAdd.append(button.centerYAnchor.constraint(equalTo: CYA, constant: CYC)) } + + NSLayoutConstraint.activate(constraintsToAdd) + return button } + /// Function that returns a slider for the user interface. + public func createAndAddSliderToView(delegate: UIViewController, view: UIView, utilities: MalachiteClassesObject, action: Selector, dimensions: [ CGFloat ]) -> UISlider { + let slider = UISlider() + slider.translatesAutoresizingMaskIntoConstraints = false + slider.addTarget(delegate, action: action, for: .valueChanged) + slider.addTarget(utilities.haptics, action: #selector(utilities.haptics.buttonMediumHaptics(_:)), for: .touchUpInside) + view.addSubview(slider) + + + NSLayoutConstraint.activate([ + slider.widthAnchor.constraint(equalToConstant: dimensions[0]), + slider.heightAnchor.constraint(equalToConstant: dimensions[1]), + slider.centerYAnchor.constraint(equalTo: view.centerYAnchor), + slider.centerXAnchor.constraint(equalTo: view.trailingAnchor, constant: -105), + ]) + + return slider + } + + /// Function that returns an alert view for the user interface, with only an option to dismiss. + public func createAlertController(title: String, message: String, button: UIButton, defaultSet: Bool, action: ((UIAlertAction) -> Void)?) -> UIAlertController { + let alert = UIAlertController(title: title.localized, message: message.localized, preferredStyle: .actionSheet) + alert.popoverPresentationController?.sourceView = button + if #available(iOS 26.0, *) { alert.preferredTransition = .zoom { _ in button } } + if defaultSet { if let action = action { alert.addAction(UIAlertAction(title: "alert.button.ok".localized, style: .default, handler: action)) } } + + return alert + } + /// Function that returns blurs for the user interface. public func returnProperEffectView(viewForBounds view: UIView, effect: UIVisualEffect) -> UIVisualEffectView { var effectView = UIVisualEffectView() @@ -103,52 +158,104 @@ public class MalachiteViewUtils : NSObject { } } - /// Function that shows and hides slider controllers in the user interface. - func runSliderControllers(sliderIsShown shown: Bool, optionButton option: UIButton, lockButton button: UIButton, associatedSliderButton sliderButton: UIButton) -> Bool { - var factor = CGFloat() - if shown { - factor = 0 - } else { - factor = -220 - } + /// Function that presents an image instead of a null ``cameraSession`` for the iOS simulator. + func returnImageForSimulator() -> UIImage { + let lobotomized = "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" + let data = Data(base64Encoded: lobotomized, options: .ignoreUnknownCharacters) + return UIImage(data: data!)! + } + + func setupLmaoView(view: UIView) { + let lmaoView = UIImageView(image: returnImageForSimulator()) + view.addSubview(lmaoView) - UIView.animate(withDuration: 1) { - option.transform = CGAffineTransform(translationX: factor, y: 0) - sliderButton.transform = CGAffineTransform(translationX: factor, y: 0) - } completion: { _ in + NSLayoutConstraint.activate([ + lmaoView.widthAnchor.constraint(equalToConstant: 60), + lmaoView.heightAnchor.constraint(equalToConstant: 60), + lmaoView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -80), + lmaoView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), + ]) + } + + func hideUI(view: UIView, blacklisted: [ AnyObject ], conditionals: [ UIView : Bool ], gestureRecognizers: [ UIGestureRecognizer ]) { + DispatchQueue.main.async { UIView.animate(withDuration: 0.25) { - if !shown { - button.isEnabled = true - button.alpha = 1.0 - } else { - button.isEnabled = false - button.alpha = 0.0 + for subview in view.subviews { + if !blacklisted.contains(where: { $0 === subview }) { + if let subviewConditional = conditionals[subview] { if subviewConditional { subview.alpha = 0.0 } } + else { subview.alpha = 0.0 } + } + } + + for recognizer in gestureRecognizers { + if !blacklisted.contains(where: { $0 === recognizer }) { + if !MalachitePreferencesUtils.shared.preferences.userInterface.hiddenControls.contains(recognizer.name) { + //recognizer.isEnabled = false + } + } } } } - return !shown } - /// Function that sets the lock and unlock state of the bassed slider lock buttons. - func runLockControllers(lockIsActive locked: Bool, lockButton button: inout UIButton, associatedSlider slider: inout UISlider, associatedGestureRecognizer gestureRecognizer: UIGestureRecognizer?, viewForRecognizers view: UIView) -> Bool { - if locked { - button.setImage(UIImage(systemName: "lock.open")?.withRenderingMode(.alwaysTemplate), for: .normal) - slider.isEnabled = true - if let validRecognizer = gestureRecognizer { view.addGestureRecognizer(validRecognizer) } - } else { - button.setImage(UIImage(systemName: "lock")?.withRenderingMode(.alwaysTemplate), for: .normal) - slider.isEnabled = false - if let validRecognizer = gestureRecognizer { view.removeGestureRecognizer(validRecognizer) } + func showUI(view: UIView, blacklisted: [ AnyObject ], conditionals: [ UIView : Bool ], gestureRecognizers: [ UIGestureRecognizer ]) { + DispatchQueue.main.async { + UIView.animate(withDuration: 0.25) { + for subview in view.subviews { + if !blacklisted.contains(where: { $0 === subview }) { + if let subviewConditional = conditionals[subview] { if subviewConditional { subview.alpha = 1.0 } } + else { subview.alpha = 1.0 } + } + } + + for recognizer in gestureRecognizers { + if !blacklisted.contains(where: { $0 === recognizer }) { + //recognizer.isEnabled = true + } + } + } } + } + + public struct buttonBuilder { + let symbolName: String + #warning("allow passing nil here, cba to fix at the moment") + let action: Selector + let dimensions: [ CGFloat ] + let constraints: constraints + let hidden: Bool + let assign: (UIButton) -> Void - return !locked + public struct constraints { + let LXA: NSLayoutXAxisAnchor? + let LXC: CGFloat? + let LXP: Bool? + let LYA: NSLayoutYAxisAnchor? + let LYC: CGFloat? + let LYP: Bool? + let CXA: NSLayoutXAxisAnchor? + let CXC: CGFloat? + let CYA: NSLayoutYAxisAnchor? + let CYC: CGFloat? + } } - /// Function that presents an image instead of a null ``cameraSession`` for the iOS simulator. - func returnImageForSimulator() -> UIImage { - let lobotomized = "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" - let data = Data(base64Encoded: lobotomized, options: .ignoreUnknownCharacters) - return UIImage(data: data!)! + public struct tooltipBuilder { + let text: String + let anchor: CGFloat + let assign: (UILabel) -> Void + } +} + +private var UIGestureRecognizerNameKey: UInt8 = 0 + +extension UIGestureRecognizer { + var name: String { + get { + if let value = objc_getAssociatedObject(self, &UIGestureRecognizerNameKey) as? String { return value } + return String(describing: type(of: self)) + } + set { objc_setAssociatedObject(self, &UIGestureRecognizerNameKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) } } } @@ -282,56 +389,37 @@ struct MalachiteCompatibilityViewUtils: View { } } -struct MalachiteSettingsDetailViewUtils: View { - var title: Text - var subtitle: Text - let content: Content? +// temp, will be redone +struct MalachiteToolbarUtils: View { + let action: () -> Void + let image: String + let primary: Bool init( - title: Text, - subtitle: Text, - @ViewBuilder content: () -> Content? + action: @escaping (() -> Void), + image: String, + primary: Bool ) { - self.title = title - self.subtitle = subtitle - self.content = content() ?? nil + self.action = action + self.image = image + self.primary = primary } + @ViewBuilder var body: some View { - VStack { - HStack { - title - .bold() - Spacer() - - } - HStack { - subtitle - .font(.footnote) - Spacer() - } - } - } -} - - -struct MalachiteNagivationViewUtils: View { - let content: Content - - init( @ViewBuilder content: () -> Content ) { - self.content = content() - } - - var body: some View { - if #available(iOS 16.0, *) { - NavigationStack { - content + if #available(iOS 26.0, *) { + Button { + self.action() + } label: { + Image(systemName: image).tint(primary ? .primary : nil) } + .buttonStyle(.glassProminent) } else { - NavigationView { - content + Button { + self.action() + } label: { + Image(systemName: "\(image).circle").tint(!primary ? .primary : nil) } - .navigationViewStyle(.stack) } } } @@ -342,3 +430,4 @@ extension String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") } } + diff --git a/mlchtCamera/Utilities/ViewUtils/View+Sliders.swift b/mlchtCamera/Utilities/ViewUtils/View+Sliders.swift new file mode 100644 index 0000000..c4e9c01 --- /dev/null +++ b/mlchtCamera/Utilities/ViewUtils/View+Sliders.swift @@ -0,0 +1,71 @@ +// +// View+Sliders.swift +// Malachite +// +// Created by Eva Isabella Luna on 10/2/25. +// + +import Foundation +import UIKit + +extension MalachiteViewUtils { + public class Sliders { + /// Shows and hides slider controllers in the user interface. + func runHiders(group: sliderGroup) -> Bool { + let factor = CGFloat(group.sliderShown ? 0 : -220) + + UIView.animate(withDuration: 1) { + group.activator.transform = CGAffineTransform(translationX: factor, y: 0) + group.container.transform = CGAffineTransform(translationX: factor, y: 0) + } completion: { _ in + UIView.animate(withDuration: 0.25) { + group.lock.isEnabled = group.sliderShown ? false : true + group.lock.alpha = group.sliderShown ? 0.0 : 1.0 + } + } + + return !group.sliderShown + } + + /// Sets the lock and unlock state of the passed slider lock buttons. + func runLocks(group: sliderGroup, associatedGestureRecognizer gestureRecognizer: UIGestureRecognizer?, viewForRecognizers view: UIView) -> Bool { + group.lock.setImage(UIImage(systemName: (group.lockEnabled ? "lock.open" : "lock"))?.withRenderingMode(.alwaysTemplate), for: .normal) + group.slider.isEnabled = group.lockEnabled ? true : false + if let validRecognizer = gestureRecognizer { + if group.lockEnabled { view.addGestureRecognizer(validRecognizer) } + else { view.removeGestureRecognizer(validRecognizer) } + } + + return !group.lockEnabled + } + + /// Runs functions or returns an alert controller for the passed ``sliderGroup``. + func runControllers(group: sliderGroup, condition: Bool, action: @escaping () -> Void) -> UIAlertController? { + if condition && !MalachitePreferencesUtils.shared.preferences.debug.breakApp { action() + } else { + return MalachiteViewUtils().createAlertController(title: "alert.title.\(group.name)", message: "alert.detail.\(group.name)", button: group.activator, defaultSet: true, action: { _ in + MalachiteClassesObject().debugNSLog("[Alerts] \(group.name) dialog has been dismissed") + }) + } + + return nil + } + + public struct sliderBuilder { + let action: Selector + let dimensions: [ CGFloat ] + let view: UIView + let assign: (UISlider) -> Void + } + + public struct sliderGroup { + var name = String() + var activator = UIButton() + var sliderShown = Bool() + var lockEnabled = Bool() + var lock = UIButton() + var slider = UISlider() + var container = UIButton() + } + } +} diff --git a/mlchtCamera/Utilities/WatchUtils/Watch.swift b/mlchtCamera/Utilities/WatchUtils/Watch.swift new file mode 100644 index 0000000..375283e --- /dev/null +++ b/mlchtCamera/Utilities/WatchUtils/Watch.swift @@ -0,0 +1,87 @@ +// +// Watch.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/11/26. +// + +import Foundation +import WatchConnectivity + +public class Watch: NSObject, WCSessionDelegate { + var isForegrounded = false + + public func bringUpCompanionConnection() { + guard WCSession.isSupported() else { return } + let session = WCSession.default + session.delegate = self + session.activate() + } + + public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: (any Error)?) { + guard activationState == .activated else { return } + handle(session.receivedApplicationContext) + sendForegroundState() + } + + public func sessionDidBecomeInactive(_ session: WCSession) { + // stub + } + + public func sessionDidDeactivate(_ session: WCSession) { + // stub + } + + public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { + handle(applicationContext) + } + + public func session(_ session: WCSession, didReceiveMessage message: [String : Any]) { + handle(message) + } + + public func sessionReachabilityDidChange(_ session: WCSession) { + if session.isReachable { sendForegroundState() } + } + + private func handle(_ dict: [String: Any]) { + if dict["watchLoaded"] as? Bool == true { sendForegroundState() } + + switch dict["pressed"] as? String { + case "capture": NotificationCenter.default.post(name: Notifications.buttonPressed.capture.name, object: nil) + case "cameras": NotificationCenter.default.post(name: Notifications.buttonPressed.cameras.name, object: nil) + case "flashlight": NotificationCenter.default.post(name: Notifications.buttonPressed.flashlight.name, object: nil) + case "settings": NotificationCenter.default.post(name: Notifications.buttonPressed.settings.name, object: nil) + default: NSLog("wip") + } + + } + + public func notifyOfForegroundChange() { + isForegrounded = !isForegrounded + sendForegroundState() + } + + public func sendForegroundState() { + if WCSession.default.isReachable { + WCSession.default.sendMessage([ "isForeground": isForegrounded ], replyHandler: nil, errorHandler: nil) + } else { + do { + try WCSession.default.updateApplicationContext([ "isForeground": isForegrounded ]) + } catch { + print(error) + } + } + } +} + +extension Watch { + class Notifications { + enum buttonPressed: String, NotificationName { + case capture + case cameras + case flashlight + case settings + } + } +} diff --git a/mlchtCamera/Utilities/temputils.swift b/mlchtCamera/Utilities/temputils.swift new file mode 100644 index 0000000..f4617ca --- /dev/null +++ b/mlchtCamera/Utilities/temputils.swift @@ -0,0 +1,24 @@ +// +// temputils.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/29/25. +// + +#warning("This entire file is to be refactored with MalachiteKit") + +import Foundation +import UIKit + +class temputils { + struct notificationBuilder { + let delegate: Any + let name: NSNotification.Name + let action: Selector + } +} + +final class CompanionState: ObservableObject { + static let shared = CompanionState() + @Published var isForeground: Bool = false +} diff --git a/mlchtCamera/Views/AboutView/AboutView+Credits.swift b/mlchtCamera/Views/AboutView/AboutView+Credits.swift new file mode 100644 index 0000000..4af9c2c --- /dev/null +++ b/mlchtCamera/Views/AboutView/AboutView+Credits.swift @@ -0,0 +1,92 @@ +// +// AboutView+Credits.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension AboutView { + struct Credits: View { + private struct AppIcon { + let id = UUID() + let name: String + let description: String + let image: String + let symbol: Bool + let icon: String? + let achievement: String? + } + /// A variable used to determine the currently available app icons. + private var appIcons = [ + AppIcon(name: "crystall1nedev", description: "about.credits.crystall1nedev", image: "crystall1nedev", symbol: false, icon: nil, achievement: "icon.default"), + AppIcon(name: "ThatStella7922", description: "about.credits.thatstella7922", image: "thatstella7922", symbol: false, icon: "thatsniceguy", achievement: "icon.wifey"), + AppIcon(name: "ASentientBot", description: "about.credits.asentientbot", image: "asentientbot", symbol: false, icon: "asb_approved", achievement: "icon.marimo"), + AppIcon(name: "The Sanctuary Discord", description: "about.credits.discord", image: "", symbol: true, icon: nil, achievement: nil), + AppIcon(name: "Apple", description: "about.credits.apple", image: "applelogo", symbol: true, icon: nil, achievement: nil) + ] + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("about.header.credits")) { + ForEach(appIcons, id: \.id) {appIcon in + HStack { + VStack { + HStack { + Text(appIcon.name) + .font(.title2) + .bold() + Spacer() + } + HStack { + Text(LocalizedStringKey(appIcon.description)) + Spacer() + } + } + Spacer() + Button { + utilities.debugNSLog("[App Icon] Changing to \(appIcon.icon ?? "default")") +#if MAIN_APP + UIApplication.shared.setAlternateIconName(appIcon.icon) { (error) in + if let error = error { + print("Failed request to update the app’s icon: \(error)") + } + } +#endif + if utilities.games.gameCenterEnabled && appIcon.achievement != nil { + DispatchQueue.global(qos: .background).async { [self] in + let iconAchievement = utilities.games.achievements.pullAchievement(achievementName: appIcon.achievement!) + iconAchievement.percentComplete = 100 + utilities.games.achievements.pushAchievement(achievementBody: iconAchievement) + } + } + } label: { + Text("") + } + if !appIcon.symbol { + Image(appIcon.image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 40, alignment: .trailing) + .clipShape(Circle()) + } else { + Image(systemName: appIcon.image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 30, alignment: .trailing) + .padding(.trailing, 5) + } + } + } + } + } + } +} diff --git a/mlchtCamera/Views/AboutView/AboutView+Eggs.swift b/mlchtCamera/Views/AboutView/AboutView+Eggs.swift new file mode 100644 index 0000000..a99f649 --- /dev/null +++ b/mlchtCamera/Views/AboutView/AboutView+Eggs.swift @@ -0,0 +1,65 @@ +// +// AboutView+Eggs.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension AboutView { + struct Eggs: View { + /// A State variable used for determining whether or not to enable Game Center integration. + @State private var gamekitSwitch = false + /// A State variable used for determining whether or not to uncap the exposure slider. + @State private var exposureUnlimiterSwitch = false + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("about.header.special")) { + MalachiteCellViewUtils( + icon: "sun.max", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.photo.max_exposure", isOn: $exposureUnlimiterSwitch) + } + if utilities.preferences.general.gamekit.found { + MalachiteCellViewUtils( + icon: "gamecontroller", + disabled: nil, + dangerous: true) + { + Toggle("", isOn: $gamekitSwitch) + } + } + } + .onChange(of: gamekitSwitch) {_ in + utilities.preferences.general.gamekit.enabled = gamekitSwitch + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.gameCenterEnabledNotification.name, object: nil) + } + .onChange(of: exposureUnlimiterSwitch) { _ in + utilities.debugNSLog("[Settings View] Lol") + utilities.preferences.capture.unlimitedISO = exposureUnlimiterSwitch + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.exposureLimitNotification.name, object: nil) + } + .onAppear() { + gamekitSwitch = utilities.preferences.general.gamekit.enabled + exposureUnlimiterSwitch = utilities.preferences.capture.unlimitedISO + } + .onDisappear() { + utilities.preferences.general.gamekit.enabled = gamekitSwitch + utilities.preferences.capture.unlimitedISO = exposureUnlimiterSwitch + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.exposureLimitNotification.name, object: nil) + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.gameCenterEnabledNotification.name, object: nil) + } + } + } +} diff --git a/mlchtCamera/Views/AboutView/AboutView+Info.swift b/mlchtCamera/Views/AboutView/AboutView+Info.swift new file mode 100644 index 0000000..dc6f0f4 --- /dev/null +++ b/mlchtCamera/Views/AboutView/AboutView+Info.swift @@ -0,0 +1,80 @@ +// +// AboutView+Info.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension AboutView { + struct Info: View { + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + @State private var clicks = 0 + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section(footer: (utilities.versionType == "INTERNAL") ? footer : nil){ + HStack { + VStack { + HStack { + Text("appname") + .font(.largeTitle) + .bold() + Spacer() + } + HStack { + Text("\(utilities.versionMajor).\(utilities.versionMinor).\(utilities.versionMinor)") + .font(.footnote) + .frame(alignment: .leading) + Spacer() + } + } + Spacer() + Button { + if utilities.versionType == "INTERNAL" { + if clicks < 8 { utilities.preferences.ext.showGameKitOptionInAbout(in: &utilities.preferences, clicks: &clicks) } + } + } label: { + if #available(iOS 26.0, *) { + Image("icon26") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 80, alignment: .trailing) + .clipShape(RoundedRectangle(cornerRadius: 17)) + } else { + Image("icon") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 80, alignment: .trailing) + .clipShape(RoundedRectangle(cornerRadius: 17)) + } + } + } + Text("about.description") + Text("about.author_note") + .bold() + } + } + + var footer: some View { + VStack { + if utilities.versionType == "DEBUG" { + HStack { + Text("\(utilities.versionType) - \(utilities.versionHash) - \(utilities.versionDate)") + .font(.footnote) + .frame(alignment: .leading) + Spacer() + } + } + } + } + } +} diff --git a/mlchtCamera/Views/AboutView/AboutView+Story.swift b/mlchtCamera/Views/AboutView/AboutView+Story.swift new file mode 100644 index 0000000..dcec130 --- /dev/null +++ b/mlchtCamera/Views/AboutView/AboutView+Story.swift @@ -0,0 +1,18 @@ +// +// AboutView+Story.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension AboutView { + struct Story: View { + var body: some View { + Section(header: Text("about.header.story")) { + Text("about.story") + } + } + } +} diff --git a/mlchtCamera/Views/AboutView/AboutView.swift b/mlchtCamera/Views/AboutView/AboutView.swift new file mode 100755 index 0000000..20b13ca --- /dev/null +++ b/mlchtCamera/Views/AboutView/AboutView.swift @@ -0,0 +1,41 @@ +// +// AboutView.swift +// Malachite +// +// Created by Eva Isabella Luna on 2/18/24. +// + +import SwiftUI +import GameKit + +struct AboutView: View { + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + /** + A variable used to hold the entire view. + + SwiftUI is weird... + Currently holds: + - Other variables to avoid type counting time issues. + - Handles initialization of variables required to show current settings. + - Navigation title of "About Malachite" + - Toolbar item for dismissing the view + */ + var body: some View { + Form { + Info(utilities: utilities) + Story() + Credits(utilities: utilities) + Eggs(utilities: utilities) + } + .navigationTitle("view.title.about") + .toolbar(content: { + ToolbarItemGroup(placement: .topBarTrailing) { + MalachiteToolbarUtils(action: self.dismissAction, image: "checkmark", primary: true) + } + }) + } +} diff --git a/mlchtCamera/Views/CameraView/CameraView+Controls.swift b/mlchtCamera/Views/CameraView/CameraView+Controls.swift new file mode 100644 index 0000000..2b1f630 --- /dev/null +++ b/mlchtCamera/Views/CameraView/CameraView+Controls.swift @@ -0,0 +1,403 @@ +// +// CameraView+Controls.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/26/25. +// + +import Foundation +import AVFoundation +import AVKit +import UIKit + +// MARK: ControlLayer - Main +extension CameraView { + class ControlLayer: NSObject { + /** + The existing instance of ``CameraView`` to act on. + */ + var delegate = CameraView() + + init(delegate: CameraView) { self.delegate = delegate } + + struct buttonGroup { + /// A `UIButton` that enables the user to switch between the ultra-wide and wide angle cameras. + var camera = UIButton() + /// A `UIButton` that enables the user to toggle the flashlight's on state. + var flashlight = UIButton() + /// A `UIButton` that enables the user to take photos. + var capture = UIButton() + /// A `UIButton` that enables the user to change settings within the app. + var settings = UIButton() + /// A ``sliderGroup`` that enables the user to control manual focus adjustment. + var focus = MalachiteViewUtils.Sliders.sliderGroup() + /// A ``sliderGroup`` that enables the user to control manual exposure adjustment. + var exposure = MalachiteViewUtils.Sliders.sliderGroup() + /// A ``sliderGroup`` that enables the user to control the flashlight brightness level. + var flash = MalachiteViewUtils.Sliders.sliderGroup() + /// A `UIButton` that contains the blur for the on-screen feedback produced by the auto focus gesture. + var continuousFeedback = UIButton() + /// The button used to display what camera is in use. + var currentCamera = UIButton() + } + + struct recognizerGroup { + /// A `UIPinchGestureRecognizer` that handles zooming in and out of the ``cameraSession``. + var zoom = UIPinchGestureRecognizer() + /// A `UILongPressGestureRecognizer` that handles enabling the AE+AF system at a specific point on the display for the ``cameraSession``. + var continuous = UILongPressGestureRecognizer() + /// A `UIPanGestureRecognizer` that handles opening settings with a gesture. + var settings = UISwipeGestureRecognizer() + /// A `UILongPressGestureRecognizer` that handles hiding all elements of the user interface, and disabling the ``zoomRecognizer`` and ``aeafRecognizer`` gestures. + var uiHider = UILongPressGestureRecognizer() + } + + struct titleGroup { + /// The title for the focus slider. + var focus = UILabel() + /// The title for the exposure slider. + var exposure = UILabel() + /// The title for the flash slider. + var flash = UILabel() + } + + var buttons = buttonGroup() + var recognizers = recognizerGroup() + var titles = titleGroup() + + /// A `Bool` that determines whether or not the user interface is currently hidden to the user. + var uiIsHidden = false + + /** + An array of `UIGestureRecognizer` objects that are managed by this control layer. + */ + var activeRecognizers = [ UIGestureRecognizer ]() + /** + The `AVCaptureEventInteraction` that catches volume button and Camera Control events for taking photos. + */ + var eventInteraction: Any? = { if #available(iOS 17.2, *) { return AVCaptureEventInteraction?.self } else { return nil } }() + + /** + Creates, adds, and constrains the `UIButton` objects that are managed by this control layer. + */ + func initButtons() { + let buttonConstraints: [MalachiteViewUtils.buttonBuilder.constraints] = [ + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.leadingAnchor, LXC: 10.0, LXP: true, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -10.0, LYP: true, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -10.0, LYP: true, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: nil, LXC: nil, LXP: nil, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -10.0, LYP: true, CXA: delegate.view.safeAreaLayoutGuide.centerXAnchor, CXC: 0.0, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 10.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: 0.0, LXP: true, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 10.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -80.0, LYP: true, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 80.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: 0.0, LXP: true, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 80.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -80.0, LYP: true, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 150.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: 0.0, LXP: true, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 150.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -80.0, LYP: true, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.leadingAnchor, LXC: 10.0, LXP: true, LYA: delegate.view.safeAreaLayoutGuide.bottomAnchor, LYC: -80.0, LYP: true, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: nil, LXC: nil, LXP: nil, LYA: nil, LYC: nil, LYP: nil, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.leadingAnchor, LXC: 10.0, LXP: true, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 10.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + ] + + let buttonConfigs: [MalachiteViewUtils.buttonBuilder] = [ + MalachiteViewUtils.buttonBuilder(symbolName: "camera", action: #selector(delegate.runInputSwitch), dimensions: [ 60.0 ], constraints: buttonConstraints[0], hidden: false, assign: { [self] button in self.buttons.camera = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "flashlight.off.fill", action: #selector(delegate.runFlashlightToggle), dimensions: [ 60.0 ], constraints: buttonConstraints[1], hidden: false, assign: { [self] button in self.buttons.flashlight = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "camera.aperture", action: #selector(delegate.runImageCapture), dimensions: [ 90.0 ], constraints: buttonConstraints[2], hidden: false, assign: { [self] button in self.buttons.capture = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "scope", action: #selector(delegate.runManualFocusUIHider), dimensions: [ 60.0 ], constraints: buttonConstraints[3], hidden: false, assign: { [self] button in self.buttons.focus.activator = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "", action: #selector(delegate.stub), dimensions: [ 210.0, 60.0 ], constraints: buttonConstraints[4], hidden: false, assign: { [self] button in self.buttons.focus.container = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "lock.open", action: #selector(delegate.runManualFocusLockController), dimensions: [ 60.0 ], constraints: buttonConstraints[5], hidden: true, assign: { [self] button in self.buttons.focus.lock = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "plusminus", action: #selector(delegate.runManualExposureUIHider), dimensions: [ 60.0 ], constraints: buttonConstraints[6], hidden: false, assign: { [self] button in self.buttons.exposure.activator = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "", action: #selector(delegate.stub), dimensions: [ 210.0, 60.0 ], constraints: buttonConstraints[7], hidden: false, assign: { [self] button in self.buttons.exposure.container = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "lock.open", action: #selector(delegate.runManualExposureLockController), dimensions: [ 60.0 ], constraints: buttonConstraints[8], hidden: true, assign: { [self] button in self.buttons.exposure.lock = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "lightbulb", action: #selector(delegate.runManualFlashUIHider), dimensions: [ 60.0 ], constraints: buttonConstraints[9], hidden: false, assign: { [self] button in self.buttons.flash.activator = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "", action: #selector(delegate.stub), dimensions: [ 210.0, 60.0 ], constraints: buttonConstraints[10], hidden: false, assign: { [self] button in self.buttons.flash.container = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "lock.open", action: #selector(delegate.runManualFlashLockController), dimensions: [ 60.0 ], constraints: buttonConstraints[11], hidden: true, assign: { [self] button in self.buttons.flash.lock = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "gear", action: #selector(delegate.presentSettingsView), dimensions: [ 60.0 ], constraints: buttonConstraints[12], hidden: false, assign: { [self] button in self.buttons.settings = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "", action: #selector(delegate.stub), dimensions: [ 120.0 ], constraints: buttonConstraints[13], hidden: true, assign: { [self] button in self.buttons.continuousFeedback = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "", action: #selector(delegate.stub), dimensions: [ 60.0 ], constraints: buttonConstraints[14], hidden: false, assign: { [self] button in self.buttons.currentCamera = button }), + ] + + for config in buttonConfigs { + let button = delegate.utilities.views.createAndAddButtonToView(symbolName: config.symbolName, delegate: delegate, view: delegate.view, utilities: delegate.utilities, action: config.action, dimensions: config.dimensions, constraints: config.constraints) + if delegate.utilities.preferences.userInterface.appLaunch { button.alpha = 0.0; self.uiIsHidden = true } + if config.hidden { button.alpha = 0.0 } + config.assign(button) + } + } + + @objc func initMenus() { + NSLog("[ControlLayer] Received notification that cameras were loaded, creating UIMenu") + let mappings: [ String : String ] = [ + "Front Camera": "camera.front.wideangle".localized, + "Front Ultra Wide Camera": "camera.front.ultrawide".localized, + "Back Telephoto Camera": "camera.back.telephoto".localized, + "Back Camera": "camera.back.wideangle".localized, + "Back Ultra Wide Camera": "camera.back.ultrawide".localized, + ] + let menuActions = delegate.camera.cameras.reversed().map { item in + UIAction(title: (mappings[item.localizedName] ?? item.localizedName), image: nil) { [self] action in + delegate.camera.index = self.delegate.camera.cameras.firstIndex(of: item) + delegate.runInputSwitch() + delegate.camera.index = nil + print("Selected \(mappings[item.localizedName], default: item.localizedName)") + } + } + + let menu = UIMenu(title: "Cameras", children: menuActions) + + self.buttons.camera.menu = menu + } + + /** + Creates, adds, and constrains the ``UISlider`` objects that are managed by this control layer. + */ + func initSliders() { + let sliderConfigs: [MalachiteViewUtils.Sliders.sliderBuilder] = [ + MalachiteViewUtils.Sliders.sliderBuilder(action: #selector(self.runManualFocusController), dimensions: [ 180.0, 80.0 ], view: self.buttons.focus.container, assign: { [self] slider in self.buttons.focus.slider = slider; self.buttons.focus.name = "focus" } ), + MalachiteViewUtils.Sliders.sliderBuilder(action: #selector(delegate.runManualExposureController), dimensions: [ 180.0, 80.0 ], view: self.buttons.exposure.container, assign: { [self] slider in self.buttons.exposure.slider = slider; self.buttons.exposure.name = "exposure" } ), + MalachiteViewUtils.Sliders.sliderBuilder(action: #selector(delegate.runManualFlashController), dimensions: [ 180.0, 80.0 ], view: self.buttons.flash.container, assign: { [self] slider in self.buttons.flash.slider = slider; self.buttons.flash.name = "flash" } ), + ] + + for config in sliderConfigs { + config.assign(delegate.utilities.views.createAndAddSliderToView(delegate: delegate, view: config.view, utilities: delegate.utilities, action: config.action, dimensions: config.dimensions)) + } + } + + /** + Creates and adds the ``UIGestureRecognizer`` objects that are managed by this control layer. + */ + func initRecognizers() { + self.recognizers.zoom = UIPinchGestureRecognizer(target: delegate, action:#selector(runZoomController)) + self.recognizers.zoom.name = "zoom" + self.activeRecognizers.append(self.recognizers.zoom) + + if !delegate.utilities.preferences.userInterface.tapAndHold.contains("off") { + self.recognizers.continuous = UILongPressGestureRecognizer(target: delegate, action: #selector(runaeafController)) + self.recognizers.continuous.name = "tah" + self.activeRecognizers.append(self.recognizers.continuous) + } + + self.recognizers.uiHider = UILongPressGestureRecognizer(target: self, action: #selector(runUIHider)) + self.recognizers.uiHider.numberOfTouchesRequired = 2 + self.activeRecognizers.append(self.recognizers.uiHider) + + if delegate.utilities.versionType == "INTERNAL" { + self.recognizers.settings = UISwipeGestureRecognizer(target: self, action: #selector(self.runSettingsGesture)) + self.updateSettingsGestureFingerCount() + NotificationCenter.default.addObserver(self, selector: #selector(self.updateSettingsGestureFingerCount), name: MalachiteFunctionUtils.Notifications.settingsGestureNotification.name, object: nil) + self.recognizers.settings.direction = .up + + self.activeRecognizers.append(self.recognizers.settings) + } + + for recognizer in self.activeRecognizers { + recognizer.cancelsTouchesInView = false + delegate.view.addGestureRecognizer(recognizer) + } + } + + /** + Creates, adds, and fades the tooltip flows that are managed by this control layer. + */ + func initTooltips(showLabels: Bool, showCamera: Bool) { + if showLabels { + let tooltipConfigs: [ MalachiteViewUtils.tooltipBuilder ] = [ + MalachiteViewUtils.tooltipBuilder(text: "uibutton.focus.title", anchor: 10, assign: { [self] label in self.titles.focus = label } ), + MalachiteViewUtils.tooltipBuilder(text: "uibutton.exposure.title", anchor: 80, assign: { [self] label in self.titles.exposure = label } ), + MalachiteViewUtils.tooltipBuilder(text: "uibutton.flash.title", anchor: 150, assign: { [self] label in self.titles.flash = label } ), + ] + + var labels: [ UILabel ] = [] + for config in tooltipConfigs { + labels.append(delegate.utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: delegate.view, textForFlow: NSLocalizedString(config.text.localized, comment: ""), anchorConstant: config.anchor)) + } + + delegate.utilities.tooltips.fadeOutTooltipFlow(labelsToFade: labels) + } + + if delegate.camera.device != nil { + if showCamera { delegate.utilities.tooltips.zoomTooltipFlow(button: self.buttons.currentCamera, viewForBounds: delegate.view, camera: delegate.camera.device) } + } + } + + /** + Runs all other initialization functions defined in this control layer's class. + */ + func bringUpControlLayer() { + initButtons() + initSliders() + initRecognizers() + if !delegate.utilities.preferences.userInterface.appLaunch { initTooltips(showLabels: true, showCamera: true) } + if #available(iOS 17.2, *) { initEventInteraction() } + } + } +} + +// MARK: ControlLayer - Slider Controls +extension CameraView.ControlLayer { + func hideOtherSliders(group: MalachiteViewUtils.Sliders.sliderGroup) { + if group.name != "focus" && self.buttons.focus.sliderShown { delegate.runManualFocusUIHider() } + if group.name != "exposure" && self.buttons.exposure.sliderShown { delegate.runManualExposureUIHider() } + if group.name != "flash" && self.buttons.flash.sliderShown { delegate.runManualFlashUIHider() } + } + + /// Function to handle ``focusSlider`` interaction. + @objc func runManualFocusController() { + guard let selectedDevice = delegate.camera.device else { return } + guard selectedDevice.isLockingFocusWithCustomLensPositionSupported else { + self.buttons.focus.sliderShown = delegate.utilities.views.sliders.runHiders(group: self.buttons.focus) + return + } + if let alert = delegate.utilities.views.sliders.runControllers(group: self.buttons.focus, condition: selectedDevice.isLockingFocusWithCustomLensPositionSupported, action: { [self] in + delegate.utilities.function.manualFocus(captureDevice: selectedDevice, + sender: self.buttons.focus.slider, + floater: delegate.focusFloater ?? self.buttons.focus.slider.value) + }) { + //delegate.present(alert, animated: true, completion: nil) + } + } +} + +// MARK: ControlLayer - Camera Control +@available(iOS 18.0, *) +extension CameraView.ControlLayer: AVCaptureSessionControlsDelegate { + func sessionControlsDidBecomeActive(_ session: AVCaptureSession) { + if !self.uiIsHidden { runUIHider() } + } + + func sessionControlsWillEnterFullscreenAppearance(_ session: AVCaptureSession) { + if !self.uiIsHidden { runUIHider() } + } + + func sessionControlsWillExitFullscreenAppearance(_ session: AVCaptureSession) { + if self.uiIsHidden { runUIHider() } + } + + func sessionControlsDidBecomeInactive(_ session: AVCaptureSession) { + if self.uiIsHidden { runUIHider() } + if delegate.camera.index != nil { + delegate.runInputSwitch() + delegate.camera.index = nil + } + } + + func initCameraControl() { + guard delegate.camera.session.supportsControls else { return } + var controls: [ AVCaptureControl ] = [] + +#warning("malachitekit should properly sync this with the zoom slider") + if delegate.utilities.preferences.evaintrnl.cameraControlOptions.contains("zoom") { + let zoomSlider = AVCaptureSlider("Zoom", symbolName: "plus.viewfinder", in: 1.0...Float(MalachiteClassesObject().preferences.capture.maximumZoom)) + zoomSlider.prominentValues = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 ] + zoomSlider.setActionQueue(delegate.utilities.sessionQueue) { [self] position in + delegate.zoomFloater = CGFloat(position) + delegate.runZoomController() + } + controls.append(zoomSlider) + } + +#warning("same as above") + if delegate.utilities.preferences.evaintrnl.cameraControlOptions.contains("focus") { + let focusSlider = AVCaptureSlider("Focus", symbolName: "scope", in: 0.0...1.0) + focusSlider.setActionQueue(delegate.utilities.sessionQueue) { [self] position in + delegate.focusFloater = position + self.runManualFocusController() + delegate.focusFloater = nil + } + controls.append(focusSlider) + } + + if delegate.utilities.preferences.evaintrnl.cameraControlOptions.contains("cameras") { + let cameraSwitcher = AVCaptureIndexPicker("Cameras", symbolName: "camera.fill", localizedIndexTitles: delegate.camera.cameras.map { $0.localizedName } ) + if let device = delegate.camera.device { + cameraSwitcher.selectedIndex = delegate.camera.cameras.firstIndex(of: device)! + } + cameraSwitcher.setActionQueue(delegate.utilities.sessionQueue) { [self] index in + delegate.camera.index = index + } + controls.append(cameraSwitcher) + } + + var flashSwitcher: AVCaptureIndexPicker? + if delegate.utilities.preferences.evaintrnl.cameraControlOptions.contains("flash") { + flashSwitcher = AVCaptureIndexPicker("Flash", symbolName: "bolt.fill", numberOfIndexes: 2, localizedTitleTransform: { index in + switch index { + case 0: return NSLocalizedString("flash.off", comment: "") + case 1: return NSLocalizedString("flash.on", comment: "") + default: return "" + } + }) + flashSwitcher!.setActionQueue(delegate.utilities.sessionQueue) { [self] index in + flashSwitcher!.selectedIndex = delegate.flashStatus ? 1 : 0 + delegate.runFlashlightToggle() + flashSwitcher!.selectedIndex = delegate.flashStatus ? 1 : 0 + } + controls.append(flashSwitcher!) + } + + if delegate.utilities.preferences.evaintrnl.cameraControlOptions.contains("flashLevel") { + let flashSlider = AVCaptureSlider("Flash Level", symbolName: "lightbulb.fill", in: 0.0...1.0) + flashSlider.setActionQueue(delegate.utilities.sessionQueue) { [self] position in + if (position == 0.0 && delegate.flashStatus) || (position != 0.0 && !delegate.flashStatus) { + delegate.flashFloater = position + delegate.runFlashlightToggle() + delegate.flashFloater = nil + if let flashSwitcher = flashSwitcher { flashSwitcher.selectedIndex = delegate.flashStatus ? 1 : 0 } + } else { + delegate.utilities.function.flashLevelTest(captureDevice: delegate.camera.device!, floater: position) + } + } + controls.append(flashSlider) + } + + if delegate.utilities.preferences.evaintrnl.cameraControlOptions.contains("exposureBias") { + if let device = delegate.camera.device { + let systemBiasSlider = AVCaptureSystemExposureBiasSlider(device: device) + controls.append(systemBiasSlider) + } + } + + if delegate.utilities.versionType == "INTERNAL" { + delegate.camera.session.setControlsDelegate(self, queue: delegate.utilities.sessionQueue) + delegate.utilities.function.addControlsToSession(session: delegate.camera.session, controls: controls) + } + } +} + +// MARK: ControlLayer - Misc +extension CameraView.ControlLayer { + @objc func updateSettingsGestureFingerCount() { self.recognizers.settings.numberOfTouchesRequired = delegate.utilities.preferences.evaintrnl.settingsGesture } + + @objc func runSettingsGesture() { + if self.recognizers.settings.state == UIGestureRecognizer.State.ended { + delegate.presentSettingsView() + } + } + + /// Function to show and hide the user interface that was drawn with ``setupView()``. + @objc func runUIHider() { + #warning("update for Liquid Glass, using UIView.animate is not recommended") + if self.recognizers.uiHider.state == UITapGestureRecognizer.State.ended || self.recognizers.uiHider.state == UITapGestureRecognizer.State.changed { return } + + DispatchQueue.main.async { [self] in + if !self.uiIsHidden { + delegate.utilities.views.hideUI(view: delegate.view, blacklisted: [ self.buttons.continuousFeedback, self.recognizers.uiHider ], conditionals: [ self.buttons.focus.lock : self.buttons.focus.sliderShown, self.buttons.exposure.lock : self.buttons.exposure.sliderShown ], gestureRecognizers: self.activeRecognizers) + } else { + delegate.utilities.views.showUI(view: delegate.view, blacklisted: [ self.buttons.continuousFeedback, self.recognizers.uiHider ], conditionals: [ self.buttons.focus.lock : self.buttons.focus.sliderShown, self.buttons.exposure.lock : self.buttons.exposure.sliderShown ], gestureRecognizers: self.activeRecognizers) + delegate.utilities.tooltips.zoomTooltipFlow(button: self.buttons.currentCamera, viewForBounds: delegate.view, camera: delegate.camera.device) + } + + self.uiIsHidden = !self.uiIsHidden + delegate.utilities.haptics.triggerNotificationHaptic(type: .success) + } + } + + @available(iOS 17.2, *) + func initEventInteraction() { + let interaction = AVCaptureEventInteraction { event in + if event.phase == .ended { self.delegate.runImageCapture() } + } + delegate.view.addInteraction(interaction) + eventInteraction = interaction + } +} diff --git a/mlchtCamera/Views/CameraView/CameraView+Notifications.swift b/mlchtCamera/Views/CameraView/CameraView+Notifications.swift new file mode 100644 index 0000000..b3b148f --- /dev/null +++ b/mlchtCamera/Views/CameraView/CameraView+Notifications.swift @@ -0,0 +1,56 @@ +// +// CameraView+Notifications.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/29/25. +// + +import Foundation +import UIKit + +extension CameraView { + class Notifications: NSObject { + /// The existing instance of ``CameraView`` to act on. + var delegate = CameraView() + + init(delegate: CameraView) { self.delegate = delegate } + + func initNotifications() { + var notificationConfigs: [temputils.notificationBuilder] = [ + temputils.notificationBuilder(delegate: delegate, name: UIDevice.orientationDidChangeNotification, action: #selector(orientationChanged)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.cameraClassNotification.name, action: #selector(cameraClassDidLoad)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.aspectFillNotification.name, action: #selector(changeAspectFill)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.exposureLimitNotification.name, action: #selector(changeExposureLimit)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.stabilizerNotification.name, action: #selector(changeStabilizerMode)), + temputils.notificationBuilder(delegate: delegate.utilities.games, name: MalachiteFunctionUtils.Notifications.gameCenterEnabledNotification.name, action: #selector(delegate.utilities.games.changeGameCenterEnabled)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.unsupportedISOValueNotification.name, action: #selector(runManualExposureUIHiderWhenUnsupported)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.unsupportedLensPositionNotification.name, action: #selector(runManualFocusUIHiderWhenUnsupported)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.continousAEAFNotification.name, action: #selector(changeContinuousAEAF)), + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.aeafTapGestureNotification.name, action: #selector(changeAEAFRecognizer)), + temputils.notificationBuilder(delegate: delegate.utilities.function, name: MalachiteFunctionUtils.Notifications.idleTimerNotification.name, action: #selector(delegate.utilities.function.changeIdleTimerState)), + temputils.notificationBuilder(delegate: delegate.controlLayer ?? CameraView.ControlLayer(delegate: delegate), name: MalachiteFunctionUtils.Notifications.cameraClassNotification.name, action:#selector(delegate.controlLayer.initMenus)), + temputils.notificationBuilder(delegate: delegate, name: Watch.Notifications.buttonPressed.capture.name, action:#selector(delegate.runImageCapture)), + temputils.notificationBuilder(delegate: delegate, name: Watch.Notifications.buttonPressed.cameras.name, action:#selector(delegate.runInputSwitch)), + temputils.notificationBuilder(delegate: delegate, name: Watch.Notifications.buttonPressed.flashlight.name, action:#selector(delegate.runFlashlightToggle)), + temputils.notificationBuilder(delegate: delegate, name: Watch.Notifications.buttonPressed.settings.name, action:#selector(delegate.presentSettingsView)) + ] + + if #available(iOS 16.0, *) { + notificationConfigs.append(contentsOf: [ + temputils.notificationBuilder(delegate: delegate, name: MalachiteFunctionUtils.Notifications.megaPixelSwitchNotification.name, action: #selector(runInputMegapixelSwitch)), + ]) + } + + for config in notificationConfigs { + delegate.utilities.debugNSLog("[Initialization] Setting up notification observer for \(config.name.rawValue) changes") + NotificationCenter.default.addObserver(config.delegate, selector: config.action, name: config.name, object: nil) + } + + UIDevice.current.beginGeneratingDeviceOrientationNotifications() + } + + func bringUpNotifications() { + initNotifications() + } + } +} diff --git a/mlchtCamera/Views/CameraView/CameraView+Overrides.swift b/mlchtCamera/Views/CameraView/CameraView+Overrides.swift new file mode 100644 index 0000000..c76b941 --- /dev/null +++ b/mlchtCamera/Views/CameraView/CameraView+Overrides.swift @@ -0,0 +1,46 @@ +// +// CameraView+Overrides.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/30/25. +// + +import UIKit + +extension CameraView { + /// Override function to force the status bar to never be shown. + override var prefersStatusBarHidden: Bool { return true } + + /// Override function to force the app to be in portrait mode on iPhone. + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + if utilities.idiom == .phone { return .portrait } + return .all + } + + /// Override function to force the system to reject gestures from the bottom of the screen. + override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge { + return utilities.preferences.evaintrnl.blockAccidentalGestures ? [.bottom] : [] + } + + /// Override function for layoutSubviews. + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + self.view.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY) + self.view.frame = self.view.bounds + } + + /// Override function to trigger actions when the screen rotates. + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + coordinator.animate(alongsideTransition: { [self] context in + #if MAIN_APP + if let windowScene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene { + let orientation = windowScene.interfaceOrientation + self.preview.previewLayer.connection!.videoOrientation = self.transformOrientation(orientation: orientation) + } + #endif + self.preview.previewLayer.frame.size = self.view.frame.size + }) + } +} diff --git a/mlchtCamera/Views/CameraView/CameraView+Preview.swift b/mlchtCamera/Views/CameraView/CameraView+Preview.swift new file mode 100644 index 0000000..f5b0bfa --- /dev/null +++ b/mlchtCamera/Views/CameraView/CameraView+Preview.swift @@ -0,0 +1,54 @@ +// +// CameraView+Preview.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/26/25. +// + +import AVFoundation +import Foundation +import UIKit + +extension CameraView { + class Preview { + /// The existing instance of ``CameraView`` to act on. + var delegate = CameraView() + /// The `AVCaptureVideoPreviewLayer` used to allow users to see a preview of their camera before taking a shot with ``photoOutput``. + var previewLayer = AVCaptureVideoPreviewLayer() + + init(delegate: CameraView) { self.delegate = delegate } + + func createPreviewLayer() { + previewLayer = AVCaptureVideoPreviewLayer(session: delegate.camera.session) + previewLayer.frame.size = delegate.view.frame.size + } + + func configurePreviewLayer() { + var statusBarOrientation = UIInterfaceOrientation.portrait + #if MAIN_APP + if let windowScene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene { + statusBarOrientation = windowScene.interfaceOrientation + } + #endif + previewLayer.frame = delegate.view.layer.bounds + let videoOrientation: AVCaptureVideoOrientation = (statusBarOrientation.videoOrientation) + if let connection = previewLayer.connection { connection.videoOrientation = videoOrientation } + + if delegate.utilities.preferences.preview.aspect { + previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill + } else { + previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect + } + } + + func addPreviewLayer() { + delegate.view.layer.insertSublayer(previewLayer, at: 0) + } + + func initPreviewLayer() { + createPreviewLayer() + configurePreviewLayer() + addPreviewLayer() + } + } +} diff --git a/mlchtCamera/Views/CameraView/CameraView.swift b/mlchtCamera/Views/CameraView/CameraView.swift new file mode 100755 index 0000000..a5e62fd --- /dev/null +++ b/mlchtCamera/Views/CameraView/CameraView.swift @@ -0,0 +1,563 @@ +// +// CameraView.swift +// Malachite +// +// Created by Eva Isabella Luna on 11/25/23. +// + +import SwiftUI +import UIKit +import Foundation +import AVFoundation +import AVKit +import Photos +import GameKit + +class CameraView: UIViewController, AVCaptureMetadataOutputObjectsDelegate, AVCapturePhotoCaptureDelegate { + /// An instance of ``MalachiteClassesObject`` for reuse across the app. + public var utilities: MalachiteClassesObject! + + /// An instance of MalachiteKit's ``Camera`` class. + var camera: Camera! + /// An instance of MalachiteKit's ``Location`` class. + var location: Location! + + /// An instance of ``CameraView/ControlLayer`` for this view. + var controlLayer: CameraView.ControlLayer! + /// An instance of ``CameraView/Notifications`` for this view. + var notifications: CameraView.Notifications! + /// An instance of ``CameraView/Preview`` for this view. + var preview: CameraView.Preview! + + /// A `CGFloat` that temporarily holds the zoom factor. + var zoomFloater = CGFloat() + /// A `Float` that temporarily holds the focus factor. + var focusFloater: Float? + /// A `Float` that temporarily holds the level of flash brightness to use. + var flashFloater: Float? + /// A `Bool` that temporarily holds the current status of the flashlight. + var flashStatus = Bool() + + /// The minimum zoom value that the ``zoomRecognizer`` is allowed to reach. + let minimumZoom: CGFloat = 1.0 + /// The maximum zoom value that the ``zoomRecognizer`` is allowed to reach. + let maximumZoom: CGFloat = 5.0 + /// The last known zoom factor that the ``zoomRecognizer`` was set to. + var lastZoomFactor: CGFloat = 1.0 + + /// A `UIActivityIndicatorView` used to let the user know that Malachite is processing the image. + var progressIndicator = UIActivityIndicatorView() + + /** + viewDidLoad override for the main user interface. + + This function currently serves to do the following: + - Create and assign a value to all variables needed to run ``cameraSession`` and its preview layer, ``cameraPreview``. + - Read the user's preferences to determine the preview layer's aspect ratio. + - Register notifications for changes to certain options in ``MalachiteSettingsView``, as well as orientation changes. + */ + override func viewDidLoad() { + super.viewDidLoad() + + if #unavailable(iOS 18.0) { overrideUserInterfaceStyle = .dark } + self.view.backgroundColor = .black + + self.notifications = CameraView.Notifications(delegate: self) + self.controlLayer = CameraView.ControlLayer(delegate: self) + + self.notifications.bringUpNotifications() + self.camera = Camera(utilities: utilities) + self.preview = CameraView.Preview(delegate: self) + + self.utilities.watch.bringUpCompanionConnection() + } + + @objc func cameraClassDidLoad() { + if camera.cameras.first != nil { + utilities.debugNSLog("[Initialization] Bringing up AVCaptureVideoPreviewLayer") + self.runInputSwitch() + preview.initPreviewLayer() + + utilities.debugNSLog("[Initialization] Starting session stream") + self.camera.queue.async { self.camera.session.startRunning() } + + if utilities.versionType == "INTERNAL" { + self.location = Location(utilities: utilities) + if self.location.locationEnabled { self.location.startLocationServices() } + } + } else { + utilities.debugNSLog("[Initialization] No cameras detected, skipping to user interface bringup") + } + } + + /** + viewDidAppear override for the main user interface. + + This function currently serves to do the following: + - Create all buttons and gestures required to operate the user interface. + - Set up GameKit integration for achievements and leaderboard reporting. + + `DEBUG` builds of Malachite additionally do the following: + - Dump the contents of UserDefaults. + - Dump the contents of Game Center achievements and leaderboards. + */ + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + utilities.debugNSLog("[Initialization] Presenting user interface") + (utilities.versionType == "INTERNAL") ? setupView_INTERNAL() : setupView() + } + + /** + Function used to determine what rotation Malachite should be in on iPadOS. + + iPhones follow the stock camera apps's behavior of only rotating buttons, while iPads get the ability to rotate the entire device. + TODO: fix bugs regarding this... + */ + func transformOrientation(orientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation { + switch orientation { + case .landscapeLeft: + return .landscapeLeft + case .landscapeRight: + return .landscapeRight + case .portraitUpsideDown: + return .portraitUpsideDown + default: + return .portrait + } + } + + func setupView_INTERNAL() { + utilities.games.changeGameCenterEnabled() + if utilities.preferences.general.gamekit.alerted { self.present(utilities.games.setupGameKitAlert(), animated: true, completion: nil) } + setupView() + } + + /** + Function to register buttons and gestures for operating Malachite. + + This function creates and provides layout properties for the following views: + - ``cameraButton`` + - ``flashlightButton`` + - ``captureButton`` + - ``focusButton`` + - ``focusSliderButton`` + - ``focusSlider`` + - ``focusLockButton`` + - ``exposureButton`` + - ``exposureSliderButton`` + - ``exposureSlider`` + - ``exposureLockButton`` + - ``settingsButton`` + + This function also creates the following gesture recognizers: + - ``zoomRecognizer`` - Pinch-to-zoom gesture + - ``aeafRecognizer`` - Tap and hold with one finger + - ``uiHiderRecognizer`` - Tap and hold with two fingers + */ + func setupView(){ +#if targetEnvironment(simulator) + utilities.views.setupLmaoView(view: self.view) +#endif + + self.controlLayer.bringUpControlLayer() + + utilities.function.changeIdleTimerState() + } + + /// Stub function. It literally does nothing. + @objc func stub() { } + + /// Function to dynamically update the aspect ratio for ``cameraPreview`` through ``MalachiteSettingsView``. + @objc func changeAspectFill() { + UIView.animate(withDuration: 20) { [self] in + if utilities.preferences.preview.aspect { + self.preview.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill + } else { + self.preview.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect + } + } + } + + /// Function to dynamically change the auto exposure and ``exposureSlider`` values when toggling in ``MalachiteSettingsView``. + @objc func changeExposureLimit() { + guard let exposure = camera.device?.isExposureModeSupported(.continuousAutoExposure) else { return } + do { + try camera.device?.lockForConfiguration() + defer { camera.device?.unlockForConfiguration() } + if exposure { camera.device?.exposureMode = .continuousAutoExposure } + } catch { + utilities.debugNSLog("[Change Exposure Limit] Couldn't lock device for configuration") + } + + UIView.animate(withDuration: 0.5) { + self.controlLayer.buttons.exposure.slider.value = 0.0 + } + } + + @objc func changeContinuousAEAF() { + guard let selectedDevice = camera.device else { return } + utilities.function.continuousAEAF(device: selectedDevice) + } + + @objc func changeAEAFRecognizer() { + let tapGestureElements = utilities.preferences.userInterface.tapAndHold + guard let currentGestureRecognizers = self.view.gestureRecognizers else { return } + + if tapGestureElements.contains("off") { + if currentGestureRecognizers.contains(self.controlLayer.recognizers.continuous) { + utilities.debugNSLog("[AE+AF] Disabling tap and hold gesture") + self.view.removeGestureRecognizer(self.controlLayer.recognizers.continuous) + } + } else { + if !currentGestureRecognizers.contains(self.controlLayer.recognizers.continuous) { + utilities.debugNSLog("[AE+AF] Enabling tap and hold gesture") + self.view.addGestureRecognizer(self.controlLayer.recognizers.continuous) + } + } + } + + /// Function to change the video stabilization mode for the ``cameraPreview``. + @objc func changeStabilizerMode() { + guard let connection = self.preview.previewLayer.connection else { return } + if utilities.preferences.preview.stablize { + if #available(iOS 17.0, *) { + if ((camera.device?.activeFormat.isVideoStabilizationModeSupported(.previewOptimized)) != nil) { + utilities.debugNSLog("[Preview Stabilization] Enabling enhanced stabilization mode") + connection.preferredVideoStabilizationMode = .previewOptimized + return + } + } + + if ((camera.device?.activeFormat.isVideoStabilizationModeSupported(.standard)) != nil) { + utilities.debugNSLog("[Preview Stabilization] Enabling standard stabilization mode") + connection.preferredVideoStabilizationMode = .standard + } + } else { + connection.preferredVideoStabilizationMode = .off + } + } + + /// Function to present ``MalachiteSettingsView`` + @objc func presentSettingsView() { +#if APP_EXTENSION + utilities.debugNSLog("[Settings] Attempt to access Settings UI from app extension") + let alert = utilities.views.createAlertController(title: "alert.title.app_extensions.settings", message: "alert.detail.app_extensions.settings", button: self.controlLayer.buttons.settings, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Settings] Dialog has been dismissed") + }) + DispatchQueue.main.async { self.present(alert, animated: true, completion: nil) } + return +#elseif MAIN_APP + var view = SettingsView(dismissAction: {self.dismiss( animated: true, completion: nil )}) + view.utilities = self.utilities + view.location = self.location + let hostingController = UIHostingController(rootView: view) + hostingController.modalPresentationStyle = UIModalPresentationStyle.popover + hostingController.popoverPresentationController?.sourceView = self.controlLayer.buttons.settings + hostingController.isModalInPresentation = true + if #available(iOS 26.0, *) { + hostingController.preferredTransition = .zoom { [self] _ in + self.controlLayer.buttons.settings + } + } + DispatchQueue.main.async { self.present(hostingController, animated: true, completion: nil) } +#endif + } + + /// Function to switch cameras and attach new camera.inputs to ``cameraSession``, and set settings based on the `activeFormat` of ``selectedDevice``. + @objc func runInputSwitch() { + DispatchQueue.main.async { self.controlLayer.buttons.camera.isUserInteractionEnabled = false } + if (self.camera.cameras.count < 2 || utilities.preferences.debug.breakApp) && !self.camera.session.inputs.isEmpty { + utilities.debugNSLog("[Camera Input] Only one AVCaptureDevice is available to use, showing error") + let alert = utilities.views.createAlertController(title: "alert.title.camera_switch", message: "alert.detail.camera_switch", button: self.controlLayer.buttons.camera, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Camera Input] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + self.controlLayer.buttons.camera.isUserInteractionEnabled = true + return + } + + DispatchQueue.main.async { + UIView.animate(withDuration: 0.5) { + self.controlLayer.buttons.focus.slider.value = 0.0 + self.controlLayer.buttons.exposure.slider.value = 0.0 + } + } + + camera.input.runInputSwitch() + + + if #available(iOS 18.0, *) { + if utilities.versionType == "INTERNAL" && utilities.preferences.evaintrnl.cameraControlEnabled { + DispatchQueue.main.async { self.controlLayer.initCameraControl() } + } + } + + DispatchQueue.main.async() { [self] in + DispatchQueue.main.async { self.controlLayer.initTooltips(showLabels: false, showCamera: true) } + } + + DispatchQueue.main.async { self.controlLayer.buttons.camera.isUserInteractionEnabled = true } + } + + @available(iOS 16.0, *) + @objc func runInputMegapixelSwitch() { + guard let selectedDevice = camera.device else { return } + utilities.function.switchInputMegapixels(device: selectedDevice, photoOutput: self.camera.output) + } + + /// Function to toggle the flashlight's on state. + @objc func runFlashlightToggle() { + guard let selectedDevice = camera.device else { return } + if selectedDevice.isFlashAvailable && !utilities.preferences.debug.breakApp { + utilities.function.toggleFlash(captureDevice: selectedDevice, + flashlightButton: self.controlLayer.buttons.flashlight, + floater: flashFloater, + isFlashOn: &flashStatus) + } else { + utilities.debugNSLog("[Flashlight] No flashlight available") + let alert = utilities.views.createAlertController(title: "alert.title.flashlight", message: "alert.detail.flashlight", button: self.controlLayer.buttons.flashlight, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Flashlight] Dialog has been dismissed") + }) + DispatchQueue.main.async { self.present(alert, animated: true, completion: nil) } + } + } + + /// Function to take an image. + @objc func runImageCapture() { + DispatchQueue.main.async { [self] in + self.controlLayer.buttons.capture.isEnabled = false + progressIndicator = UIActivityIndicatorView(frame: self.controlLayer.buttons.capture.frame) + self.view.addSubview(progressIndicator) + self.controlLayer.buttons.capture.setImage(nil, for: .normal) + progressIndicator.startAnimating() + } + + let status = PHPhotoLibrary.authorizationStatus(for: .addOnly) + + if (status == .authorized || status == .limited) && !utilities.preferences.debug.breakApp { + self.camera.output = utilities.function.captureImage(output: self.camera.output, viewForBounds: self.view, captureDelegate: self) + } else { + utilities.debugNSLog("[Capture Photo] PHPhotoLibrary not authorized, showing error") + let alert = utilities.views.createAlertController(title: "alert.title.phphotolibrary", message: "alert.detail.phphotolibrary", button: self.controlLayer.buttons.capture, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Capture Photo] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + /// Function for opening ``MalachitePhotoPreview`` and running GameKit commands after photo processing is completed. + func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { + guard let imageData = photo.fileDataRepresentation() else { return } + let getterForOrientation = UIImage(data: imageData) + let previewImage = UIImage(ciImage: CIImage(data: imageData, options: [.applyOrientationProperty: true, + .properties: [kCGImagePropertyOrientation: CGImagePropertyOrientation(getterForOrientation!.imageOrientation).rawValue]])!) + let photoPreview = PhotoPreviewView() + #warning("really need to get SceneDelegate.utilities up...") + photoPreview.utilities = utilities + photoPreview.location = location + photoPreview.photoImageData = imageData + photoPreview.photoImageView.frame = view.frame + photoPreview.photoImage = previewImage + if utilities.preferences.preview.fastPath { + photoPreview.savePhoto(finalImage: photoPreview.finalizeImageForExport(imageData: imageData)) + } else { + let navigationController = UINavigationController(rootViewController: photoPreview) + navigationController.modalPresentationStyle = UIModalPresentationStyle.pageSheet + navigationController.isModalInPresentation = true + navigationController.isNavigationBarHidden = true + navigationController.popoverPresentationController?.sourceView = self.controlLayer.buttons.capture + if #available(iOS 26.0, *) { + navigationController.preferredTransition = .zoom { [self] _ in self.controlLayer.buttons.capture } + } + self.present(navigationController, animated: true, completion: nil) + NotificationCenter.default.addObserver(photoPreview, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil) + } + + self.controlLayer.buttons.capture.isEnabled = true + self.controlLayer.buttons.capture.setImage(UIImage(systemName: "camera.aperture"), for: .normal) + progressIndicator.stopAnimating() + + DispatchQueue.global(qos: .background).async { [self] in + utilities.preferences.ext.runPhotoCounter() + if utilities.games.gameCenterEnabled { + let numPhotos = utilities.preferences.general.photoCount + if numPhotos == 1 { + let firstPhoto = utilities.games.achievements.pullAchievement(achievementName: "first_photo") + firstPhoto.percentComplete = 100 + utilities.games.achievements.pushAchievement(achievementBody: firstPhoto) + } + utilities.games.leaderboards.pushLeaderboard(scoreToSubmit: numPhotos, leaderboardToSubmit: "photos_taken") + } + } + } + + /// Function to zoom in and out with ``zoomRecognizer``. + @objc func runZoomController() { + guard let selectedDevice = camera.device else { return } + utilities.function.zoom(sender: self.controlLayer.recognizers.zoom, + floater: &zoomFloater, + captureDevice: selectedDevice, + lastZoomFactor: &lastZoomFactor, + hapticClass: utilities.haptics) + } + + /// Function to autofocus + autoexposure with ``aeafRecognizer``. + @objc func runaeafController() { + guard let selectedDevice = camera.device else { return } + utilities.function.pointOfInterestAEAF(sender: self.controlLayer.recognizers.continuous, + captureDevice: selectedDevice, + button: self.controlLayer.buttons.continuousFeedback, + viewForScale: self.view, + hapticClass: utilities.haptics) + } + + /// Function to handle ``exposureSlider`` interaction. + @objc func runManualExposureController() { + guard let selectedDevice = camera.device else { return } + if selectedDevice.isExposureModeSupported(.custom) && !utilities.preferences.debug.breakApp { + utilities.function.manualExposure(captureDevice: selectedDevice, + sender: self.controlLayer.buttons.exposure.slider) + } else { + utilities.debugNSLog("[Manual Exposure] Current camera is not capable of adjusting exposure") + let alert = utilities.views.createAlertController(title: "alert.title.exposure", message: "alert.detail.exposure", button: self.controlLayer.buttons.exposure.activator, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Manual Exposure] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + /// Function to show and hide the ``exposureSliderButton`` and ``exposureLockButton``. + @objc func runManualExposureUIHider() { + guard let exposure = camera.device?.isExposureModeSupported(.custom) else { return } + if exposure && !utilities.preferences.debug.breakApp { + self.controlLayer.hideOtherSliders(group: self.controlLayer.buttons.exposure) + self.controlLayer.buttons.exposure.sliderShown = utilities.views.sliders.runHiders(group: self.controlLayer.buttons.exposure) + } else { + utilities.debugNSLog("[Manual Focus] Current camera is not capable of adjusting exposure") + let alert = utilities.views.createAlertController(title: "alert.title.exposure", message: "alert.detail.exposure", button: self.controlLayer.buttons.exposure.activator, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Manual Exposure] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + @objc func runManualExposureUIHiderWhenUnsupported() { + self.controlLayer.buttons.exposure.sliderShown = utilities.views.sliders.runHiders(group: self.controlLayer.buttons.exposure) + } + + /// Function to lock and unlock the ``exposureSlider``. + @objc func runManualExposureLockController() { + self.controlLayer.buttons.exposure.lockEnabled = utilities.views.sliders.runLocks(group: self.controlLayer.buttons.exposure, + associatedGestureRecognizer: nil, + viewForRecognizers: self.view) + } + + /// Function to handle ``focusSlider`` interaction. + @objc func runManualFocusController() { + guard let selectedDevice = camera.device else { return } + if selectedDevice.isLockingFocusWithCustomLensPositionSupported && !utilities.preferences.debug.breakApp { + utilities.function.manualFocus(captureDevice: selectedDevice, + sender: self.controlLayer.buttons.focus.slider, + floater: focusFloater ?? self.controlLayer.buttons.focus.slider.value) + } else { + #warning("refactor to call unsupported codepath") + utilities.debugNSLog("[Manual Focus] Current camera is not capable of adjusting focus") + let alert = utilities.views.createAlertController(title: "alert.title.focus", message: "alert.detail.focus", button: self.controlLayer.buttons.focus.activator, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Manual Focus] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + /// Function to handle ``focusSlider`` interaction. + @objc func runManualFocusUIHider() { + guard let selectedDevice = camera.device else { return } + if selectedDevice.isLockingFocusWithCustomLensPositionSupported && !utilities.preferences.debug.breakApp { + self.controlLayer.hideOtherSliders(group: self.controlLayer.buttons.focus) + self.controlLayer.buttons.focus.sliderShown = utilities.views.sliders.runHiders(group: self.controlLayer.buttons.focus) + } else { + utilities.debugNSLog("[Manual Focus] Current camera is not capable of adjusting focus") + let alert = utilities.views.createAlertController(title: "alert.title.focus", message: "alert.detail.focus", button: self.controlLayer.buttons.focus.activator, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Manual Focus] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + @objc func runManualFocusUIHiderWhenUnsupported() { + self.controlLayer.buttons.focus.sliderShown = utilities.views.sliders.runHiders(group: self.controlLayer.buttons.focus) + } + + /// Function to show and hide the ``focusSliderButton`` and ``focusLockButton``. + @objc func runManualFocusLockController() { + self.controlLayer.buttons.focus.lockEnabled = utilities.views.sliders.runLocks(group: self.controlLayer.buttons.focus, + associatedGestureRecognizer: self.controlLayer.recognizers.continuous, + viewForRecognizers: self.view) + } + + /// Function to handle ``focusSlider`` interaction. + @objc func runManualFlashController() { + guard let selectedDevice = camera.device else { return } + if selectedDevice.hasTorch && !utilities.preferences.debug.breakApp { + if (self.controlLayer.buttons.flash.slider.value == 0.0 && flashStatus) || (self.controlLayer.buttons.flash.slider.value != 0.0 && !flashStatus) { + flashFloater = self.controlLayer.buttons.flash.slider.value + runFlashlightToggle() + flashFloater = nil + } else { + utilities.function.flashLevelTest(captureDevice: selectedDevice, + floater: flashFloater ?? self.controlLayer.buttons.flash.slider.value) + } + + } else { + #warning("refactor to call unsupported codepath") + utilities.debugNSLog("[Flashlight Level] Device does not have a flashlight") + let alert = utilities.views.createAlertController(title: "alert.title.flashlight", message: "alert.detail.flashlight", button: self.controlLayer.buttons.flash.activator, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Flashlight Level] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + /// Function to handle ``focusSlider`` interaction. + @objc func runManualFlashUIHider() { + guard let selectedDevice = camera.device else { return } + if selectedDevice.hasTorch && !utilities.preferences.debug.breakApp { + self.controlLayer.hideOtherSliders(group: self.controlLayer.buttons.flash) + self.controlLayer.buttons.flash.sliderShown = utilities.views.sliders.runHiders(group: self.controlLayer.buttons.flash) + } else { + utilities.debugNSLog("[Flashlight Level] Device does not have a flashlight") + let alert = utilities.views.createAlertController(title: "alert.title.flashlight", message: "alert.detail.flashlight", button: self.controlLayer.buttons.flash.activator, defaultSet: true, action: { _ in + self.utilities.debugNSLog("[Flashlight Level] Dialog has been dismissed") + }) + self.present(alert, animated: true, completion: nil) + } + } + + @objc func runManualFlashUIHiderWhenUnsupported() { + self.controlLayer.buttons.flash.sliderShown = utilities.views.sliders.runHiders(group: self.controlLayer.buttons.flash) + } + + /// Function to show and hide the ``focusSliderButton`` and ``focusLockButton``. + @objc func runManualFlashLockController() { + self.controlLayer.buttons.flash.lockEnabled = utilities.views.sliders.runLocks(group: self.controlLayer.buttons.flash, + associatedGestureRecognizer: nil, + viewForRecognizers: self.view) + } + + /// Function to handle device rotation. + #warning("refactor to view utils") + @objc func orientationChanged() { + utilities.views.rotateButtonsWithOrientation(buttonsToRotate: [ self.controlLayer.buttons.camera, + self.controlLayer.buttons.flashlight, + self.controlLayer.buttons.capture, + self.controlLayer.buttons.settings, + self.controlLayer.buttons.focus.activator, + self.controlLayer.buttons.focus.lock, + self.controlLayer.buttons.exposure.activator, + self.controlLayer.buttons.exposure.lock, + self.controlLayer.buttons.flash.activator, + self.controlLayer.buttons.flash.lock ]) + } +} + diff --git a/mlchtCamera/Views/CompatibilityView/CompatibilityView+Camera.swift b/mlchtCamera/Views/CompatibilityView/CompatibilityView+Camera.swift new file mode 100644 index 0000000..0ccf88f --- /dev/null +++ b/mlchtCamera/Views/CompatibilityView/CompatibilityView+Camera.swift @@ -0,0 +1,35 @@ +// +// CompatibilityView+Camera.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/17/25. +// + +import SwiftUI + +extension CompatibilityView { + struct Cameras: View { + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("compatibility.header.cameras")) { + MalachiteCompatibilityViewUtils( + title: "compatibility.title.ultrawide", + available: utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.ultrawide) ? true : false) + MalachiteCompatibilityViewUtils( + title: "compatibility.title.wide", + available: utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.wideangle) ? true : false) + MalachiteCompatibilityViewUtils( + title: "compatibility.title.telephoto", + available: utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.telephoto) ? true : false) + } + } + } +} diff --git a/mlchtCamera/Views/CompatibilityView/CompatibilityView+Format.swift b/mlchtCamera/Views/CompatibilityView/CompatibilityView+Format.swift new file mode 100644 index 0000000..463e28c --- /dev/null +++ b/mlchtCamera/Views/CompatibilityView/CompatibilityView+Format.swift @@ -0,0 +1,28 @@ +// +// CompatibilityView+Format.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/17/25. +// + +import SwiftUI + +extension CompatibilityView { + struct Formats: View { + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("compatibility.header.format")) { + MalachiteCompatibilityViewUtils(title: "compatibility.title.jpeg", available: utilities.preferences.compatibility.jpeg) + MalachiteCompatibilityViewUtils(title: "compatibility.title.heic", available: utilities.preferences.compatibility.heic) + } + } + } +} diff --git a/mlchtCamera/Views/CompatibilityView/CompatibilityView+Resolutions.swift b/mlchtCamera/Views/CompatibilityView/CompatibilityView+Resolutions.swift new file mode 100644 index 0000000..0496bbd --- /dev/null +++ b/mlchtCamera/Views/CompatibilityView/CompatibilityView+Resolutions.swift @@ -0,0 +1,42 @@ +// +// CompatibilityView+Resolutions.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/17/25. +// + +import SwiftUI + +extension CompatibilityView { + struct Resolutions: View { + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("compatibility.header.resolution")) { + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.ultrawide) { + // Ultra wide megapixel capabilities + MalachiteCompatibilityViewUtils(title: "compatibility.title.12mp.ultrawide", available: utilities.preferences.compatibility.ultrawide["12"] ?? false) + MalachiteCompatibilityViewUtils(title: "compatibility.title.48mp.ultrawide", available: utilities.preferences.compatibility.ultrawide["48"] ?? false) + } + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.wideangle) { + // Wide angle megapixel capabilities + MalachiteCompatibilityViewUtils(title: "compatibility.title.8mp.wide", available: utilities.preferences.compatibility.wideangle["8"] ?? false) + MalachiteCompatibilityViewUtils(title: "compatibility.title.12mp.wide", available: utilities.preferences.compatibility.wideangle["12"] ?? false) + MalachiteCompatibilityViewUtils(title: "compatibility.title.48mp.wide", available: utilities.preferences.compatibility.wideangle["48"] ?? false) + } + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.telephoto) { + // Telephoto megapixel capabilities + MalachiteCompatibilityViewUtils(title: "compatibility.title.12mp.telephoto", available: utilities.preferences.compatibility.telephoto["12"] ?? false) + MalachiteCompatibilityViewUtils(title: "compatibility.title.48mp.telephoto", available: utilities.preferences.compatibility.telephoto["48"] ?? false) + } + } + } + } +} diff --git a/mlchtCamera/Views/CompatibilityView/CompatibilityView.swift b/mlchtCamera/Views/CompatibilityView/CompatibilityView.swift new file mode 100755 index 0000000..cfbcf72 --- /dev/null +++ b/mlchtCamera/Views/CompatibilityView/CompatibilityView.swift @@ -0,0 +1,51 @@ +// +// CompatibilityView.swift +// Malachite +// +// Created by Eva Isabella Luna on 10/21/24. +// + +import SwiftUI + +public struct CompatibilityView: View { + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + public var body: some View { + Form { + Section { + Text("compatibility.note") + } + Cameras(utilities: utilities) + Resolutions(utilities: utilities) + Formats(utilities: utilities) + Misc(utilities: utilities) + } + .navigationTitle("view.title.compatibility") + .toolbar(content: { + ToolbarItemGroup(placement: .topBarTrailing) { + MalachiteToolbarUtils(action: self.dismissAction, image: "checkmark", primary: true) + } + }) + } + + @available(*, deprecated, message: "Avoid using the Misc structure. When possible, build into a more descriptive structure.") + struct Misc: View { + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + ) { + self.utilities = utilities + } + + var body: some View { + Section { + MalachiteCompatibilityViewUtils(title: "compatibility.title.hdr", available: utilities.preferences.compatibility.hdr) + } + } + } +} diff --git a/mlchtCamera/Views/DeveloperView/DeveloperView+BuildInfo.swift b/mlchtCamera/Views/DeveloperView/DeveloperView+BuildInfo.swift new file mode 100644 index 0000000..6d379c9 --- /dev/null +++ b/mlchtCamera/Views/DeveloperView/DeveloperView+BuildInfo.swift @@ -0,0 +1,35 @@ +// +// DeveloperView+BuildInfo.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/15/25. +// + +import SwiftUI + +extension DeveloperView { + struct BuildInfo: View { + var utilities: MalachiteClassesObject + + init( + utilities: MalachiteClassesObject + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("developer.header.info")) { + if utilities.versionType == "DEBUG" || utilities.versionType == "INTERNAL" { + createBuildInformation(label: "developer.option.version_type", value: utilities.versionType) + createBuildInformation(label: "developer.option.version_branch", value: utilities.versionBranch) + createBuildInformation(label: "developer.option.version_hash", value: utilities.versionHash) + createBuildInformation(label: "developer.option.version_date", value: utilities.versionDate) + } + if utilities.versionType == "INTERNAL" { + createBuildInformation(label: "developer.option.version_user", value: utilities.versionUser) + createBuildInformation(label: "developer.option.version_host", value: utilities.versionHost) + } + } + } + } +} diff --git a/mlchtCamera/Views/DeveloperView/DeveloperView+DeviceInfo.swift b/mlchtCamera/Views/DeveloperView/DeveloperView+DeviceInfo.swift new file mode 100644 index 0000000..638b5a0 --- /dev/null +++ b/mlchtCamera/Views/DeveloperView/DeveloperView+DeviceInfo.swift @@ -0,0 +1,54 @@ +// +// DeveloperView+DeviceInfo.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/15/25. +// + +import SwiftUI +import Foundation + +extension DeveloperView { + struct DeviceInfo: View { + var utilities: MalachiteClassesObject + + init( + utilities: MalachiteClassesObject + ) { + self.utilities = utilities + } + + var body: some View { + Section(header: Text("developer.header.device"), footer: Text("developer.footer.device")) { + createBuildInformation(label: "developer.option.device_model", value: utilities.preferences.compatibility.device.model) + createBuildInformation(label: "developer.option.device_version", value: getCurrentOSVersion()) + createBuildInformation(label: "developer.option.device_build", value: getCurrentOSBuild()) + } + } + + @available(*, deprecated, message: "Will be renamed in MalachiteKit.") + func getCurrentOSVersion() -> String { + var osVersion: String + + // Doing it like this to possibly add "iOS" "macOS" "watchOS" in the future + osVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion.description + osVersion += "." + osVersion += ProcessInfo.processInfo.operatingSystemVersion.minorVersion.description + osVersion += "." + osVersion += ProcessInfo.processInfo.operatingSystemVersion.patchVersion.description + + return osVersion + } + + @available(*, deprecated, message: "Will be renamed in MalachiteKit.") + func getCurrentOSBuild() -> String { + var size = 0 + sysctlbyname("kern.osversion", nil, &size, nil, 0) + var buffer = [CChar](repeating: 0, count: size) + let result = sysctlbyname("kern.osversion", &buffer, &size, nil, 0) + if result == 0 { return String(cString: buffer) } + + return "Unknown" + } + } +} diff --git a/mlchtCamera/Views/DeveloperView/DeveloperView+Internal.swift b/mlchtCamera/Views/DeveloperView/DeveloperView+Internal.swift new file mode 100644 index 0000000..1366c70 --- /dev/null +++ b/mlchtCamera/Views/DeveloperView/DeveloperView+Internal.swift @@ -0,0 +1,107 @@ +// +// DeveloperView+Internal.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension DeveloperView { + struct InternalSettings: View { + /// A State variable used for determining how many fingers are used for the settings gesture. + @State private var settingsGestureFingers = Int() + /// A State variable used for determining whether or not to block accidental gestures. + @State private var blockAccidentalGestures = Bool() + /// A State variable used for determining whether or not to enable the Camera Control. + @State private var cameraControlEnabled = Bool() + /// A State variable used for determining whether or not to enable geotagging images. + @State private var locationEnabled = Bool() + + var utilities: MalachiteClassesObject + var location: Location + + init( + utilities: MalachiteClassesObject, + location: Location + ) { + self.utilities = utilities + self.location = location + } + + var body: some View { + Section(header: Text("developer.header.internal")) { + MalachiteCellViewUtils( + icon: "hand.draw", + disabled: nil, + dangerous: false) + { + Picker("internal.option.settingsgesture", selection: $settingsGestureFingers) { + Text("internal.option.settingsgesture.1") + .tag(1) + Text("internal.option.settingsgesture.2") + .tag(2) + Text("internal.option.settingsgesture.3") + .tag(3) + } + } + MalachiteCellViewUtils( + icon: "", + disabled: nil, + dangerous: false) + { + Toggle("internal.option.blockaccidentalgestures", isOn: $blockAccidentalGestures) + } + if #available(iOS 18.0, *) { + MalachiteCellViewUtils( + icon: "", + disabled: nil, + dangerous: false) + { + Toggle("internal.option.cameracontrol", isOn: $cameraControlEnabled) + } + } + MalachiteCellViewUtils( + icon: "", + disabled: !location.locationEnabled, + dangerous: false) + { + Toggle("internal.option.location", isOn: $locationEnabled) + } + #warning("do camera control options") + } + .onAppear(perform: onAppear) + .onDisappear(perform: onDisappear) + .onChange(of: settingsGestureFingers) {_ in + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.settingsGestureNotification.name, object: nil) + + utilities.preferences.evaintrnl.settingsGesture = settingsGestureFingers + } + .onChange(of: blockAccidentalGestures) {_ in + utilities.preferences.evaintrnl.blockAccidentalGestures = blockAccidentalGestures + } + .onChange(of: cameraControlEnabled) {_ in + utilities.preferences.evaintrnl.cameraControlEnabled = cameraControlEnabled + } + .onChange(of: locationEnabled) {_ in + if location.locationEnabled { utilities.preferences.evaintrnl.locationEnabled = locationEnabled } + } + } + + func onAppear() { + settingsGestureFingers = utilities.preferences.evaintrnl.settingsGesture + blockAccidentalGestures = utilities.preferences.evaintrnl.blockAccidentalGestures + cameraControlEnabled = utilities.preferences.evaintrnl.cameraControlEnabled + locationEnabled = location.locationEnabled ? utilities.preferences.evaintrnl.locationEnabled : false + } + + func onDisappear() { + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.settingsGestureNotification.name, object: nil) + + utilities.preferences.evaintrnl.settingsGesture = settingsGestureFingers + utilities.preferences.evaintrnl.blockAccidentalGestures = blockAccidentalGestures + utilities.preferences.evaintrnl.cameraControlEnabled = cameraControlEnabled + if location.locationEnabled { utilities.preferences.evaintrnl.locationEnabled = locationEnabled } + } + } +} diff --git a/mlchtCamera/Views/DeveloperView/DeveloperView+Settings.swift b/mlchtCamera/Views/DeveloperView/DeveloperView+Settings.swift new file mode 100644 index 0000000..7951a11 --- /dev/null +++ b/mlchtCamera/Views/DeveloperView/DeveloperView+Settings.swift @@ -0,0 +1,135 @@ +// +// DeveloperView+Settings.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/15/25. +// + +import SwiftUI + +extension DeveloperView { + struct Settings: View { + @State private var debugLoggingUnified = false + @State private var debugLoggingPreferences = false + @State private var debugLoggingImageProps = false + @State private var forceCompatibilityRechecks = false + /// A State variable used for determining whether or not to literally break the app. + @State private var breakApp = false + + var utilities: MalachiteClassesObject + + init( + utilities: MalachiteClassesObject + ) { + self.utilities = utilities + } + + var body: some View { + Section { + MalachiteCellViewUtils( + icon: "text.redaction", + disabled: utilities.versionType == "INTERNAL", + dangerous: false) + { + Toggle("developer.option.debug.logging.unified", isOn: $debugLoggingUnified) + } + MalachiteCellViewUtils( + icon: "slider.horizontal.3", + disabled: nil, + dangerous: false) + { + Toggle("developer.option.debug.logging.preferences", isOn: $debugLoggingPreferences) + } + MalachiteCellViewUtils( + icon: "camera.badge.ellipsis", + disabled: nil, + dangerous: false) + { + Toggle("developer.option.debug.logging.imageprops", isOn: $debugLoggingImageProps) + } + MalachiteCellViewUtils( + icon: "checkmark.seal", + disabled: nil, + dangerous: false) + { + Toggle("developer.option.debug.forcecheck", isOn: $forceCompatibilityRechecks) + } + MalachiteCellViewUtils( + icon: "iphone.slash", + disabled: nil, + dangerous: false) + { + Toggle("developer.option.debug.breakapp", isOn: $breakApp) + } + MalachiteCellViewUtils( + icon: "trash", + disabled: nil, + dangerous: true) + { + Button { + utilities.debugNSLog("[Preferences] Resetting all preferences, relaunch the app to complete!") + utilities.preferences.ext.resetPreferences() + } label: { + if #available(iOS 17.0, *) { + Text("developer.option.debug.erase.preferences") + .foregroundStyle(.red) + } else { + Text("developer.option.debug.erase.preferences") + .foregroundColor(.red) + } + } + } + + if utilities.versionType == "INTERNAL" { + MalachiteCellViewUtils( + icon: "trash", + disabled: nil, + dangerous: true) + { + Button { + utilities.internalNSLog("[Preferences] Resetting all GameKit data!") + utilities.games.achievements.resetAchievements() + } label: { + if #available(iOS 17.0, *) { + Text("developer.option.debug.erase.gamekit") + .foregroundStyle(.red) + } else { + Text("developer.option.debug.erase.gamekit") + .foregroundColor(.red) + } + } + } + } + } + .onAppear { + if utilities.versionType == "INTERNAL" { debugLoggingUnified = true } + else { debugLoggingUnified = utilities.preferences.debug.logging.unified } + debugLoggingPreferences = utilities.preferences.debug.logging.preferences + debugLoggingImageProps = utilities.preferences.debug.logging.imageProps + forceCompatibilityRechecks = utilities.preferences.debug.compatibility.forcecheck + breakApp = utilities.preferences.debug.breakApp + } + .onChange(of: debugLoggingUnified) {_ in + utilities.preferences.debug.logging.unified = debugLoggingUnified + } + .onChange(of: debugLoggingPreferences) {_ in + utilities.preferences.debug.logging.preferences = debugLoggingPreferences + } + .onChange(of: debugLoggingImageProps) {_ in + utilities.preferences.debug.logging.imageProps = debugLoggingImageProps + } + .onChange(of: forceCompatibilityRechecks) {_ in + utilities.preferences.debug.compatibility.forcecheck = forceCompatibilityRechecks + } + .onChange(of: breakApp) {_ in + utilities.preferences.debug.breakApp = breakApp + } + .onDisappear { + utilities.preferences.debug.logging.unified = debugLoggingUnified + utilities.preferences.debug.logging.preferences = debugLoggingPreferences + utilities.preferences.debug.logging.imageProps = debugLoggingImageProps + utilities.preferences.debug.breakApp = breakApp + } + } + } +} diff --git a/mlchtCamera/Views/DeveloperView/DeveloperView.swift b/mlchtCamera/Views/DeveloperView/DeveloperView.swift new file mode 100644 index 0000000..f6f7db4 --- /dev/null +++ b/mlchtCamera/Views/DeveloperView/DeveloperView.swift @@ -0,0 +1,62 @@ +// +// DeveloperView.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/15/25. +// + +import SwiftUI +import UIKit + +struct DeveloperView: View { + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities: MalachiteClassesObject + + var location: Location + + var body: some View { + Form { + Settings(utilities: utilities) + if utilities.versionType == "INTERNAL" { InternalSettings(utilities: utilities, location: location)} + BuildInfo(utilities: utilities) + DeviceInfo(utilities: utilities) + } + .navigationTitle("view.title.developer") + .toolbar(content: { + #warning("log export implementation eta") + ToolbarItemGroup(placement: .topBarLeading) { + if #available(iOS 26.0, *) { help } + } + ToolbarItemGroup(placement: .topBarTrailing) { + if #unavailable(iOS 26.0) { help; } + MalachiteToolbarUtils(action: self.dismissAction, image: "checkmark", primary: true) + } + }) + } + + var help: some View { + NavigationLink(destination: QuickHelpViewDeveloper(utilities: utilities, dismissAction: dismissAction)) { + if #available(iOS 26.0, *) { + Image(systemName: "questionmark.circle") + } else { + Image(systemName: "questionmark.circle").tint(.primary) + } + } + } + + struct createBuildInformation: View { + var label: LocalizedStringKey + var value: String + var body: some View { + HStack { + Text(label) + .frame(alignment: .leading) + Spacer() + Text(value) + .frame(alignment: .trailing) + } + } + } +} diff --git a/mlchtCamera/Views/PhotoPreviewView/PhotoPreviewView+Controls.swift b/mlchtCamera/Views/PhotoPreviewView/PhotoPreviewView+Controls.swift new file mode 100644 index 0000000..4642cdb --- /dev/null +++ b/mlchtCamera/Views/PhotoPreviewView/PhotoPreviewView+Controls.swift @@ -0,0 +1,66 @@ +// +// PhotoPreviewView+Controls.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/27/25. +// + +import Foundation +import UIKit + +extension PhotoPreviewView { + class controls: NSObject { + /// The existing instance of ``PhotoPreviewView`` to act on. + var delegate = PhotoPreviewView() + + init(delegate: PhotoPreviewView) { self.delegate = delegate } + + func initButtons() { + let buttonConstraints: [MalachiteViewUtils.buttonBuilder.constraints] = [ + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 10.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 80.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + MalachiteViewUtils.buttonBuilder.constraints(LXA: delegate.view.safeAreaLayoutGuide.trailingAnchor, LXC: -10.0, LXP: false, LYA: delegate.view.safeAreaLayoutGuide.topAnchor, LYC: 150.0, LYP: false, CXA: nil, CXC: nil, CYA: nil, CYC: nil), + ] + + let buttonConfigs: [MalachiteViewUtils.buttonBuilder] = [ + MalachiteViewUtils.buttonBuilder(symbolName: "xmark", action: #selector(delegate.dismissView), dimensions: [ 60.0 ], constraints: buttonConstraints[0], hidden: false, assign: { [self] button in delegate.dismissButton = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "photo.on.rectangle", action: #selector(delegate.savePhotoWrapped), dimensions: [ 60.0 ], constraints: buttonConstraints[1], hidden: false, assign: { [self] button in delegate.savePhotoButton = button }), + MalachiteViewUtils.buttonBuilder(symbolName: "square.and.arrow.up", action: #selector(delegate.sharePhoto), dimensions: [ 60.0 ], constraints: buttonConstraints[2], hidden: false, assign: { [self] button in delegate.sharePhotoButton = button }), + ] + + for config in buttonConfigs { + let button = delegate.utilities.views.createAndAddButtonToView(symbolName: config.symbolName, delegate: delegate, view: delegate.view, utilities: delegate.utilities, action: config.action, dimensions: config.dimensions, constraints: config.constraints) + config.assign(button) + } + } + + func initRecognizers() { + let doubleTapRecognizer = UITapGestureRecognizer(target: delegate, action: #selector(handleDoubleTap(_:))) + doubleTapRecognizer.numberOfTapsRequired = 2 + delegate.photoScrollView.addGestureRecognizer(doubleTapRecognizer) + } + + func initTooltips(showLabels: Bool) { + if showLabels { + let tooltipConfigs: [ MalachiteViewUtils.tooltipBuilder ] = [ + MalachiteViewUtils.tooltipBuilder(text: "uibutton.close.title", anchor: 10, assign: { [self] label in delegate.dismissTitle = label } ), + MalachiteViewUtils.tooltipBuilder(text: "uibutton.save.title", anchor: 80, assign: { [self] label in delegate.savePhotoTitle = label } ), + MalachiteViewUtils.tooltipBuilder(text: "uibutton.share.title", anchor: 150, assign: { [self] label in delegate.sharePhotoTitle = label } ), + ] + + var labels: [ UILabel ] = [] + for config in tooltipConfigs { + labels.append(delegate.utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: delegate.view, textForFlow: NSLocalizedString(config.text.localized, comment: ""), anchorConstant: config.anchor)) + } + + delegate.utilities.tooltips.fadeOutTooltipFlow(labelsToFade: labels) + } + } + + func bringUpControlLayer() { + initButtons() + initRecognizers() + initTooltips(showLabels: true) + } + } +} diff --git a/Malachite/Views/MalachitePhotoPreview.swift b/mlchtCamera/Views/PhotoPreviewView/PhotoPreviewView.swift similarity index 61% rename from Malachite/Views/MalachitePhotoPreview.swift rename to mlchtCamera/Views/PhotoPreviewView/PhotoPreviewView.swift index dcda68d..e03dc46 100755 --- a/Malachite/Views/MalachitePhotoPreview.swift +++ b/mlchtCamera/Views/PhotoPreviewView/PhotoPreviewView.swift @@ -10,13 +10,17 @@ import UIKit import Photos import LinkPresentation -class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { +class PhotoPreviewView : UIViewController, UIScrollViewDelegate { /// A variable to hold the existing instance of ``MalachiteClassesObject``. - var utilities = MalachiteClassesObject() + var utilities: MalachiteClassesObject! + /// A variable to hold the existing instance of ``Location``. + var location: Location! /// The scroll view that holds the image view for zooming and panning. var photoScrollView = UIScrollView() + var controls: controls? + /** The image view that holds the captuerd image for user review. @@ -59,8 +63,8 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { /// A variable to store whether or not HDR is enabled. let enableHDR = MalachitePreferencesUtils.shared.preferences.capture.hdr - /// A variable to store whether or not the HEIF file format is enabled. - let enableHEIF = MalachitePreferencesUtils.shared.preferences.capture.format.heic + /// A variable to store whether or not the HEIC file format is enabled. + let enableHEIC = MalachitePreferencesUtils.shared.preferences.capture.format.heic /** viewDidLoad override for the main user interface. @@ -75,7 +79,11 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { // TODO: dev/malachitekit refactor this file self.finalizedImage = self.finalizeImageForExport(imageData: self.photoImageData) + self.controls = PhotoPreviewView.controls(delegate: self) + super.viewDidLoad() + + if #unavailable(iOS 18.0) { overrideUserInterfaceStyle = .dark } self.view.backgroundColor = .red var rotation = -1.0 @@ -133,48 +141,11 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { photoScrollView.contentInset = UIEdgeInsets(top: yOffset, left: xOffset, bottom: yOffset, right: xOffset) - dismissTitle = utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: view, textForFlow: NSLocalizedString("uibutton.close.title", comment: ""), anchorConstant: 10) - savePhotoTitle = utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: view, textForFlow: NSLocalizedString( "uibutton.save.title", comment: ""), anchorConstant: 80) - sharePhotoTitle = utilities.tooltips.returnLabelForTooltipFlows(viewForBounds: view, textForFlow: NSLocalizedString( "uibutton.share.title", comment: ""), anchorConstant: 150) - self.view.addSubview(blurredBackgroundView) photoScrollView.addSubview(photoImageView) self.view.addSubview(photoScrollView) - let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:))) - doubleTapRecognizer.numberOfTapsRequired = 2 - photoScrollView.addGestureRecognizer(doubleTapRecognizer) - - - dismissButton = utilities.views.returnProperButton(symbolName: "xmark", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - self.view.addSubview(dismissButton) - NSLayoutConstraint.activate([ - dismissButton.widthAnchor.constraint(equalToConstant: 60), - dismissButton.heightAnchor.constraint(equalToConstant: 60), - dismissButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10), - dismissButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - ]) - dismissButton.addTarget(self, action: #selector(self.dismissView), for: .touchUpInside) - - savePhotoButton = utilities.views.returnProperButton(symbolName: "photo.on.rectangle", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - self.view.addSubview(savePhotoButton) - NSLayoutConstraint.activate([ - savePhotoButton.widthAnchor.constraint(equalToConstant: 60), - savePhotoButton.heightAnchor.constraint(equalToConstant: 60), - savePhotoButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 80), - savePhotoButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - ]) - savePhotoButton.addTarget(self, action: #selector(self.savePhotoWrapped), for: .touchUpInside) - - sharePhotoButton = utilities.views.returnProperButton(symbolName: "square.and.arrow.up", cornerRadius: 30, viewForBounds: self.view, hapticClass: utilities.haptics) - self.view.addSubview(sharePhotoButton) - NSLayoutConstraint.activate([ - sharePhotoButton.widthAnchor.constraint(equalToConstant: 60), - sharePhotoButton.heightAnchor.constraint(equalToConstant: 60), - sharePhotoButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 150), - sharePhotoButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - ]) - sharePhotoButton.addTarget(self, action: #selector(self.sharePhoto), for: .touchUpInside) + self.controls!.bringUpControlLayer() orientationChanged() } @@ -183,7 +154,7 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { return photoImageView } - @objc private func handleDoubleTap(_ sender: UITapGestureRecognizer) { + @objc func handleDoubleTap(_ sender: UITapGestureRecognizer) { if photoScrollView.zoomScale == 1 { photoScrollView.setZoomScale(2, animated: true) } else { @@ -192,7 +163,7 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { } /// Function to allow the user to close the model view. - @objc private func dismissView() { + @objc func dismissView() { DispatchQueue.main.async { self.navigationController?.dismiss(animated: true) } @@ -224,8 +195,8 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { } /// Function to share the image to other apps or people without saving to the Photos library. - @objc private func sharePhoto() { - let shareableData = try! dataToShareable(data: finalizedImage, title: "sharable.title") + @objc func sharePhoto() { + let shareableData = try! dataToShareable(data: finalizedImage, title: "sharable.title".localized) let shareSheet = UIActivityViewController(activityItems: [shareableData], applicationActivities: nil) shareSheet.popoverPresentationController?.sourceView = sharePhotoButton if #available(iOS 26.0, *) { @@ -241,43 +212,83 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { - Creates an image from the ``photoImageData`` that was passed on creation of the view controller. - If ``enableHDR`` is enabled, creates a gain map image with HDR data inside. - If the user has enabled watermarking, creates an image with the watermark and the original image's dimensions. - - If ``enableHEIF`` is enabled, create a HEIC representation of all above images combined. Otherwise, JPEG is used. + - If ``enableHEIC`` is enabled, create a HEIC representation of all above images combined. Otherwise, JPEG is used. */ public func finalizeImageForExport(imageData: Data) -> Data { - var data = Data() - var rawImage = CIImage() - var gainMapImage = CIImage() - - if enableHDR { - rawImage = CIImage(data: imageData)! + guard let rawImage = CIImage(data: imageData, options: [.toneMapHDRtoSDR : (enableHDR ? true : false)]) else { return Data() } + + let exifOrientationValue = (rawImage.properties[kCGImagePropertyOrientation as String] as? NSNumber)?.intValue + let orientedCI: CIImage + if let exif = exifOrientationValue, let cgOrientation = CGImagePropertyOrientation(rawValue: UInt32(exif)) { + orientedCI = rawImage.oriented(cgOrientation) } else { - rawImage = CIImage(data: imageData, - options: [.toneMapHDRtoSDR : true])! + orientedCI = rawImage } + + let context = CIContext() + guard let cg = context.createCGImage(orientedCI, from: orientedCI.extent) else { return Data() } + let upright = CIImage(cgImage: cg) var imageProperties = rawImage.properties - let watermarkImage = CIImage(image: self.watermark()) - let outputImage = watermarkImage!.composited(over: rawImage) - if enableHDR { - gainMapImage = returnGainMap(properties: &imageProperties, imageData: imageData) + if var tiff = imageProperties[kCGImagePropertyTIFFDictionary as String] as? [String: Any] { + tiff[kCGImagePropertyTIFFOrientation as String] = 1 + imageProperties[kCGImagePropertyTIFFDictionary as String] = tiff } - if MalachiteClassesObject().versionType == "INTERNAL" { - for prop in imageProperties { - MalachiteClassesObject().internalNSLog("[Capture Photo] \(prop)") + imageProperties[kCGImagePropertyOrientation as String] = 1 + + if location.locationEnabled && utilities.versionType == "INTERNAL" && utilities.preferences.evaintrnl.locationEnabled, let loc = location.location.location { + let gps = NSMutableDictionary() + let formatter = DateFormatter() + + // This is actually slightly more verbose than the stock camera app LOL + gps[kCGImagePropertyGPSAltitude] = (loc.altitude >= 0.0) ? loc.altitude : -loc.altitude + gps[kCGImagePropertyGPSAltitudeRef] = (loc.altitude >= 0.0) ? 0 : 1 + formatter.dateFormat = "yyyy:MM:dd" + gps[kCGImagePropertyGPSDateStamp] = formatter.string(from:loc.timestamp) + gps[kCGImagePropertyGPSDOP] = loc.horizontalAccuracy + gps[kCGImagePropertyGPSHPositioningError] = loc.horizontalAccuracy + gps[kCGImagePropertyGPSLatitudeRef] = (loc.coordinate.latitude >= 0.0) ? "N" : "S" + gps[kCGImagePropertyGPSLatitude] = (loc.coordinate.latitude >= 0.0) ? loc.coordinate.latitude : -loc.coordinate.latitude + gps[kCGImagePropertyGPSLongitudeRef] = (loc.coordinate.longitude >= 0.0) ? "E" : "W" + gps[kCGImagePropertyGPSLongitude] = (loc.coordinate.longitude >= 0.0) ? loc.coordinate.longitude : -loc.coordinate.longitude + gps[kCGImagePropertyGPSSpeedRef] = "K" + gps[kCGImagePropertyGPSSpeed] = loc.speed + formatter.dateFormat = "HH:mm:ss" + gps[kCGImagePropertyGPSTimeStamp] = formatter.string(from:loc.timestamp) + + if let heading = location.location.heading { + gps[kCGImagePropertyGPSDestBearingRef] = "T" + gps[kCGImagePropertyGPSDestBearing] = heading.trueHeading + gps[kCGImagePropertyGPSImgDirectionRef] = "T" + gps[kCGImagePropertyGPSImgDirection] = heading.trueHeading } + + imageProperties[kCGImagePropertyGPSDictionary as String] = gps } - + + let canvasSize = upright.extent.size + let watermarkUIImage = self.watermark(canvasSize: canvasSize) + let watermarkImage = CIImage(image: watermarkUIImage) + + let outputImage = (watermarkImage ?? CIImage()).composited(over: upright) + + let gainMap = returnGainMap(properties: &imageProperties, imageData: imageData) let outputImageWithProps = outputImage.settingProperties(imageProperties) - - if enableHEIF { - data = returnHEIC(imageForRepresentation: outputImageWithProps, imageForGainMap: gainMapImage, imageColorspace: rawImage.colorSpace?.name) - } else { - data = returnJPEG(imageForRepresentation: outputImageWithProps, imageForGainMap: gainMapImage, imageColorspace: rawImage.colorSpace?.name) + + if utilities.preferences.debug.logging.imageProps { + for prop in imageProperties { + MalachiteClassesObject().internalNSLog("[Capture Photo] \(prop)") + } } - return data + return returnImageFile( + imageForRepresentation: outputImageWithProps, + imageForGainMap: gainMap, + imageColorspace: rawImage.colorSpace?.name, + imageProperties: imageProperties + ) } /** @@ -285,66 +296,96 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { FIX: A lot of things here, primarly watermark and image rotation being misaligned. */ - func watermark() -> UIImage - { - let imageView = UIImageView() - imageView.backgroundColor = UIColor.clear - - if photoImage.size.width < photoImage.size.height { - imageView.frame = CGRect(x:0, y:0, width:photoImage.size.height, height:photoImage.size.width) - } else { - imageView.frame = CGRect(x:0, y:0, width:photoImage.size.width, height:photoImage.size.height) + func watermark(canvasSize: CGSize, inset: CGPoint = CGPoint(x: 20, y: 20)) -> UIImage { + let imageView = UIImageView(frame: CGRect(origin: .zero, size: canvasSize)) + imageView.backgroundColor = .clear + + guard utilities.preferences.watermark.enabled else { + UIGraphicsBeginImageContextWithOptions(canvasSize, false, 1) + let img = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return img } + + let text = utilities.preferences.watermark.text + let font = UIFont.monospacedSystemFont(ofSize: 70, weight: .regular) + + let label = UILabel() + label.textAlignment = .right + label.textColor = .white + label.text = text + label.font = font + label.backgroundColor = .clear + label.numberOfLines = 1 + + label.sizeToFit() - if utilities.preferences.watermark.enabled { - utilities.debugNSLog("[Watermarking] User has opted to show a watermark") - var label = UILabel() - label = UILabel(frame: CGRect(x:50, y:20, width:photoImage.size.width - 100, height:120)) - label.textAlignment = .left - label.textColor = .white - label.text = utilities.preferences.watermark.text - label.font = UIFont(name: "Menlo", size: 70) - - imageView.addSubview(label) - } + let originalSize = label.bounds.size - UIGraphicsBeginImageContext(imageView.bounds.size) + label.frame = CGRect(origin: .zero, size: originalSize) + label.transform = CGAffineTransform(rotationAngle: .pi / 2) + let finalX = canvasSize.width - inset.x - originalSize.height + let finalY = inset.y + label.frame.origin = CGPoint(x: finalX, y: finalY) + + imageView.addSubview(label) + + UIGraphicsBeginImageContextWithOptions(canvasSize, false, 1) imageView.layer.render(in: UIGraphicsGetCurrentContext()!) - let imageWithText = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext(); - return imageWithText! + let imageWithText = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + + return imageWithText } /// Function to return a HEIC representation of the passed image with its colorspace and an optional gain map image. - func returnHEIC(imageForRepresentation image: CIImage, imageForGainMap hdrImage: CIImage?, imageColorspace colorSpace: CFString?) -> Data { + func returnImageFile(imageForRepresentation image: CIImage, imageForGainMap hdrImage: CIImage?, imageColorspace colorSpace: CFString?, imageProperties: [String: Any]) -> Data { + let context = CIContext() + + var finalSpace = CGColorSpace(name: colorSpace ?? CGColorSpace.sRGB)! + let metadataKey = CIImageRepresentationOption(rawValue: kCGImageDestinationMetadata as String) + + var options: [CIImageRepresentationOption: Any] = [ metadataKey: imageProperties ] + if enableHDR, let hdr = hdrImage { options[.hdrGainMapImage] = hdr } + let types = CGImageDestinationCopyTypeIdentifiers() as NSArray - if types.contains("public.heic") { - if enableHDR && (hdrImage != nil){ - return CIContext().heifRepresentation(of: image, format: .RGBA8, colorSpace: CGColorSpace(name: colorSpace!)!, options: [ .hdrGainMapImage : hdrImage! ])! - } else { - return CIContext().heifRepresentation(of: image, format: .RGBA8, colorSpace: CGColorSpace(name: colorSpace!)!)! - } - } else { - utilities.debugNSLog("[Capture Photo] Device does not support encoding HEIF, falling back to JPEG") - utilities.preferences.capture.format.heic = false - return returnJPEG(imageForRepresentation: image, imageForGainMap: hdrImage, imageColorspace: colorSpace) - } - } - - /// Function to return a JPEG representation of the passed image with its colorspace and an optional gain map image. - func returnJPEG(imageForRepresentation image: CIImage, imageForGainMap hdrImage: CIImage?, imageColorspace colorSpace: CFString?) -> Data { - utilities.debugNSLog("[Capture Photo] HEIF is disabled, saving JPEG representation") - if enableHDR && (hdrImage != nil) { - return CIContext().jpegRepresentation(of: image, colorSpace: CGColorSpace(name: colorSpace!)!, options: [ .hdrGainMapImage : hdrImage! ])! + let useHEIC = enableHEIC && types.contains("public.heic") + + if useHEIC { + utilities.debugNSLog("[Capture Photo] Saving HEIC representation") + return context.heifRepresentation( + of: image, + format: .RGBAf, + colorSpace: finalSpace, + options: options + ) ?? Data() } else { - return CIContext().jpegRepresentation(of: image, colorSpace: CGColorSpace(name: colorSpace!)!)! + utilities.debugNSLog("[Capture Photo] Saving JPEG representation") + return context.jpegRepresentation( + of: image, + colorSpace: finalSpace, + options: options + ) ?? Data() } } + /// Function to extract gain map data from the image. - func returnGainMap(properties props: inout [String: Any], imageData: Data) -> CIImage { + func returnGainMap(properties props: inout [String: Any], imageData: Data) -> CIImage? { + if !enableHDR { return nil } var gainMapImage = CIImage() - if let gainMapDataInfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(CGImageSourceCreateWithData(NSData(data: imageData), nil)!, 0, kCGImageAuxiliaryDataTypeHDRGainMap) as? Dictionary { + + guard let source = CGImageSourceCreateWithData(NSData(data: imageData), nil) else { return nil } + + let propsDict = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any] + let orientationCF = propsDict?[kCGImagePropertyOrientation] as? NSNumber + let sourceOrientation = orientationCF.flatMap { CGImagePropertyOrientation(rawValue: $0.uint32Value) } + let key: CFString + + if #available(iOS 18.0, *) { key = kCGImageAuxiliaryDataTypeISOGainMap } + else { key = kCGImageAuxiliaryDataTypeHDRGainMap } + + if let gainMapDataInfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(source, 0, key) as? Dictionary { utilities.debugNSLog("[Capture Photo] Saving gain map properties from image") let gainMapData = gainMapDataInfo[kCGImageAuxiliaryDataInfoData] as! Data let gainMapDescription = gainMapDataInfo[kCGImageAuxiliaryDataInfoDataDescription]! as! [String: Int] @@ -353,28 +394,38 @@ class MalachitePhotoPreview : UIViewController, UIScrollViewDelegate { bytesPerRow: gainMapDescription["BytesPerRow"]!, size: gainMapSize, format: .L8, colorSpace: nil) - let gainMapcgImage = CIContext().createCGImage(gainMapciImage, - from: CGRect(origin: CGPoint(x: 0, y: 0), size: gainMapSize))! + + let orientedGainMap: CIImage + if let srcOri = sourceOrientation { + orientedGainMap = gainMapciImage.oriented(srcOri) + } else { + orientedGainMap = gainMapciImage + } + + let context = CIContext() + let gainMapcgImage = context.createCGImage(orientedGainMap, + from: CGRect(origin: CGPoint(x: 0, y: 0), size: orientedGainMap.extent.size))! let gainMapOutputData = NSMutableData() let gainMapDest = CGImageDestinationCreateWithData(gainMapOutputData, UTType.bmp.identifier as CFString, 1, nil) CGImageDestinationAddImage(gainMapDest!, gainMapcgImage, [:] as CFDictionary) CGImageDestinationFinalize(gainMapDest!) - + gainMapImage = CIImage(data: gainMapOutputData as Data)! - + var applDict = extractEXIFData(properties: props, dictionary: kCGImagePropertyMakerAppleDictionary) var exifDict = extractEXIFData(properties: props, dictionary: kCGImagePropertyExifDictionary) - - applDict["33"] = 0.0 - applDict["48"] = 0.0 + + applDict["33"] = 0.0 + applDict["48"] = 0.0 + applDict["HDRImageType"] = 3 exifDict["CustomRendered"] = 2 - + props[kCGImagePropertyMakerAppleDictionary as String] = applDict props[kCGImagePropertyExifDictionary as String] = exifDict } else { utilities.debugNSLog("[Capture Photo] Couldn't save the gain map properties. Opting to ignore.") } - + return gainMapImage } @@ -423,3 +474,4 @@ final class dataToShareable: NSObject, UIActivityItemSource { return metadata } } + diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView+About.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView+About.swift new file mode 100644 index 0000000..06d63a5 --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView+About.swift @@ -0,0 +1,27 @@ +// +// QuickHelpView+About.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension QuickHelpView { + struct About: View { + var utilities = MalachiteClassesObject() + + init(utilities: MalachiteClassesObject ) { self.utilities = utilities } + + /// A variable to hold the about section. + var body: some View { + Section { + createQuickHelpRow(title: Text("view.title.about"), subtitle: Text("view.detail.about")) + if utilities.versionType == "INTERNAL" { + createQuickHelpRow(title: Text("view.title.compatibility"), subtitle: Text("view.detail.compatibility")) + createQuickHelpRow(title: Text("view.title.developer"), subtitle: Text("view.detail.developer")) + } + } + } + } +} diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView+Photo.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Photo.swift new file mode 100644 index 0000000..abe311f --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Photo.swift @@ -0,0 +1,21 @@ +// +// QuickHelpView+Photo.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension QuickHelpView { + struct Photo: View { + /// A variable to hold the photo settings section. + var body: some View { + Section(header: Text("settings.header.photo"), footer: Text("settings.footer.photo")) { + createQuickHelpRow(title: Text("settings.option.photo.fileformat"), subtitle: Text("settings.detail.photo.fileformat")) + createQuickHelpRow(title: Text("settings.option.photo.hdr"), subtitle: Text("settings.detail.photo.hdr")) + createQuickHelpRow(title: Text("settings.option.photo.continuous"), subtitle: Text("settings.detail.photo.continuous")) + } + } + } +} diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView+Preview.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Preview.swift new file mode 100644 index 0000000..0314c7d --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Preview.swift @@ -0,0 +1,21 @@ +// +// QuickHelpView+Preview.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension QuickHelpView { + struct Preview: View { + /// A variable to hold the preview settings section. + var body: some View { + Section(header: Text("settings.header.preview"), footer: Text("settings.footer.preview")) { + createQuickHelpRow(title: Text("settings.option.preview.aspect_ratio"), subtitle: Text("settings.detail.preview.aspect_ratio")) + createQuickHelpRow(title: Text("settings.option.preview.sbtlz"), subtitle: Text("settings.detail.preview.sbtlz")) + createQuickHelpRow(title: Text("settings.option.preview.zoom_maximum"), subtitle: Text("settings.detail.preview.zoom_maximum")) + } + } + } +} diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView+Resolution.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Resolution.swift new file mode 100644 index 0000000..7c157f2 --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Resolution.swift @@ -0,0 +1,31 @@ +// +// QuickHelpView+Resolution.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension QuickHelpView { + struct Resolution: View { + var utilities = MalachiteClassesObject() + + init(utilities: MalachiteClassesObject ) { self.utilities = utilities } + + /// A variable to hold the image resolution section. + var body: some View { + Section(header: Text("settings.header.resolution"), footer: Text("settings.footer.resolution")) { + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.ultrawide) { + createQuickHelpRow(title: Text("settings.option.resolution.ultrawide"), subtitle: Text("settings.detail.resolution.ultrawide")) + } + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.wideangle) { + createQuickHelpRow(title: Text("settings.option.resolution.wide"), subtitle: Text("settings.detail.resolution.wide")) + } + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.telephoto) { + createQuickHelpRow(title: Text("settings.option.resolution.telephoto"), subtitle: Text("settings.detail.resolution.telephoto")) + } + } + } + } +} diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView+UI.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView+UI.swift new file mode 100644 index 0000000..269a2be --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView+UI.swift @@ -0,0 +1,23 @@ +// +// QuickHelpView+UI.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension QuickHelpView { + struct UserInterface: View { + /// A variable to hold the user interface settings section. + var body: some View { + Section(header: Text("settings.header.ui"), footer: Text("settings.footer.ui")) { + createQuickHelpRow(title: Text("settings.option.ui.tapgesture"), subtitle: Text("settings.detail.ui.tapgesture")) + createQuickHelpRow(title: Text("settings.option.ui.hiddengestures"), subtitle: Text("settings.detail.ui.hiddengestures")) + createQuickHelpRow(title: Text("settings.option.ui.idletimer"), subtitle: Text("settings.detail.ui.idletimer")) + createQuickHelpRow(title: Text("settings.option.ui.haptics"), subtitle: Text("settings.detail.ui.haptics")) + createQuickHelpRow(title: Text("settings.option.ui.hiddenonlaunch"), subtitle: Text("settings.detail.ui.hiddenonlaunch")) + } + } + } +} diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView+Watermarking.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Watermarking.swift new file mode 100644 index 0000000..18585fb --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView+Watermarking.swift @@ -0,0 +1,20 @@ +// +// QuickHelpView+Watermarking.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension QuickHelpView { + struct Watermarking: View { + /// A variable to hold the watermark settings section. + var body: some View { + Section(header: Text("settings.header.watermark"), footer: Text("settings.footer.watermark")) { + createQuickHelpRow(title: Text("settings.option.watermark.enable"), subtitle: Text("settings.detail.watermark.enable")) + createQuickHelpRow(title: Text("settings.option.watermark.text"), subtitle: Text("settings.detail.watermark.text")) + } + } + } +} diff --git a/mlchtCamera/Views/QuickHelpView/QuickHelpView.swift b/mlchtCamera/Views/QuickHelpView/QuickHelpView.swift new file mode 100755 index 0000000..08461c0 --- /dev/null +++ b/mlchtCamera/Views/QuickHelpView/QuickHelpView.swift @@ -0,0 +1,113 @@ +// +// SettingsView+Help.swift +// Malachite +// +// Created by Eva Isabella Luna on 3/14/24. +// + +import SwiftUI + +struct QuickHelpView: View { + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + /// A variable used to hold the entire view. + var body: some View { + Form { + About(utilities: utilities) + Preview() + Resolution(utilities: utilities) + Photo() + Watermarking() + UserInterface() + if utilities.versionType == "DEBUG" { Debugging() } + } + .navigationTitle("view.title.help") + .toolbar(content: { + ToolbarItemGroup(placement: .topBarTrailing) { + MalachiteToolbarUtils(action: self.dismissAction, image: "checkmark", primary: true) + } + }) + } + + @available(*, deprecated, message: "This struct is set to be replaced by DeveloperView in the near future.") + struct Debugging: View { + /// A variable to hold the debug settings section. Only available with debug and internal builds. + var body: some View { + Section(header: Text("developer.header.debug"), footer: Text("developer.footer.debug")) { + createQuickHelpRow(title: Text("developer.option.debug.logging.unified"), subtitle: Text("developer.detail.debug.logging.unified")) + createQuickHelpRow(title: Text("developer.option.debug.logging.preferences"), subtitle: Text("developer.detail.debug.logging.preferences")) + createQuickHelpRow(title: Text("developer.option.debug.logging.imageprops"), subtitle: Text("developer.detail.debug.logging.imageprops")) + createQuickHelpRow(title: Text("developer.option.debug.breakapp"), subtitle: Text("developer.detail.debug.breakapp")) + createQuickHelpRow(title: Text("developer.option.debug.erase.preferences"), subtitle: Text("developer.detail.debug.erase.preferences")) + createQuickHelpRow(title: Text("developer.option.debug.erase.gamekit"), subtitle: Text("developer.detail.debug.erase.gamekit")) + } + } + } + + struct createQuickHelpRow: View { + var title: Text + var subtitle: Text + + init( + title: Text, + subtitle: Text + ) { + self.title = title + self.subtitle = subtitle + } + + var body: some View { + VStack { + HStack { + title + .bold() + Spacer() + + } + HStack { + subtitle + .font(.footnote) + Spacer() + } + } + } + } +} + +struct QuickHelpViewDeveloper: View { + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + var body: some View { + Form { + QuickHelpView.Debugging() + } + .navigationTitle("view.title.help") + .toolbar(content: { + ToolbarItemGroup(placement: .topBarTrailing) { + MalachiteToolbarUtils(action: self.dismissAction, image: "checkmark", primary: true) + } + }) + } +} diff --git a/mlchtCamera/Views/SettingsView/SettingsView+About.swift b/mlchtCamera/Views/SettingsView/SettingsView+About.swift new file mode 100644 index 0000000..ce7098b --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView+About.swift @@ -0,0 +1,61 @@ +// +// SettingsView+About.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension SettingsView { + struct About: View { + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities: MalachiteClassesObject + var location: Location + init( + utilities: MalachiteClassesObject, + location: Location, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.location = location + self.dismissAction = dismissAction + } + + var body: some View { + Section { + MalachiteCellViewUtils( + icon: "info.circle", + disabled: nil, + dangerous: false) + { + NavigationLink(destination: AboutView(dismissAction: dismissAction)) { + Text("view.title.about") + } + } + if utilities.versionType == "INTERNAL" { + MalachiteCellViewUtils( + icon: "checkmark.seal", + disabled: nil, + dangerous: false) + { + NavigationLink(destination: CompatibilityView(dismissAction: dismissAction, utilities: utilities)) { + Text("view.title.compatibility") + } + } + MalachiteCellViewUtils( + icon: "wrench.and.screwdriver", + disabled: nil, + dangerous: false) + { + NavigationLink(destination: DeveloperView(dismissAction: dismissAction, utilities: utilities, location: location)) { + Text("view.title.developer") + } + } + } + } + } + } +} diff --git a/mlchtCamera/Views/SettingsView/SettingsView+Photo.swift b/mlchtCamera/Views/SettingsView/SettingsView+Photo.swift new file mode 100644 index 0000000..0a35d88 --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView+Photo.swift @@ -0,0 +1,143 @@ +// +// SettingsView+Photo.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension SettingsView { + struct Photo: View { + /// A State variable used for presenting the user with a footer based on capabilities. + @State private var formatFooterText: String? + /// A State variable used for determining the active photo format. + @State private var photoFormat = Int() + /// A State variable used for determining whether or not to capture in HDR. + @State private var hdrSwitch = false + /// A State variable used for determining whether or not to enable continuous auto exposure. + @State private var continuousAEAF = Int() + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + var body: some View { + Section(header: Text("settings.header.photo"), footer: (formatFooterText != nil) ? Text(formatFooterText!) : nil) { + MalachiteCellViewUtils( + icon: "square.and.arrow.down", + disabled: !utilities.preferences.compatibility.heic, + dangerous: false) + { + Picker("settings.option.photo.fileformat", selection: $photoFormat) { + Text("settings.option.photo.fileformat.jpeg") + .tag(0) + Text("settings.option.photo.fileformat.heic") + .tag(1) + } + } + + MalachiteCellViewUtils( + icon: "camera.filters", + disabled: !utilities.preferences.compatibility.hdr, + dangerous: false) + { + Toggle("settings.option.photo.hdr", isOn: $hdrSwitch ) + } + MalachiteCellViewUtils( + icon: "plus.viewfinder", + disabled: nil, + dangerous: false) + { + Picker("settings.option.photo.continuous", selection: $continuousAEAF) { + Text("settings.option.reusable.aeaf") + .tag(0) + Text("settings.option.reusable.af") + .tag(1) + Text("settings.option.reusable.ae") + .tag(2) + Text("settings.option.reusable.off") + .tag(3) + } + } + } + .onAppear(perform: self.onAppear) + .onDisappear(perform: self.onDisappear) + .onChange(of: photoFormat) {_ in + utilities.preferences.capture.format.jpeg = photoFormat == 0 ? true : false + utilities.preferences.capture.format.heic = photoFormat == 1 ? true : false + } + .onChange(of: hdrSwitch) { _ in + if utilities.preferences.compatibility.hdr { utilities.preferences.capture.hdr = hdrSwitch } + } + .onChange(of: continuousAEAF) { _ in + switch continuousAEAF { + case 0: + utilities.preferences.capture.continuous = ["ae", "af"] + case 1: + utilities.preferences.capture.continuous = ["af"] + case 2: + utilities.preferences.capture.continuous = ["ae"] + default: + utilities.preferences.capture.continuous = ["off"] + } + + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.continousAEAFNotification.name, object: nil) + } + } + + func onAppear() { + if !utilities.preferences.compatibility.heic { formatFooterText = "settings.footer.photo.heic".localized } + + if !utilities.preferences.compatibility.hdr { + formatFooterText = (formatFooterText != nil) ? formatFooterText! + "settings.footer.photo.hdr".localized : "settings.footer.photo.hdr".localized + } else { + hdrSwitch = utilities.preferences.capture.hdr + } + + // TODO: Better way to do this + photoFormat = utilities.preferences.capture.format.jpeg ? 0 : 1 + photoFormat = utilities.preferences.capture.format.heic ? 1 : 0 + + switch utilities.preferences.capture.continuous { + case ["ae", "af"]: + continuousAEAF = 0 + case ["af"]: + continuousAEAF = 1 + case ["ae"]: + continuousAEAF = 2 + default: + continuousAEAF = 3 + } + } + + func onDisappear() { + formatFooterText = "" + + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.continousAEAFNotification.name, object: nil) + + if utilities.preferences.compatibility.hdr { utilities.preferences.capture.hdr = hdrSwitch } + utilities.preferences.capture.format.jpeg = photoFormat == 0 ? true : false + utilities.preferences.capture.format.heic = photoFormat == 1 ? true : false + + switch continuousAEAF { + case 0: + utilities.preferences.capture.continuous = ["ae", "af"] + case 1: + utilities.preferences.capture.continuous = ["af"] + case 2: + utilities.preferences.capture.continuous = ["ae"] + default: + utilities.preferences.capture.continuous = ["off"] + } + } + } +} diff --git a/mlchtCamera/Views/SettingsView/SettingsView+Preview.swift b/mlchtCamera/Views/SettingsView/SettingsView+Preview.swift new file mode 100644 index 0000000..fc06bc6 --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView+Preview.swift @@ -0,0 +1,123 @@ +// +// SettingsView+Preview.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension SettingsView { + struct Preview: View { + /// A State variable used for determining the current aspect ratio for the ``cameraPreview``. + @State private var previewAspect = Int() + /// A State variable used for determining whether or not to stabilize the ``cameraPreview``. + @State private var shouldStabilize = Bool() + /// A State variable used for determining whether or not to skip opening ``MalachitePhotoPreview`` and save the photo. + @State private var shouldUseFastPath = Bool() + /// A State variable used for determining what the maximum zoom level for each camera should be. + @State private var zoomMaximum = Int() + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + /// A variable to hold the preview settings section. + var body: some View { + Section(header: Text("settings.header.preview")) { + MalachiteCellViewUtils( + icon: "aspectratio", + disabled: false, + dangerous: false) + { + Picker("settings.option.preview.aspect_ratio", selection: $previewAspect) { + Text("settings.option.preview.aspect_ratio.fit") + .tag(0) + Text("settings.option.preview.aspect_ratio.fill") + .tag(1) + } + } + MalachiteCellViewUtils( + icon: "level", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.preview.sbtlz", isOn: $shouldStabilize) + } + // Testing things out + if utilities.versionType == "INTERNAL" { + MalachiteCellViewUtils( + icon: "forward", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.preview.fastpath", isOn: $shouldUseFastPath) + } + } + MalachiteCellViewUtils( + icon: "plus.magnifyingglass", + disabled: nil, + dangerous: false) + { + Stepper { + Text("settings.option.preview.zoom_maximum") + } onIncrement: { + if zoomMaximum < 10 { zoomMaximum += 1 } + } onDecrement: { + if zoomMaximum > 5 { zoomMaximum -= 1 } + } + Text(String(format: "%lldx", Int(zoomMaximum))) + .font(.footnote) + .foregroundColor(.gray) + } + } + .onAppear(perform: self.onAppear) + .onDisappear(perform: self.onDisappear) + // Called when the preview's aspect ratio is changed. + .onChange(of: previewAspect) {_ in + utilities.preferences.preview.aspect = (previewAspect == 1) + + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aspectFillNotification.name, object: nil) + } + // Called when the user enables/disables preview stabilization. + .onChange(of: shouldStabilize) {_ in + utilities.preferences.preview.stablize = shouldStabilize + + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.stabilizerNotification.name, object: nil) + } + // Called when the user enables/disables the fast path. + .onChange(of: shouldUseFastPath) {_ in + utilities.preferences.preview.fastPath = shouldUseFastPath + } + // Called when the maximum zoom level changes. + .onChange(of: zoomMaximum) {_ in + utilities.preferences.capture.maximumZoom = zoomMaximum + } + } + + func onAppear() { + shouldStabilize = utilities.preferences.preview.stablize + shouldUseFastPath = utilities.preferences.preview.fastPath + zoomMaximum = utilities.preferences.capture.maximumZoom + previewAspect = utilities.preferences.preview.aspect ? 1 : 0 + } + + func onDisappear() { + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aspectFillNotification.name, object: nil) + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.stabilizerNotification.name, object: nil) + + utilities.preferences.preview.aspect = (previewAspect == 1) + utilities.preferences.preview.stablize = shouldStabilize + utilities.preferences.preview.fastPath = shouldUseFastPath + utilities.preferences.capture.maximumZoom = zoomMaximum + } + } +} diff --git a/mlchtCamera/Views/SettingsView/SettingsView+Resolution.swift b/mlchtCamera/Views/SettingsView/SettingsView+Resolution.swift new file mode 100644 index 0000000..11ec376 --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView+Resolution.swift @@ -0,0 +1,185 @@ +// +// SettingsView+Resolution.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension SettingsView { + struct Resolution: View { + /// A State variable used for determining what megapixel count the ultrawide camera should shoot in. + @State private var ultrawideMegapixelCount = Int() + /// A State variable used for determining what megapixel count the wide angle camera should shoot in. + @State private var wideMegapixelCount = Int() + /// A State variable used for determining what megapixel count the telephoto camera should shoot in. + @State private var telephotoMegapixelCount = Int() + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + var body: some View { + Section(header: Text("settings.header.resolution")) { + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.ultrawide) { + MalachiteCellViewUtils( + icon: "camera.aperture", + disabled: utilities.preferences.ext.dictionary.getCount(dictionary: utilities.preferences.compatibility.ultrawide) == 1, + dangerous: false) + { + Picker("settings.option.resolution.ultrawide", selection: $ultrawideMegapixelCount) { + if let mp = utilities.preferences.compatibility.ultrawide["8"] { if mp { + Text("settings.option.resolution.8") + .tag(0) + } } + if let mp = utilities.preferences.compatibility.ultrawide["12"] { if mp { + Text("settings.option.resolution.12") + .tag(1) + } } + if let mp = utilities.preferences.compatibility.ultrawide["48"] { if mp { + Text("settings.option.resolution.48") + .tag(2) + } } + } + } + } + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.wideangle) { + MalachiteCellViewUtils( + icon: "camera.aperture", + disabled: utilities.preferences.ext.dictionary.getCount(dictionary: utilities.preferences.compatibility.wideangle) == 1, + dangerous: false) + { + Picker("settings.option.resolution.wide", selection: $wideMegapixelCount) { + if let mp = utilities.preferences.compatibility.wideangle["8"] { if mp { + Text("settings.option.resolution.8") + .tag(0) + } } + if let mp = utilities.preferences.compatibility.wideangle["12"] { if mp { + Text("settings.option.resolution.12") + .tag(1) + } } + if let mp = utilities.preferences.compatibility.wideangle["48"] { if mp { + Text("settings.option.resolution.48") + .tag(2) + } } + } + } + } + if utilities.preferences.ext.dictionary.isValid(dictionary: utilities.preferences.compatibility.telephoto) { + MalachiteCellViewUtils( + icon: "camera.aperture", + disabled: utilities.preferences.ext.dictionary.getCount(dictionary: utilities.preferences.compatibility.telephoto) == 1, + dangerous: false) + { + Picker("settings.option.resolution.telephoto", selection: $telephotoMegapixelCount) { + if let mp = utilities.preferences.compatibility.telephoto["12"] { if mp { + Text("settings.option.resolution.12") + .tag(0) + } } + if let mp = utilities.preferences.compatibility.telephoto["48"] { if mp { + Text("settings.option.resolution.48") + .tag(1) + } } + } + } + } + } + .onAppear(perform: self.onAppear) + .onDisappear(perform: self.onDisappear) + .onChange(of: ultrawideMegapixelCount) { _ in + switch ultrawideMegapixelCount { + case 1: + utilities.preferences.capture.mp.ultrawide = 12 + case 2: + utilities.preferences.capture.mp.ultrawide = 48 + default: + utilities.preferences.capture.mp.ultrawide = 8 + } + } + .onChange(of: wideMegapixelCount) { _ in + switch wideMegapixelCount { + case 1: + utilities.preferences.capture.mp.wideangle = 12 + case 2: + utilities.preferences.capture.mp.wideangle = 48 + default: + utilities.preferences.capture.mp.wideangle = 8 + } + } + .onChange(of: telephotoMegapixelCount) { _ in + switch telephotoMegapixelCount { + case 1: + utilities.preferences.capture.mp.telephoto = 48 + default: + utilities.preferences.capture.mp.telephoto = 12 + } + } + } + + func onAppear() { + switch utilities.preferences.capture.mp.ultrawide { + case 12: + ultrawideMegapixelCount = 1 + case 48: + ultrawideMegapixelCount = 2 + default: + ultrawideMegapixelCount = 0 + } + + switch utilities.preferences.capture.mp.wideangle { + case 12: + wideMegapixelCount = 1 + case 48: + wideMegapixelCount = 2 + default: + wideMegapixelCount = 0 + } + + switch utilities.preferences.capture.mp.telephoto { + case 48: + telephotoMegapixelCount = 1 + default: + telephotoMegapixelCount = 0 + } + } + + func onDisappear() { + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.megaPixelSwitchNotification.name, object: nil) + + switch ultrawideMegapixelCount { + case 1: + utilities.preferences.capture.mp.ultrawide = 12 + case 2: + utilities.preferences.capture.mp.ultrawide = 48 + default: + utilities.preferences.capture.mp.ultrawide = 8 + } + + switch wideMegapixelCount { + case 1: + utilities.preferences.capture.mp.wideangle = 12 + case 2: + utilities.preferences.capture.mp.wideangle = 48 + default: + utilities.preferences.capture.mp.wideangle = 8 + } + + switch telephotoMegapixelCount { + case 1: + utilities.preferences.capture.mp.telephoto = 48 + default: + utilities.preferences.capture.mp.telephoto = 12 + } + } + } +} + diff --git a/mlchtCamera/Views/SettingsView/SettingsView+UI.swift b/mlchtCamera/Views/SettingsView/SettingsView+UI.swift new file mode 100644 index 0000000..b59bc0d --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView+UI.swift @@ -0,0 +1,190 @@ +// +// SettingsView+UI.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension SettingsView { + struct UserInterface: View { + /// A State variable used for determining whether or not to enable exposure and focus POI on tap and hold. + @State private var poiTapAndHold = Int() + /// A State variable used for determining whether or not to enable the system's auto locking APIs. + @State private var idleTimerDisabled = Bool() + /// A State variable used for determining whether or not to enabel haptics. + @State private var hapticsDisabled = Bool() + /// A State variable used for determining whether or not to start the app with the UI hidden. + @State private var appStartsUIHidden = Bool() + /// A State vairable used for determining whether or not to enable pinch to zoom and the tap gesture while the UI is hidden. + @State private var uiHiderGestures = Int() + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + var body: some View { + Section(header: Text("settings.header.ui")) { + MalachiteCellViewUtils( + icon: "hand.tap", + disabled: nil, + dangerous: false) + { + Picker("settings.option.ui.tapgesture", selection: $poiTapAndHold) { + Text("settings.option.reusable.aeaf") + .tag(0) + Text("settings.option.reusable.af") + .tag(1) + Text("settings.option.reusable.ae") + .tag(2) + Text("settings.option.reusable.off") + .tag(3) + } + } + MalachiteCellViewUtils( + icon: "hand.raised.slash", + disabled: nil, + dangerous: false) + { + Picker("settings.option.ui.hiddengestures", selection: $uiHiderGestures) { + Text("settings.option.reusable.pinchzoomtapandhold") + .tag(0) + Text("settings.option.reusable.pinchzoom") + .tag(1) + Text("settings.option.reusable.tapandhold") + .tag(2) + Text("settings.option.reusable.off") + .tag(3) + } + } + MalachiteCellViewUtils( + icon: "clock", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.ui.idletimer", isOn: $idleTimerDisabled) + } + MalachiteCellViewUtils( + icon: "iphone.radiowaves.left.and.right", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.ui.haptics", isOn: $hapticsDisabled) + } + MalachiteCellViewUtils( + icon: "eye.slash", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.ui.hiddenonlaunch", isOn: $appStartsUIHidden) + } + } + .onAppear(perform: self.onAppear) + .onDisappear(perform: self.onDisappear) + .onChange(of: poiTapAndHold) {_ in + switch poiTapAndHold { + case 0: + utilities.preferences.userInterface.tapAndHold = ["ae", "af"] + case 1: + utilities.preferences.userInterface.tapAndHold = ["af"] + case 2: + utilities.preferences.userInterface.tapAndHold = ["ae"] + default: + utilities.preferences.userInterface.tapAndHold = ["off"] + } + } + .onChange(of: uiHiderGestures) {_ in + switch uiHiderGestures { + case 0: + utilities.preferences.userInterface.hiddenControls = ["zoom", "tah"] + case 1: + utilities.preferences.userInterface.hiddenControls = ["zoom"] + case 2: + utilities.preferences.userInterface.hiddenControls = ["tah"] + default: + utilities.preferences.userInterface.hiddenControls = ["off"] + } + + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aeafTapGestureNotification.name, object: nil) + } + .onChange(of: idleTimerDisabled) { _ in + utilities.preferences.userInterface.idleTimerDisabled = idleTimerDisabled + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.idleTimerNotification.name, object: nil) + } + .onChange(of: hapticsDisabled) { _ in + utilities.preferences.userInterface.hapticFeedback = hapticsDisabled + } + .onChange(of: appStartsUIHidden) { _ in + utilities.preferences.userInterface.appLaunch = appStartsUIHidden + } + } + + func onAppear() { + idleTimerDisabled = utilities.preferences.userInterface.idleTimerDisabled + hapticsDisabled = utilities.preferences.userInterface.hapticFeedback + appStartsUIHidden = utilities.preferences.userInterface.appLaunch + + switch utilities.preferences.userInterface.tapAndHold { + case ["ae", "af"]: + poiTapAndHold = 0 + case ["af"]: + poiTapAndHold = 1 + case ["ae"]: + poiTapAndHold = 2 + default: + poiTapAndHold = 3 + } + + switch utilities.preferences.userInterface.hiddenControls { + case ["zoom", "tah"]: + uiHiderGestures = 0 + case ["zoom"]: + uiHiderGestures = 1 + case ["tah"]: + uiHiderGestures = 2 + default: + uiHiderGestures = 3 + } + } + + func onDisappear() { + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.aeafTapGestureNotification.name, object: nil) + NotificationCenter.default.post(name: MalachiteFunctionUtils.Notifications.idleTimerNotification.name, object: nil) + + utilities.preferences.userInterface.idleTimerDisabled = idleTimerDisabled + utilities.preferences.userInterface.hapticFeedback = hapticsDisabled + utilities.preferences.userInterface.appLaunch = appStartsUIHidden + + switch poiTapAndHold { + case 0: + utilities.preferences.userInterface.tapAndHold = ["ae", "af"] + case 1: + utilities.preferences.userInterface.tapAndHold = ["af"] + case 2: + utilities.preferences.userInterface.tapAndHold = ["ae"] + default: + utilities.preferences.userInterface.tapAndHold = ["off"] + } + + switch uiHiderGestures { + case 0: + utilities.preferences.userInterface.hiddenControls = ["zoom", "tah"] + case 1: + utilities.preferences.userInterface.hiddenControls = ["zoom"] + case 2: + utilities.preferences.userInterface.hiddenControls = ["tah"] + default: + utilities.preferences.userInterface.hiddenControls = ["off"] + } + } + } +} diff --git a/mlchtCamera/Views/SettingsView/SettingsView+Watermarking.swift b/mlchtCamera/Views/SettingsView/SettingsView+Watermarking.swift new file mode 100644 index 0000000..bd0fbb6 --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView+Watermarking.swift @@ -0,0 +1,71 @@ +// +// SettingsView+Watermarking.swift +// Malachite +// +// Created by Eva Isabella Luna on 8/16/25. +// + +import SwiftUI + +extension SettingsView { + struct Watermarking: View { + /// A State variable used for determining whether or not watermarking is enabled. + @State private var watermarkSwitch = false + /// A State variable used for determining the current watermark string. + @State private var watermarkText = String() + /// A State variable used for determining whether or not this view is being presented as a modal. + var dismissAction: (() -> Void) + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities = MalachiteClassesObject() + + init( + utilities: MalachiteClassesObject, + dismissAction: @escaping (() -> Void) + ) { + self.utilities = utilities + self.dismissAction = dismissAction + } + + var body: some View { + Section(header: Text("settings.header.watermark")) { + MalachiteCellViewUtils( + icon: "textformat", + disabled: nil, + dangerous: false) + { + Toggle("settings.option.watermark.enable", isOn: $watermarkSwitch) + } + + MalachiteCellViewUtils( + icon: "signature", + disabled: nil, + dangerous: false) + { + Text("settings.option.watermark.text") + TextField("settings.option.watermark.text.placeholder", text: $watermarkText) + .multilineTextAlignment(.trailing) + .autocorrectionDisabled() + .keyboardType(.twitter) + } + } + .onAppear(perform: self.onAppear) + .onDisappear(perform: self.onDisappear) + .onChange(of: watermarkSwitch) {_ in + utilities.preferences.watermark.enabled = watermarkSwitch + } + .onChange(of: watermarkText) {_ in + utilities.preferences.watermark.text = watermarkText.isEmpty ? "Shot with Malachite" : String(watermarkText.prefix(65)) + } + } + + func onAppear() { + watermarkText = utilities.preferences.watermark.text + watermarkSwitch = utilities.preferences.watermark.enabled + } + + func onDisappear() { + utilities.preferences.watermark.enabled = watermarkSwitch + utilities.preferences.watermark.text = watermarkText.isEmpty ? "Shot with Malachite" : String(watermarkText.prefix(65)) + } + } +} diff --git a/mlchtCamera/Views/SettingsView/SettingsView.swift b/mlchtCamera/Views/SettingsView/SettingsView.swift new file mode 100755 index 0000000..2b3497f --- /dev/null +++ b/mlchtCamera/Views/SettingsView/SettingsView.swift @@ -0,0 +1,79 @@ +// +// SettingsView.swift +// Malachite +// +// Created by Eva Isabella Luna on 11/26/23. +// + +import SwiftUI + +struct SettingsView: View { + /// A State variable used for determining whether or not debug logging UserDefaults is enabled. + @State private var debugLoggingUserDefaults = false + /// A State variable used for determining whether or not to literally break the app. + @State private var breakApp = false + /// A variable to hold the existing instance of ``MalachiteClassesObject``. + var utilities: MalachiteClassesObject! + /// A variable to hold the existing instance of ``Location``. + var location: Location! + /// A variable used to hold the function for dismissing with the toolbar item. + var dismissAction: (() -> Void) + + /** + A variable used to hold the entire view. + + SwiftUI is weird... + Currently holds: + - Other variables to avoid type counting time issues. + - Handles initialization of variables required to show current settings. + - Navigation title of "Settings" + - Toolbar item for dismissing the view. + */ + var body: some View { + if #available(iOS 16.0, *) { + NavigationStack { guts } + } else { + NavigationView { guts }.navigationViewStyle(.stack) + } + } + + var guts: some View { + Form { + About(utilities: utilities, location: location, dismissAction: dismissAction) + Preview(utilities: utilities, dismissAction: dismissAction) + Resolution(utilities: utilities, dismissAction: dismissAction) + Photo(utilities: utilities, dismissAction: dismissAction) + Watermarking(utilities: utilities, dismissAction: dismissAction) + UserInterface(utilities: utilities, dismissAction: dismissAction) + if utilities.versionType == "DEBUG" { DeveloperView.Settings(utilities: utilities) } + } + .onAppear { onAppear() } + .onDisappear { onDisappear() } + .navigationTitle("view.title.settings") + .toolbar(content: { + ToolbarItemGroup(placement: .topBarLeading) { + NavigationLink(destination: QuickHelpView(utilities: utilities, dismissAction: dismissAction)) { + if #available(iOS 26.0, *) { + Image(systemName: "questionmark.circle") + } else { + Image(systemName: "questionmark.circle").tint(.primary) + } + } + } + ToolbarItemGroup(placement: .topBarTrailing) { + MalachiteToolbarUtils(action: self.dismissAction, image: "checkmark", primary: true) + } + }) + } + + func onAppear() { + debugLoggingUserDefaults = utilities.preferences.debug.logging.preferences + breakApp = utilities.preferences.debug.breakApp + } + + func onDisappear() { + utilities.preferences.debug.logging.preferences = debugLoggingUserDefaults + utilities.preferences.debug.breakApp = breakApp + } +} + diff --git a/Malachite/WidgetBundle/ControlCenterWidget.swift b/mlchtCamera/WidgetBundle/ControlCenterWidget.swift similarity index 100% rename from Malachite/WidgetBundle/ControlCenterWidget.swift rename to mlchtCamera/WidgetBundle/ControlCenterWidget.swift diff --git a/Malachite/WidgetBundle/Info.plist b/mlchtCamera/WidgetBundle/Info.plist similarity index 100% rename from Malachite/WidgetBundle/Info.plist rename to mlchtCamera/WidgetBundle/Info.plist diff --git a/Malachite/WidgetBundle/LockScreenWidget.swift b/mlchtCamera/WidgetBundle/LockScreenWidget.swift similarity index 100% rename from Malachite/WidgetBundle/LockScreenWidget.swift rename to mlchtCamera/WidgetBundle/LockScreenWidget.swift diff --git a/Malachite/WidgetBundle/MalachiteWidgetBundle.swift b/mlchtCamera/WidgetBundle/MalachiteWidgetBundle.swift similarity index 100% rename from Malachite/WidgetBundle/MalachiteWidgetBundle.swift rename to mlchtCamera/WidgetBundle/MalachiteWidgetBundle.swift diff --git a/mlchtCamera/WidgetBundle/mlchtWidgetBundle.entitlements b/mlchtCamera/WidgetBundle/mlchtWidgetBundle.entitlements new file mode 100644 index 0000000..b85694e --- /dev/null +++ b/mlchtCamera/WidgetBundle/mlchtWidgetBundle.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.hardened-process + + com.apple.security.hardened-process.checked-allocations + + com.apple.security.hardened-process.dyld-ro + + com.apple.security.hardened-process.enhanced-security-version-string + 1 + com.apple.security.hardened-process.hardened-heap + + com.apple.security.hardened-process.platform-restrictions-string + 2 + + diff --git a/MalachiteWatch/Info.plist b/mlchtCamera/WidgetBundle/mlchtWidgetBundleWatch.entitlements similarity index 100% rename from MalachiteWatch/Info.plist rename to mlchtCamera/WidgetBundle/mlchtWidgetBundleWatch.entitlements diff --git a/mlchtCamera/mlchtCamera.docc/Malachite.md b/mlchtCamera/mlchtCamera.docc/Malachite.md new file mode 100755 index 0000000..4daf145 --- /dev/null +++ b/mlchtCamera/mlchtCamera.docc/Malachite.md @@ -0,0 +1,9 @@ +# ``mlchtCamera`` + +mlchtCamera is an app designed to bring more control to users for macro photography, while offering playful elements to enjoy. + +## Overview + +Welcome to the documentation browser for mlchtCamera! While this application is not designed to by used as a library inside of other projects, making its code easy to reference is paramount for code readibility, learning how to create your own Swift applications, or contributing your own fixes and features to mlchtCamera itself. + + diff --git a/mlchtCamera/mlchtCamera.entitlements b/mlchtCamera/mlchtCamera.entitlements new file mode 100755 index 0000000..b936cff --- /dev/null +++ b/mlchtCamera/mlchtCamera.entitlements @@ -0,0 +1,20 @@ + + + + + com.apple.developer.game-center + + com.apple.security.hardened-process + + com.apple.security.hardened-process.checked-allocations + + com.apple.security.hardened-process.dyld-ro + + com.apple.security.hardened-process.enhanced-security-version-string + 1 + com.apple.security.hardened-process.hardened-heap + + com.apple.security.hardened-process.platform-restrictions-string + 2 + + diff --git a/Malachite/Malachite.entitlements b/mlchtRemote/Info.plist old mode 100755 new mode 100644 similarity index 71% rename from Malachite/Malachite.entitlements rename to mlchtRemote/Info.plist index c74150d..0c67376 --- a/Malachite/Malachite.entitlements +++ b/mlchtRemote/Info.plist @@ -1,8 +1,5 @@ - - com.apple.developer.game-center - - + diff --git a/mlchtRemote/Utilities/ConnectionUtils/Connection+Notifications.swift b/mlchtRemote/Utilities/ConnectionUtils/Connection+Notifications.swift new file mode 100644 index 0000000..2fcdd16 --- /dev/null +++ b/mlchtRemote/Utilities/ConnectionUtils/Connection+Notifications.swift @@ -0,0 +1,12 @@ +// +// Connection+Notifications.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/12/26. +// + +extension Connection { + public enum Notifications: String, NotificationName { + case phoneForegroundChanged + } +} diff --git a/mlchtRemote/Utilities/ConnectionUtils/Connection.swift b/mlchtRemote/Utilities/ConnectionUtils/Connection.swift new file mode 100644 index 0000000..f37fb7b --- /dev/null +++ b/mlchtRemote/Utilities/ConnectionUtils/Connection.swift @@ -0,0 +1,66 @@ +// +// Connection.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/12/26. +// + +import Foundation +import WatchConnectivity + +class Connection: NSObject, WCSessionDelegate { + func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: (any Error)?) { + sendWatchLoaded() + } + + func sessionReachabilityDidChange(_ session: WCSession) { + if session.isReachable { sendWatchLoaded() } + } + + func session(_ session: WCSession, didReceiveMessage message: [String : Any]) { + handle(message) + } + + func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { + handle(applicationContext) + } + + private func handle(_ dict: [String: Any]) { + if let isForeground = dict["isForeground"] as? Bool { + isPhoneForeground = isForeground + DispatchQueue.main.async { NotificationCenter.default.post(name: Connection.Notifications.phoneForegroundChanged.name, object: nil) } + } + } + + static let shared = Connection() + var isPhoneForeground = false + var isSupported = false + + public func bringUpWatchRemote() { + guard WCSession.isSupported() else { return } + let session = WCSession.default + session.delegate = self + session.activate() + } + + private func sendWatchLoaded() { + let session = WCSession.default + if session.isReachable { + session.sendMessage(["watchLoaded": true], replyHandler: nil) { error in + print("Failed to send watchLoaded: \(error)") + } + } else { + do { + try session.updateApplicationContext(["watchLoaded": true]) + } catch { + print("Failed to update context with watchLoaded: \(error)") + } + } + } + + func sendButtonPress(key: String) { + WCSession.default.sendMessage(["pressed": key], replyHandler: nil) { error in + print("Failed to send: \(error)") + } + } +} diff --git a/mlchtRemote/Utilities/Misc.swift b/mlchtRemote/Utilities/Misc.swift new file mode 100644 index 0000000..d91ebf2 --- /dev/null +++ b/mlchtRemote/Utilities/Misc.swift @@ -0,0 +1,20 @@ +// +// Misc.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/12/26. +// + +import SwiftUI + +/// An extension that enables Notification posting and getting. +extension RawRepresentable where RawValue == String, Self: NotificationName { + var name: Notification.Name { + get { return Notification.Name(self.rawValue) } + } +} + +/// A protocol that enables Notification posting and getting. +protocol NotificationName { + var name: Notification.Name { get } +} diff --git a/mlchtRemote/Utilities/ViewUtils/View.swift b/mlchtRemote/Utilities/ViewUtils/View.swift new file mode 100644 index 0000000..7a24a44 --- /dev/null +++ b/mlchtRemote/Utilities/ViewUtils/View.swift @@ -0,0 +1,41 @@ +// +// View.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/12/26. +// + +import SwiftUI + +/// Common cell content view to prevent the code from becoming massive +struct CellViewUtils: View { + var icon: String + var disabled: Bool + var dangerous: Bool + let content: Content + + init( + icon: String, + disabled: Bool?, + dangerous: Bool, + @ViewBuilder content: () -> Content + ) { + self.icon = icon + self.disabled = disabled ?? false + self.dangerous = dangerous + self.content = content() + } + + var body: some View { + VStack() { + HStack(spacing: 10) { + Image(systemName: icon) + .frame(maxWidth: 20) + .foregroundStyle(dangerous ? .red : Color.accentColor) + .symbolRenderingMode(.hierarchical) + content + .disabled(disabled) + } + } + } +} diff --git a/mlchtRemote/Views/Content+Controls.swift b/mlchtRemote/Views/Content+Controls.swift new file mode 100644 index 0000000..7df7dfe --- /dev/null +++ b/mlchtRemote/Views/Content+Controls.swift @@ -0,0 +1,74 @@ +// +// Content+Controls.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/12/26. +// + +import SwiftUI + +extension Content { + struct Controls: View { + @Binding var isPresented: Bool + @Binding var hasSeenOnce: Bool + + var mainAction: some View { + Button { + Connection.shared.sendButtonPress(key: "capture") + } label: { + Text("Take picture") + } + } + + var body: some View { + if #available(watchOS 11.0, *) { + CellViewUtils( + icon: "camera.aperture", + disabled: nil, + dangerous: false) + { mainAction.handGestureShortcut(.primaryAction) } + } else { + CellViewUtils( + icon: "camera.aperture", + disabled: nil, + dangerous: false) + { mainAction } + } + CellViewUtils( + icon: "gear", + disabled: nil, + dangerous: false) + { + Button { + Connection.shared.sendButtonPress(key: "settings") + } label: { + Text("Open Settings") + } + } + + CellViewUtils( + icon: "flashlight.off.fill", + disabled: nil, + dangerous: false) + { + Button { + Connection.shared.sendButtonPress(key: "flashlight") + } label: { + Text("Toggle flashlight") + } + } + + CellViewUtils( + icon: "camera.fill", + disabled: nil, + dangerous: false) + { + Button { + Connection.shared.sendButtonPress(key: "cameras") + } label: { + Text("Switch cameras") + } + } + } + } +} diff --git a/mlchtRemote/Views/Content+Debug.swift b/mlchtRemote/Views/Content+Debug.swift new file mode 100644 index 0000000..0b4ff42 --- /dev/null +++ b/mlchtRemote/Views/Content+Debug.swift @@ -0,0 +1,26 @@ +// +// Content+Debug.swift +// mlchtCamera +// +// Created by Eva Isabella Luna on 3/12/26. +// + +import SwiftUI + +extension Content { + struct Debug: View { + @Binding var isPresented: Bool + + var body: some View { + CellViewUtils( + icon: "wrench.and.screwdriver", + disabled: nil, + dangerous: false) + { + Button("DEBUG") { + isPresented.toggle() + } + } + } + } +} diff --git a/mlchtRemote/Views/Content.swift b/mlchtRemote/Views/Content.swift new file mode 100644 index 0000000..f8fadcb --- /dev/null +++ b/mlchtRemote/Views/Content.swift @@ -0,0 +1,80 @@ +// +// ContentView.swift +// MalachiteWatch Watch App +// +// Created by Eva Isabella Luna on 7/24/25. +// + +// this entire app is buttcheeks + +import Foundation +import SwiftUI + +struct CompanionWaitView: View { + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + if #available(watchOS 8.0, *) { + Image(systemName: "exclamationmark.triangle.fill") + .imageScale(.large) + .foregroundStyle(.tint) + } else { + Image(systemName: "exclamationmark.triangle.fill") + .imageScale(.large) + .foregroundColor(.accentColor) + } + Text("") + Text("Keep mlchtCamera open on your phone to use this app.") + .padding(10) + .multilineTextAlignment(.center) + } + } +} + +struct Content: View { + @State private var hasSeenOnce = false + @State private var isPresented = false + + var body: some View { + if #available(watchOS 9.0, *) { + NavigationStack { guts } + } else { + NavigationView { guts }.navigationViewStyle(.stack) + } + } + + var guts: some View { + List { + Controls(isPresented: $isPresented, hasSeenOnce: $hasSeenOnce) + Debug(isPresented: $isPresented) + } + .listStyle(.carousel) + .onAppear { + if !hasSeenOnce { + Connection.shared.bringUpWatchRemote() + isPresented = true + } + } + .onReceive(NotificationCenter.default.publisher(for: Connection.Notifications.phoneForegroundChanged.name)) { _ in + if !hasSeenOnce { hasSeenOnce = true } + if isPresented == !Connection.shared.isPhoneForeground { + isPresented = Connection.shared.isPhoneForeground + } + isPresented = !Connection.shared.isPhoneForeground + } + .fullScreenCover(isPresented: $isPresented) { + CompanionWaitView() + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button {} label: { + ProgressView() + } + .onTapGesture(count: 3) { + isPresented = false + } + } + } + } + } +} diff --git a/MalachiteWatch/MalachiteWatchApp.swift b/mlchtRemote/mlchtRemote.swift similarity index 53% rename from MalachiteWatch/MalachiteWatchApp.swift rename to mlchtRemote/mlchtRemote.swift index 3c10178..72d3c60 100644 --- a/MalachiteWatch/MalachiteWatchApp.swift +++ b/mlchtRemote/mlchtRemote.swift @@ -1,6 +1,6 @@ // -// MalachiteWatchApp.swift -// MalachiteWatch Watch App +// mlchtRemote.swift +// mlchtRemote // // Created by Eva Isabella Luna on 7/24/25. // @@ -8,10 +8,10 @@ import SwiftUI @main -struct MalachiteWatch_Watch_AppApp: App { +struct mlchtRemote: App { var body: some Scene { WindowGroup { - ContentView() + Content() } } }