Skip to content

Commit

Permalink
Merge pull request #142 from blackboxembedded/carplay
Browse files Browse the repository at this point in the history
Initial CarPlay Support
  • Loading branch information
blackboxembedded authored Aug 31, 2024
2 parents cb3c8fa + 28b8c98 commit aeeb027
Show file tree
Hide file tree
Showing 8 changed files with 980 additions and 864 deletions.
39 changes: 32 additions & 7 deletions WunderLINQ.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@
B98CA75D252E6C6B00530075 /* UIStoryboard+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CA75C252E6C6B00530075 /* UIStoryboard+extensions.swift */; };
B98CA762252E6F5500530075 /* String+Localizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CA761252E6F5500530075 /* String+Localizable.swift */; };
D80365FE2B377FCD00F48637 /* SoundManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80365FD2B377FCD00F48637 /* SoundManager.swift */; };
D81CBFAE2C8119580029C5AB /* Entitlements.plist in Resources */ = {isa = PBXBuildFile; fileRef = D81CBFAD2C8119580029C5AB /* Entitlements.plist */; };
D826545A2C82768000049F9E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82654582C82768000049F9E /* SceneDelegate.swift */; };
D826545B2C82768000049F9E /* CarPlaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82654592C82768000049F9E /* CarPlaySceneDelegate.swift */; };
D8585C53293EBD550025D0AB /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D8585C51293EBD540025D0AB /* Main_iPad.storyboard */; };
D87629122A7EDEB90021E7A7 /* standard-dashboard-portrait.svg in Resources */ = {isa = PBXBuildFile; fileRef = D87629112A7EDEB90021E7A7 /* standard-dashboard-portrait.svg */; };
D880B6022C6BFED900B4252B /* wav in Resources */ = {isa = PBXBuildFile; fileRef = D880B6012C6BFED900B4252B /* wav */; };
Expand Down Expand Up @@ -272,6 +275,9 @@
B98CA75C252E6C6B00530075 /* UIStoryboard+extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+extensions.swift"; sourceTree = "<group>"; };
B98CA761252E6F5500530075 /* String+Localizable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localizable.swift"; sourceTree = "<group>"; };
D80365FD2B377FCD00F48637 /* SoundManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundManager.swift; sourceTree = "<group>"; };
D81CBFAD2C8119580029C5AB /* Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = "<group>"; };
D82654582C82768000049F9E /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
D82654592C82768000049F9E /* CarPlaySceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarPlaySceneDelegate.swift; sourceTree = "<group>"; };
D8585C52293EBD540025D0AB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPad.storyboard; sourceTree = "<group>"; };
D87629112A7EDEB90021E7A7 /* standard-dashboard-portrait.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "standard-dashboard-portrait.svg"; sourceTree = "<group>"; };
D880B6012C6BFED900B4252B /* wav */ = {isa = PBXFileReference; lastKnownFileType = folder; path = wav; sourceTree = "<group>"; };
Expand Down Expand Up @@ -411,6 +417,7 @@
7EA4784C1F40B154000CEDDC /* WunderLINQ */ = {
isa = PBXGroup;
children = (
D82654572C82758F00049F9E /* CarPlay */,
7E00516528EA150B00BC3CC1 /* TaskList */,
7EBC4E2328E133B800B1998B /* comms */,
7EB80AB82790FF8800D3A051 /* hardware */,
Expand Down Expand Up @@ -462,6 +469,7 @@
7EA478561F40B154000CEDDC /* LaunchScreen.storyboard */,
7E4D76E521DDAEC100825175 /* InfoPlist.strings */,
7EA478591F40B154000CEDDC /* Info.plist */,
D81CBFAD2C8119580029C5AB /* Entitlements.plist */,
);
path = WunderLINQ;
sourceTree = "<group>";
Expand Down Expand Up @@ -566,6 +574,15 @@
path = External;
sourceTree = "<group>";
};
D82654572C82758F00049F9E /* CarPlay */ = {
isa = PBXGroup;
children = (
D82654592C82768000049F9E /* CarPlaySceneDelegate.swift */,
D82654582C82768000049F9E /* SceneDelegate.swift */,
);
path = CarPlay;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -623,7 +640,6 @@
7EA478491F40B154000CEDDC = {
CreatedOnToolsVersion = 8.3.3;
LastSwiftMigration = 1020;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.BackgroundModes = {
enabled = 1;
Expand All @@ -632,9 +648,7 @@
};
7EA4785D1F40B154000CEDDC = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = L5697YQ5RS;
LastSwiftMigration = 1020;
ProvisioningStyle = Automatic;
TestTargetID = 7EA478491F40B154000CEDDC;
};
};
Expand Down Expand Up @@ -683,6 +697,7 @@
D87629122A7EDEB90021E7A7 /* standard-dashboard-portrait.svg in Resources */,
7EA478581F40B154000CEDDC /* LaunchScreen.storyboard in Resources */,
7EA478551F40B154000CEDDC /* Assets.xcassets in Resources */,
D81CBFAE2C8119580029C5AB /* Entitlements.plist in Resources */,
7EB5EDE4252136F800B61160 /* Main.storyboard in Resources */,
7ED2013620D0D0030066BCE6 /* Default-568h@2x.png in Resources */,
7E755C13260A787100B29507 /* standard-dashboard.svg in Resources */,
Expand Down Expand Up @@ -750,6 +765,7 @@
buildActionMask = 2147483647;
files = (
7EA478711F40F273000CEDDC /* Device.swift in Sources */,
D826545B2C82768000049F9E /* CarPlaySceneDelegate.swift in Sources */,
7E80BD3D211CFF7300B8CA0D /* LocalisableButton.swift in Sources */,
7E5E0F78212289140048B9A2 /* AppUtility.swift in Sources */,
7EA478771F412BAC000CEDDC /* CLLocation+Extension.swift in Sources */,
Expand Down Expand Up @@ -796,6 +812,7 @@
7EBFD48E210ADD0000FC9665 /* String+Extensions.swift in Sources */,
7E9346B528E0A67F009E13C0 /* UIViewController+Extensions.swift in Sources */,
7EAF646A22FE261D00B11550 /* WeatherMapViewController.swift in Sources */,
D826545A2C82768000049F9E /* SceneDelegate.swift in Sources */,
7EA478811F44F57C000CEDDC /* MusicViewController.swift in Sources */,
7EF07772258FF762009A6BAA /* HWSettingsActionViewController.swift in Sources */,
7E620A0D26278E4900F6E23B /* AppSettingsViewController.swift in Sources */,
Expand Down Expand Up @@ -950,6 +967,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = WunderLINQ/Entitlements.plist;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
Expand Down Expand Up @@ -1012,6 +1030,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = WunderLINQ/Entitlements.plist;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
Expand Down Expand Up @@ -1043,7 +1062,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_LOCALIZABILITY_EMPTY_CONTEXT = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 3J48UMN5W2;
Expand Down Expand Up @@ -1073,7 +1092,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_LOCALIZABILITY_EMPTY_CONTEXT = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 3J48UMN5W2;
Expand Down Expand Up @@ -1101,7 +1120,9 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = L5697YQ5RS;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = WunderLINQTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -1110,6 +1131,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.blackboxembedded.WunderLINQTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WunderLINQ.app/WunderLINQ";
};
Expand All @@ -1120,7 +1142,9 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = L5697YQ5RS;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = WunderLINQTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -1129,6 +1153,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.blackboxembedded.WunderLINQTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WunderLINQ.app/WunderLINQ";
};
Expand Down
3 changes: 3 additions & 0 deletions WunderLINQ/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,9 @@
"dash_altitude_label" = "ELEV";
//VolumeViewController
"systemvolume_title" = "System Volume";
//CarPlay
"carplay_ios14_warning_text" = "iOS 14 Required";
"carplay_ios14_warning_detail" = "CarPlay Support Requires iOS 14+";
//Keyboard HID
"hid_0x00_label" = "Not Set";
"hid_keyboard_label" = "Standard";
Expand Down
141 changes: 141 additions & 0 deletions WunderLINQ/CarPlay/CarPlaySceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
WunderLINQ Client Application
Copyright (C) 2020 Keith Conger, Black Box Embedded, LLC

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import UIKit
import CarPlay

class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPListTemplateDelegate {
var interfaceController: CPInterfaceController?
var refreshTimer: Timer?

func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController) {

self.interfaceController = interfaceController

updateGridTemplate()

// Start the timer to refresh the grid template every 10 seconds
startRefreshTimer()
}

@objc func updateGridTemplate() {

if #available(iOS 14.0, *) {
let dataPoint1 = UserDefaults.standard.integer(forKey: "grid_one_preference")
let gridButton1 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint1))],
image: MotorcycleData.getIcon(dataPoint: dataPoint1)) { button in
}
let dataPoint2 = UserDefaults.standard.integer(forKey: "grid_two_preference")
let gridButton2 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint2))],
image: MotorcycleData.getIcon(dataPoint: dataPoint2)) { button in
}
let dataPoint3 = UserDefaults.standard.integer(forKey: "grid_three_preference")
let gridButton3 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint3))],
image: MotorcycleData.getIcon(dataPoint: dataPoint3)) { button in
}
let dataPoint4 = UserDefaults.standard.integer(forKey: "grid_four_preference")
let gridButton4 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint4))],
image: MotorcycleData.getIcon(dataPoint: dataPoint4)) { button in
}
let dataPoint5 = UserDefaults.standard.integer(forKey: "grid_five_preference")
let gridButton5 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint5))],
image: MotorcycleData.getIcon(dataPoint: dataPoint5)) { button in
}
let dataPoint6 = UserDefaults.standard.integer(forKey: "grid_six_preference")
let gridButton6 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint6))],
image: MotorcycleData.getIcon(dataPoint: dataPoint6)) { button in
}
let dataPoint7 = UserDefaults.standard.integer(forKey: "grid_seven_preference")
let gridButton7 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint7))],
image: MotorcycleData.getIcon(dataPoint: dataPoint7)) { button in
}
let dataPoint8 = UserDefaults.standard.integer(forKey: "grid_eight_preference")
let gridButton8 = CPGridButton(titleVariants: [padString(MotorcycleData.getValue(dataPoint: dataPoint8))],
image: MotorcycleData.getIcon(dataPoint: dataPoint8)) { button in
}
let gridTemplate = CPGridTemplate(title: NSLocalizedString("product", comment: ""), gridButtons: [gridButton1,gridButton2,gridButton3,gridButton4,gridButton5,gridButton6,gridButton7,gridButton8])

interfaceController?.setRootTemplate(gridTemplate,
animated: true,
completion: nil)
} else {
// Fallback for iOS 13
interfaceController?.setRootTemplate(self.listTemplate(),
animated: true)
}
}

func startRefreshTimer() {
refreshTimer?.invalidate() // Invalidate any existing timer
refreshTimer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(updateGridTemplate), userInfo: nil, repeats: true)
}

func listTemplate() -> CPListTemplate {
let item = CPListItem(text: NSLocalizedString("carplay_ios14_warning_text", comment: ""), detailText: NSLocalizedString("carplay_ios14_warning_detail", comment: ""))

if #available(iOS 14.0, *) {
item.handler = { item, completion in
NSLog("Item selected")
completion()
}
}

let section = CPListSection(items: [item])
let listTemplate = CPListTemplate(title: NSLocalizedString("product", comment: ""), sections: [section])

if #available(iOS 14.0, *) {
// Do nothing, handler is already set
} else {
listTemplate.delegate = self
}

return listTemplate
}

func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didDisconnectInterfaceController interfaceController: CPInterfaceController) {
self.interfaceController = nil
refreshTimer?.invalidate() // Invalidate the timer when disconnected
}

// Delegate method for CPListTemplateDelegate (iOS 13)
func listTemplate(_ listTemplate: CPListTemplate, didSelect item: CPListItem, completionHandler: @escaping () -> Void) {
NSLog("Item selected")
completionHandler()
}

func padString(_ input: String) -> String {
let minimumLength = 10

// If the string is already of sufficient length, return it as is
if input.count >= minimumLength {
return input
}

// Calculate the number of spaces needed
let totalPadding = minimumLength - input.count
let leadingPadding = totalPadding / 2
let trailingPadding = totalPadding - leadingPadding

// Create the padded string
let paddedString = String(repeating: " ", count: leadingPadding) + input + String(repeating: " ", count: trailingPadding)

return paddedString
}

}
25 changes: 25 additions & 0 deletions WunderLINQ/CarPlay/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
WunderLINQ Client Application
Copyright (C) 2020 Keith Conger, Black Box Embedded, LLC

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

}
15 changes: 15 additions & 0 deletions WunderLINQ/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
Entitlements.plist
WunderLINQ
Created by Keith Conger on 8/29/24.
Copyright (c) 2024 Black Box Embedded, LLC. All rights reserved.
-->
<plist version="1.0">
<dict>
<key>com.apple.developer.carplay-driving-task</key>
<true/>
</dict>
</plist>
Loading

0 comments on commit aeeb027

Please sign in to comment.