specific carplay ui
This commit is contained in:
1 parent
9d8aef4039
commit
069d5cf1c3
8 files changed
+306
-6
No files matched your search
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
6F09B7BB2DEB873F00CDE89C /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */; };
|
6F09B7BB2DEB873F00CDE89C /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */; };
|
||||||
|
6F09BA332E00808300CDE89C /* Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 6F09BA322E00808300CDE89C /* Dynamic */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
6F09BA332E00808300CDE89C /* Dynamic in Frameworks */,
|
||||||
6F09B7BB2DEB873F00CDE89C /* ZIPFoundation in Frameworks */,
|
6F09B7BB2DEB873F00CDE89C /* ZIPFoundation in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
name = "Pocket Poster";
|
name = "Pocket Poster";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */,
|
6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */,
|
||||||
|
6F09BA322E00808300CDE89C /* Dynamic */,
|
||||||
);
|
);
|
||||||
productName = "Pocket Poster";
|
productName = "Pocket Poster";
|
||||||
productReference = 6F09B79D2DEB694B00CDE89C /* Pocket Poster.app */;
|
productReference = 6F09B79D2DEB694B00CDE89C /* Pocket Poster.app */;
|
||||||
@@ -105,6 +108,7 @@
|
|||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
|
6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
|
||||||
|
6F09BA312E00808300CDE89C /* XCRemoteSwiftPackageReference "Dynamic" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = 6F09B79E2DEB694B00CDE89C /* Products */;
|
productRefGroup = 6F09B79E2DEB694B00CDE89C /* Products */;
|
||||||
@@ -276,7 +280,7 @@
|
|||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -308,7 +312,7 @@
|
|||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -354,6 +358,14 @@
|
|||||||
minimumVersion = 0.9.19;
|
minimumVersion = 0.9.19;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
6F09BA312E00808300CDE89C /* XCRemoteSwiftPackageReference "Dynamic" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/mhdhejazi/Dynamic";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.2.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
@@ -362,6 +374,11 @@
|
|||||||
package = 6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
|
package = 6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
|
||||||
productName = ZIPFoundation;
|
productName = ZIPFoundation;
|
||||||
};
|
};
|
||||||
|
6F09BA322E00808300CDE89C /* Dynamic */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 6F09BA312E00808300CDE89C /* XCRemoteSwiftPackageReference "Dynamic" */;
|
||||||
|
productName = Dynamic;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 6F09B7952DEB694B00CDE89C /* Project object */;
|
rootObject = 6F09B7952DEB694B00CDE89C /* Project object */;
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "d0d4edfdf2bf3cd05b3ba2dec0af1a9c271c93f944cbba8677cc647f74a6b323",
|
"originHash" : "b781b75a76813cee1292db0b540a9eecd3df93961ced8a0063095503735dadb2",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "dynamic",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/mhdhejazi/Dynamic",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "ab9a2570862d54aed2663691bb767f881226a12f",
|
||||||
|
"version" : "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "zipfoundation",
|
"identity" : "zipfoundation",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// CPBitmapHandler.swift
|
||||||
|
// Pocket Poster
|
||||||
|
//
|
||||||
|
// Created by lemin on 6/16/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Dynamic
|
||||||
|
|
||||||
|
class CPBitmapHandler {
|
||||||
|
static func resizeAndSave(image: UIImage, to url: URL, size: CGSize) throws {
|
||||||
|
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||||||
|
image.draw(in: CGRect(origin: CGPoint.zero, size: size))
|
||||||
|
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()!
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
|
||||||
|
try? FileManager.default.removeItem(at: url)
|
||||||
|
|
||||||
|
resizedImage.writeToCPBitmapFile(to: url.path() as NSString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIImage {
|
||||||
|
func writeToCPBitmapFile(to path: NSString) {
|
||||||
|
Dynamic(self).writeToCPBitmapFile(path, flags: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -273,6 +273,32 @@ class PosterBoardManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyCarPlay(appHash: String, wallpapers: [CarPlayWallpaper]) throws {
|
||||||
|
// write the image
|
||||||
|
var toRemove: [URL] = []
|
||||||
|
for wallpaper in wallpapers {
|
||||||
|
if let data = wallpaper.selectedImageDataLight, let img = UIImage(data: data) {
|
||||||
|
let imgURL = SymHandler.getDocumentsDirectory().appendingPathComponent("CAR\(wallpaper.name)Dynamic-Light-11.cpbitmap")
|
||||||
|
img.writeToCPBitmapFile(to: imgURL.path() as NSString)
|
||||||
|
toRemove.append(imgURL)
|
||||||
|
}
|
||||||
|
if let data = wallpaper.selectedImageDataDark, let img = UIImage(data: data) {
|
||||||
|
let imgURL = SymHandler.getDocumentsDirectory().appendingPathComponent("CAR\(wallpaper.name)Dynamic-Dark-11.cpbitmap")
|
||||||
|
img.writeToCPBitmapFile(to: imgURL.path() as NSString)
|
||||||
|
toRemove.append(imgURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// symlink and apply
|
||||||
|
let _ = try SymHandler.createAppSymlink(for: "\(appHash)/Library/Caches/MappedImageCache/com.apple.CarPlayApp.wallpaper-images")
|
||||||
|
defer {
|
||||||
|
SymHandler.cleanup()
|
||||||
|
}
|
||||||
|
for imgURL in toRemove {
|
||||||
|
try FileManager.default.trashItem(at: imgURL, resultingItemURL: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static func clearCache() throws {
|
static func clearCache() throws {
|
||||||
SymHandler.cleanup()
|
SymHandler.cleanup()
|
||||||
let docDir = SymHandler.getDocumentsDirectory()
|
let docDir = SymHandler.getDocumentsDirectory()
|
||||||
|
|||||||
@@ -88,6 +88,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Apply CarPlay Wallpapers" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Applying Wallpapers..." : {
|
"Applying Wallpapers..." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -132,6 +135,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"CarPlay" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"CarPlay Wallpapers" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Clear App Cache" : {
|
"Clear App Cache" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -400,6 +409,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Enter App Hash" : {
|
"Enter App Hash" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"es" : {
|
"es" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -420,6 +430,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Enter CarPlayWallpaper App Hash" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Enter PosterBoard App Hash" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Enter your CarPlayWallpaper app hash in Settings." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Enter your PosterBoard app hash in Settings." : {
|
"Enter your PosterBoard app hash in Settings." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
//
|
||||||
|
// CarPlayView.swift
|
||||||
|
// Pocket Poster
|
||||||
|
//
|
||||||
|
// Created by lemin on 6/19/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import PhotosUI
|
||||||
|
|
||||||
|
struct CarPlayWallpaper: Identifiable {
|
||||||
|
var id = UUID()
|
||||||
|
var name: String
|
||||||
|
var lightImage: UIImage
|
||||||
|
var darkImage: UIImage
|
||||||
|
var selectedImageDataLight: Data?
|
||||||
|
var selectedImageDataDark: Data?
|
||||||
|
var changed: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CarPlayView: View {
|
||||||
|
@State var wallpapers: [CarPlayWallpaper] = []
|
||||||
|
@State var didChange: Bool = false
|
||||||
|
@State var showDark: Bool = false
|
||||||
|
|
||||||
|
@AppStorage("cpHash") var cpHash: String = ""
|
||||||
|
@ObservedObject var pbManager = PosterBoardManager.shared
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
ScrollView {
|
||||||
|
if wallpapers.isEmpty {
|
||||||
|
ProgressView()
|
||||||
|
} else {
|
||||||
|
LazyVGrid(columns: [GridItem(.adaptive(minimum: 150, maximum: 250))]) {
|
||||||
|
ForEach($wallpapers) { wallpaper in
|
||||||
|
WallpaperView(wallpaper: wallpaper, didChange: $didChange, showDark: $showDark)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpHash == "" {
|
||||||
|
Text("Enter your CarPlayWallpaper app hash in Settings.")
|
||||||
|
.padding()
|
||||||
|
} else if didChange {
|
||||||
|
Button(action: {
|
||||||
|
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
|
||||||
|
UIApplication.shared.alert(title: NSLocalizedString("Applying Wallpapers...", comment: ""), body: NSLocalizedString("Please wait", comment: ""), animated: false, withButton: false)
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
do {
|
||||||
|
try pbManager.applyCarPlay(appHash: cpHash, wallpapers: wallpapers)
|
||||||
|
SymHandler.cleanup() // just to be extra sure
|
||||||
|
try? FileManager.default.removeItem(at: pbManager.getTendiesStoreURL())
|
||||||
|
UIApplication.shared.dismissAlert(animated: false)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
|
||||||
|
pbManager.selectedTendies.removeAll()
|
||||||
|
Haptic.shared.notify(.success)
|
||||||
|
UIApplication.shared.confirmAlert(title: NSLocalizedString("Success!", comment: ""), body: NSLocalizedString("The PosterBoard app will now open. Please close it from the app switcher.", comment: ""), onOK: {
|
||||||
|
if !pbManager.openPosterBoard() {
|
||||||
|
UIApplication.shared.confirmAlert(title: NSLocalizedString("Falling Back to Shortcut", comment: ""), body: NSLocalizedString("PosterBoard failed to open directly. The fallback shortcut will now be opened.", comment: ""), onOK: {
|
||||||
|
pbManager.runShortcut(named: "PosterBoard")
|
||||||
|
}, noCancel: true)
|
||||||
|
}
|
||||||
|
}, noCancel: true)
|
||||||
|
})
|
||||||
|
} catch CocoaError.fileWriteUnknown {
|
||||||
|
presentError(ApplyError.wrongAppHash)
|
||||||
|
} catch CocoaError.fileWriteFileExists {
|
||||||
|
presentError(ApplyError.collectionsNeedsReset)
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
presentError(ApplyError.unexpected(info: error.localizedDescription))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Label("Apply CarPlay Wallpapers", systemImage: "checkmark.circle")
|
||||||
|
}
|
||||||
|
.buttonStyle(TintedButton(color: .blue, fullwidth: true))
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("CarPlay Wallpapers")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .topBarTrailing, content: {
|
||||||
|
Button(action: {
|
||||||
|
withAnimation {
|
||||||
|
showDark.toggle()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Image(systemName: showDark ? "moon" : "sun.max")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
if wallpapers.isEmpty {
|
||||||
|
// load the wallpapers
|
||||||
|
let frameworkPath = "/System/Library/PrivateFrameworks/CarPlayUIServices.framework"
|
||||||
|
do {
|
||||||
|
for file in try FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: frameworkPath), includingPropertiesForKeys: nil, options: .skipsHiddenFiles) {
|
||||||
|
if file.path().hasSuffix("-Light.heic") {
|
||||||
|
let imgData = try Data(contentsOf: file)
|
||||||
|
if let wpImg = UIImage(data: imgData) {
|
||||||
|
var darkImg = wpImg
|
||||||
|
if let darkData = try? Data(contentsOf: URL(fileURLWithPath: file.path().replacingOccurrences(of: "-Light.heic", with: "-Dark.heic"))) {
|
||||||
|
darkImg = UIImage(data: darkData) ?? wpImg
|
||||||
|
}
|
||||||
|
let name = file.lastPathComponent.replacingOccurrences(of: "-Light.heic", with: "")
|
||||||
|
wallpapers.append(.init(name: name, lightImage: wpImg, darkImage: darkImg, changed: false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func presentError(_ error: ApplyError) {
|
||||||
|
SymHandler.cleanup()
|
||||||
|
UIApplication.shared.dismissAlert(animated: false)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
|
||||||
|
Haptic.shared.notify(.error)
|
||||||
|
UIApplication.shared.alert(body: error.localizedDescription)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WallpaperView: View {
|
||||||
|
@Binding var wallpaper: CarPlayWallpaper
|
||||||
|
@Binding var didChange: Bool
|
||||||
|
@Binding var showDark: Bool
|
||||||
|
|
||||||
|
@State private var selectedItem: PhotosPickerItem? = nil
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
PhotosPicker(
|
||||||
|
selection: $selectedItem,
|
||||||
|
matching: .images,
|
||||||
|
photoLibrary: .shared()) {
|
||||||
|
VStack {
|
||||||
|
if showDark {
|
||||||
|
if let data = wallpaper.selectedImageDataDark, let img = UIImage(data: data) {
|
||||||
|
WallpaperImageView(img: img)
|
||||||
|
} else {
|
||||||
|
WallpaperImageView(img: wallpaper.darkImage)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let data = wallpaper.selectedImageDataLight, let img = UIImage(data: data) {
|
||||||
|
WallpaperImageView(img: img)
|
||||||
|
} else {
|
||||||
|
WallpaperImageView(img: wallpaper.lightImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.overlay {
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.stroke(.gray, lineWidth: 2)
|
||||||
|
}
|
||||||
|
.onChange(of: selectedItem) { newItem in
|
||||||
|
Task {
|
||||||
|
// Retrieve selected asset in the form of Data
|
||||||
|
if let data = try? await newItem?.loadTransferable(type: Data.self) {
|
||||||
|
if showDark {
|
||||||
|
wallpaper.selectedImageDataDark = data
|
||||||
|
} else {
|
||||||
|
wallpaper.selectedImageDataLight = data
|
||||||
|
}
|
||||||
|
didChange = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WallpaperImageView: View {
|
||||||
|
var img: UIImage
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Image(uiImage: img)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(1.25, contentMode: .fill)
|
||||||
|
.cornerRadius(8)
|
||||||
|
.clipped()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,10 @@ struct RootView: View {
|
|||||||
.tabItem {
|
.tabItem {
|
||||||
Label("Videos", systemImage: "camera")
|
Label("Videos", systemImage: "camera")
|
||||||
}
|
}
|
||||||
|
CarPlayView()
|
||||||
|
.tabItem {
|
||||||
|
Label("CarPlay", systemImage: "car")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ import SwiftUI
|
|||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
// Prefs
|
// Prefs
|
||||||
@AppStorage("pbHash") var pbHash: String = ""
|
@AppStorage("pbHash") var pbHash: String = ""
|
||||||
|
@AppStorage("cpHash") var cpHash: String = ""
|
||||||
|
|
||||||
@State var checkingForHash: Bool = false
|
@State var checkingForHash: Bool = false
|
||||||
@State var hashCheckTask: Task<Void, any Error>? = nil
|
@State var hashCheckTask: Task<Void, any Error>? = nil
|
||||||
@@ -18,14 +19,17 @@ struct SettingsView: View {
|
|||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
VStack {
|
VStack {
|
||||||
TextField("Enter App Hash", text: $pbHash)
|
TextField("Enter PosterBoard App Hash", text: $pbHash)
|
||||||
|
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||||
|
.font(.system(.body, design: .monospaced))
|
||||||
|
TextField("Enter CarPlayWallpaper App Hash", text: $cpHash)
|
||||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||||
.font(.system(.body, design: .monospaced))
|
.font(.system(.body, design: .monospaced))
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
// Run task to check until file exists from Nugget pc over AFC
|
// Run task to check until file exists from Nugget pc over AFC
|
||||||
Button(action: {
|
Button(action: {
|
||||||
UIApplication.shared.confirmAlert(title: "Waiting for app hash...", body: "Connect your device to Nugget and click the \"Pocket Poster Helper\" button.", confirmTitle: "Cancel", onOK: {
|
UIApplication.shared.confirmAlert(title: "Waiting for app hashes...", body: "Connect your device to Nugget and click the \"Pocket Poster Helper\" button.", confirmTitle: "Cancel", onOK: {
|
||||||
cancelWaitForHash()
|
cancelWaitForHash()
|
||||||
}, noCancel: true)
|
}, noCancel: true)
|
||||||
startWaitForHash()
|
startWaitForHash()
|
||||||
@@ -33,7 +37,7 @@ struct SettingsView: View {
|
|||||||
Text("Detect")
|
Text("Detect")
|
||||||
}
|
}
|
||||||
.foregroundStyle(.green)
|
.foregroundStyle(.green)
|
||||||
.onChange(of: checkingForHash) {
|
.onChange(of: checkingForHash) { _ in
|
||||||
if !checkingForHash {
|
if !checkingForHash {
|
||||||
// hide ui alert
|
// hide ui alert
|
||||||
UIApplication.shared.dismissAlert(animated: true)
|
UIApplication.shared.dismissAlert(animated: true)
|
||||||
@@ -137,6 +141,9 @@ struct SettingsView: View {
|
|||||||
if let pb = items.first {
|
if let pb = items.first {
|
||||||
pbHash = String(pb)
|
pbHash = String(pb)
|
||||||
}
|
}
|
||||||
|
if items.count >= 2 {
|
||||||
|
cpHash = String(items[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
|
|||||||
Reference in new issue
Block a user