specific carplay ui

This commit is contained in:
leminlimez committed 2025-06-19 04:23:26 -04:00
1 parent 9d8aef4039
commit 069d5cf1c3
8 files changed
+306 -6

No files matched your search

@@ -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()
}
}
+4
View File
@@ -18,6 +18,10 @@ struct RootView: View {
.tabItem {
Label("Videos", systemImage: "camera")
}
CarPlayView()
.tabItem {
Label("CarPlay", systemImage: "car")
}
}
}
}
+10 -3
View File
@@ -10,6 +10,7 @@ import SwiftUI
struct SettingsView: View {
// Prefs
@AppStorage("pbHash") var pbHash: String = ""
@AppStorage("cpHash") var cpHash: String = ""
@State var checkingForHash: Bool = false
@State var hashCheckTask: Task<Void, any Error>? = nil
@@ -18,14 +19,17 @@ struct SettingsView: View {
List {
Section {
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())
.font(.system(.body, design: .monospaced))
HStack {
Spacer()
// Run task to check until file exists from Nugget pc over AFC
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()
}, noCancel: true)
startWaitForHash()
@@ -33,7 +37,7 @@ struct SettingsView: View {
Text("Detect")
}
.foregroundStyle(.green)
.onChange(of: checkingForHash) {
.onChange(of: checkingForHash) { _ in
if !checkingForHash {
// hide ui alert
UIApplication.shared.dismissAlert(animated: true)
@@ -137,6 +141,9 @@ struct SettingsView: View {
if let pb = items.first {
pbHash = String(pb)
}
if items.count >= 2 {
cpHash = String(items[1])
}
}
} catch {
print(error.localizedDescription)