Skip to content

Commit

Permalink
feat: toggle 'Game Mode' & alt icon selection
Browse files Browse the repository at this point in the history
  • Loading branch information
khcrysalis committed Jan 19, 2025
1 parent 33cc66a commit 39d24f1
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 33 deletions.
3 changes: 3 additions & 0 deletions Shared/Localizations/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION" = "Force ProMotion";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables ProMotion capabilities within the app, however on lower versions of 15.x this may not be enough.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE" = "Force Game Mode";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables Game Mode within the app, minimizing background activity and prioritized performance for the app.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN" = "Force Fullscreen";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION" = "Forces only fullscreen capabilities within iPad apps, disallowing sharing the screen with other apps. On an external screen, the window for an app with this setting maintains its canvas size.";

Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION" = "Force ProMotion";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables ProMotion capabilities within the app, however on lower versions of 15.x this may not be enough.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE" = "Force Game Mode";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables Game Mode within the app, minimizing background activity and prioritized performance for the app.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN" = "Force Fullscreen";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION" = "Forces only fullscreen capabilities within iPad apps, disallowing sharing the screen with other apps. On an external screen, the window for an app with this setting maintains its canvas size.";

Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION" = "Force ProMotion";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables ProMotion capabilities within the app, however on lower versions of 15.x this may not be enough.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE" = "Force Game Mode";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables Game Mode within the app, minimizing background activity and prioritized performance for the app.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN" = "Force Fullscreen";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION" = "Forces only fullscreen capabilities within iPad apps, disallowing sharing the screen with other apps. On an external screen, the window for an app with this setting maintains its canvas size.";

Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION" = "Force ProMotion";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables ProMotion capabilities within the app, however on lower versions of 15.x this may not be enough.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE" = "Force Game Mode";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables Game Mode within the app, minimizing background activity and prioritized performance for the app.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN" = "Force Fullscreen";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION" = "Forces only fullscreen capabilities within iPad apps, disallowing sharing the screen with other apps. On an external screen, the window for an app with this setting maintains its canvas size.";

Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION" = "Принудить ProMotion";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Включает возможности ProMotion в приложении, однако на более низких версиях 15.x этого может быть недостаточно.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE" = "Force Game Mode";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables Game Mode within the app, minimizing background activity and prioritized performance for the app.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN" = "Принудить полноэкранный режим";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION" = "Принудительно включает только полноэкранные возможности в приложениях для iPad, запрещая совместное использование экрана с другими приложениями. На внешнем экране окно приложения с этой настройкой сохраняет свой размер холста.";

Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/zh.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION" = "强制启用 ProMotion";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "在应用程序中启用 ProMotion 功能,但在 15.x 的较低版本上可能还不够。";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE" = "Force Game Mode";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION" = "Enables Game Mode within the app, minimizing background activity and prioritized performance for the app.";

"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN" = "强制全屏";
"APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION" = "仅在 iPad 应用程序中启用全屏功能,不允许与其他应用程序共享屏幕。在外部屏幕上,启用此设置的应用程序窗口将保持画布大小。";

Expand Down
1 change: 1 addition & 0 deletions Shared/Signing/AppSigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ func updateInfoPlist(infoDict: NSMutableDictionary, main: SigningMainDataWrapper
if options.signingOptions.removeSupportedDevices { infoDict.removeObject(forKey: "UISupportedDevices") }
if options.signingOptions.removeURLScheme { infoDict.removeObject(forKey: "CFBundleURLTypes") }
if options.signingOptions.forceProMotion { infoDict.setObject(true, forKey: "CADisableMinimumFrameDurationOnPhone" as NSCopying)}
if options.signingOptions.forceGameMode { infoDict.setObject(true, forKey: "GCSupportsGameMode" as NSCopying)}
if options.signingOptions.forceForceFullScreen { infoDict.setObject(true, forKey: "UIRequiresFullScreen" as NSCopying) }
if options.signingOptions.forceMinimumVersion != "Automatic" { infoDict.setObject(options.signingOptions.forceMinimumVersion, forKey: "MinimumOSVersion" as NSCopying) }
if options.signingOptions.forceLightDarkAppearence != "Automatic" { infoDict.setObject(options.signingOptions.forceLightDarkAppearence, forKey: "UIUserInterfaceStyle" as NSCopying)}
Expand Down
4 changes: 4 additions & 0 deletions feather.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
33C0409E2C30DD3E00243D90 /* AlertKit in Frameworks */ = {isa = PBXBuildFile; productRef = 33C0409D2C30DD3E00243D90 /* AlertKit */; };
33C29F202C7BD73800EF7608 /* UIUserInterfaceStyle+allCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C29F1F2C7BD73800EF7608 /* UIUserInterfaceStyle+allCases.swift */; };
33CE81A92C4295F300C05327 /* CertImportingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CE81A82C4295F300C05327 /* CertImportingViewController.swift */; };
33DA8AC52D3CA2D7009357AD /* SettingsAltIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DA8AC42D3CA2D7009357AD /* SettingsAltIconView.swift */; };
33DF8D7D2C0828CF00D6C05F /* Zsign.md in Resources */ = {isa = PBXBuildFile; fileRef = 33DF8D7C2C0828CF00D6C05F /* Zsign.md */; };
33DF8D7F2C0828FB00D6C05F /* Nuke.md in Resources */ = {isa = PBXBuildFile; fileRef = 33DF8D7E2C0828FB00D6C05F /* Nuke.md */; };
33DF8D812C08290F00D6C05F /* HttpSwift_RequestSwift_SocketSwift.md in Resources */ = {isa = PBXBuildFile; fileRef = 33DF8D802C08290F00D6C05F /* HttpSwift_RequestSwift_SocketSwift.md */; };
Expand Down Expand Up @@ -247,6 +248,7 @@
33C0409A2C30B4A100243D90 /* AppDownload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDownload.swift; sourceTree = "<group>"; };
33C29F1F2C7BD73800EF7608 /* UIUserInterfaceStyle+allCases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIUserInterfaceStyle+allCases.swift"; sourceTree = "<group>"; };
33CE81A82C4295F300C05327 /* CertImportingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertImportingViewController.swift; sourceTree = "<group>"; };
33DA8AC42D3CA2D7009357AD /* SettingsAltIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAltIconView.swift; sourceTree = "<group>"; };
33DF8D7C2C0828CF00D6C05F /* Zsign.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Zsign.md; sourceTree = "<group>"; };
33DF8D7E2C0828FB00D6C05F /* Nuke.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Nuke.md; sourceTree = "<group>"; };
33DF8D802C08290F00D6C05F /* HttpSwift_RequestSwift_SocketSwift.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = HttpSwift_RequestSwift_SocketSwift.md; sourceTree = "<group>"; };
Expand Down Expand Up @@ -561,6 +563,7 @@
children = (
339FE3352CCDE05A00C297BA /* SigningsViewController.swift */,
339FE3452CCE3CB100C297BA /* SigningsViewController+Import-AppIcon.swift */,
33DA8AC42D3CA2D7009357AD /* SettingsAltIconView.swift */,
33BE87622C6F17870044D245 /* SigningsInputViewController.swift */,
33E3B6712C8151ED0075E3A2 /* SigningsDylibViewController.swift */,
33BE87662C6F17DF0044D245 /* SigningsTweakViewController.swift */,
Expand Down Expand Up @@ -1060,6 +1063,7 @@
33951CFE2C37AFCF00802F78 /* FloatingActionButton.swift in Sources */,
33E4D8412C67E5C3006A1C26 /* DonationTableViewCell.swift in Sources */,
33E5A5992CC8586E00532930 /* LanguageViewController.swift in Sources */,
33DA8AC52D3CA2D7009357AD /* SettingsAltIconView.swift in Sources */,
33EEDAA42C5D9358009089DC /* CoreDataManager+DownloadedApps.swift in Sources */,
339FE3442CCE343C00C297BA /* SwitchViewCell.swift in Sources */,
33BE877D2C729A7D0044D245 /* DownloadCertificate.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions iOS/Views/Signing/SigningData/SigningOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct SigningOptions: Codable {
var removeSupportedDevices: Bool = true
var removeURLScheme: Bool = false
var forceProMotion: Bool = false
var forceGameMode: Bool = false

var forceForceFullScreen: Bool = false
var forceiTunesFileSharing: Bool = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,53 +20,58 @@ func toggleOptions(signingDataWrapper: SigningDataWrapper) -> [TogglesOption] {
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_PLUGINS"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_PLUGINS_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.removePlugins
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_ALLOW_BROWSING_DOCUMENTS"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_ALLOW_BROWSING_DOCUMENTS_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.forceFileSharing
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_UISUPPORTEDDEVICES"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_UISUPPORTEDDEVICES_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.removeSupportedDevices
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_URLSCHEME"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_URLSCHEME_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.removeURLScheme
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.forceProMotion
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_GAME_MODE"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.forceGameMode
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.forceForceFullScreen
),
TogglesOption(
binding: signingDataWrapper.signingOptions.forceForceFullScreen
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_ALLOW_ITUNES_SHARING"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_ALLOW_ITUNES_SHARING_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.forceiTunesFileSharing
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_FORCELOCALIZATIONS"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_FORCELOCALIZATIONS_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.forceTryToLocalize
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_PROVISIONING"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_PROVISIONING_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.removeProvisioningFile
),
TogglesOption(
),
TogglesOption(
title: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_DELETE_PLACEHOLDER_WATCH_APP"),
footer: String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_DELETE_PLACEHOLDER_WATCH_APP_DESCRIPTION"),
binding: signingDataWrapper.signingOptions.removeWatchPlaceHolder
),
]
),
]
}

class SigningsOptionViewController: UITableViewController {
Expand Down Expand Up @@ -148,21 +153,24 @@ class SigningsOptionViewController: UITableViewController {
case 8: // Force ProMotion
signingDataWrapper.signingOptions.forceProMotion = sender.isOn
Debug.shared.log(message: "Force ProMotion (tag 8) set to: \(sender.isOn)")
case 9: // Force fullscreen
case 9: // Force GameMode
signingDataWrapper.signingOptions.forceGameMode = sender.isOn
Debug.shared.log(message: "Force ProMotion (tag 0) set to: \(sender.isOn)")
case 10: // Force fullscreen
signingDataWrapper.signingOptions.forceForceFullScreen = sender.isOn
Debug.shared.log(message: "Force fullscreen (tag 9) set to: \(sender.isOn)")
case 10: // Force iTunes file sharing
Debug.shared.log(message: "Force fullscreen (tag 10) set to: \(sender.isOn)")
case 11: // Force iTunes file sharing
signingDataWrapper.signingOptions.forceiTunesFileSharing = sender.isOn
Debug.shared.log(message: "Force iTunes file sharing (tag 10) set to: \(sender.isOn)")
case 11: // Force try to localize
Debug.shared.log(message: "Force iTunes file sharing (tag 11) set to: \(sender.isOn)")
case 12: // Force try to localize
signingDataWrapper.signingOptions.forceTryToLocalize = sender.isOn
Debug.shared.log(message: "Force try to localize (tag 11) set to: \(sender.isOn)")
case 12: // Remove provisioning file
Debug.shared.log(message: "Force try to localize (tag 12) set to: \(sender.isOn)")
case 13: // Remove provisioning file
signingDataWrapper.signingOptions.removeProvisioningFile = sender.isOn
Debug.shared.log(message: "Remove provisioning file (tag 12) set to: \(sender.isOn)")
case 13: // Remove watch placeholder
Debug.shared.log(message: "Remove provisioning file (tag 13) set to: \(sender.isOn)")
case 14: // Remove watch placeholder
signingDataWrapper.signingOptions.removeWatchPlaceHolder = sender.isOn
Debug.shared.log(message: "Remove watch placeholder (tag 13) set to: \(sender.isOn)")
Debug.shared.log(message: "Remove watch placeholder (tag 14) set to: \(sender.isOn)")
default:
break
}
Expand Down
120 changes: 120 additions & 0 deletions iOS/Views/Signing/SigningViewController/SettingsAltIconView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// SettingsAltIconView.swift
// feather
//
// Created by samara on 18.01.2025.
//

import SwiftUI

struct SettingsAltIconView: View {
@Environment(\.dismiss) var dismiss

private let mainOptions: SigningMainDataWrapper
private let applicationPath: URL

init(mainOptions: SigningMainDataWrapper, app: URL) {
self.mainOptions = mainOptions
self.applicationPath = app
}

var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 100), spacing: 8)], spacing: 8) {
if let defaultIcon = loadDefaultIcon() {
IconButton(
iconPath: defaultIcon,
name: "Default",
applicationPath: applicationPath,
action: {
mainOptions.mainOptions.iconURL = nil
dismiss()
NotificationCenter.default.post(name: Notification.Name("reloadSigningController"), object: nil)
}
)
}

ForEach(loadAlternateIcons().sorted(by: { $0.key < $1.key }), id: \.key) { name, path in
IconButton(
iconPath: path,
name: name,
applicationPath: applicationPath,
action: {
mainOptions.mainOptions.iconURL = UIImage(contentsOfFile: applicationPath.appendingPathComponent(path).path)
dismiss()
NotificationCenter.default.post(name: Notification.Name("reloadSigningController"), object: nil)
}
)
}
}
.padding()
}
.navigationTitle("Alt Icons")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
Button("Close") { dismiss() }
}
}
}
}

extension SettingsAltIconView {
// im not making this better, I may be reusing code but I dont carfe
private func loadDefaultIcon() -> String? {
guard let infoPlistPath = applicationPath.appendingPathComponent("Info.plist") as? URL,
let infoPlist = NSDictionary(contentsOf: infoPlistPath),
let iconDict = infoPlist["CFBundleIcons"] as? [String: Any],
let primaryIcon = iconDict["CFBundlePrimaryIcon"] as? [String: Any],
let files = primaryIcon["CFBundleIconFiles"] as? [String],
let iconPath = files.first else {
return nil
}
return iconPath
}

private func loadAlternateIcons() -> [String: String] {
guard let infoPlistPath = applicationPath.appendingPathComponent("Info.plist") as? URL,
let infoPlist = NSDictionary(contentsOf: infoPlistPath),
let iconDict = infoPlist["CFBundleIcons"] as? [String: Any],
let alternateIcons = iconDict["CFBundleAlternateIcons"] as? [String: [String: Any]] else {
return [:]
}

var icons: [String: String] = [:]
for (name, details) in alternateIcons {
if let files = details["CFBundleIconFiles"] as? [String],
let iconPath = files.first {
icons[name] = iconPath
}
}
return icons
}
}

private struct IconButton: View {
let iconPath: String
let name: String
let applicationPath: URL
let action: () -> Void

var body: some View {
Button(action: action) {
VStack {
Image(uiImage: UIImage(contentsOfFile: applicationPath.appendingPathComponent(iconPath).path) ?? UIImage())
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
Text(name)
.font(.caption)
.fontWeight(.bold)
.lineLimit(2)
.multilineTextAlignment(.center)
.foregroundStyle(Color.primary)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.aspectRatio(1, contentMode: .fill)
}
}
}
Loading

0 comments on commit 39d24f1

Please sign in to comment.