specific carplay ui
This commit is contained in:
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()
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,10 @@ struct RootView: View {
|
||||
.tabItem {
|
||||
Label("Videos", systemImage: "camera")
|
||||
}
|
||||
CarPlayView()
|
||||
.tabItem {
|
||||
Label("CarPlay", systemImage: "car")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user