[ExpFeat] Alternate App Icons (#259)
commit 34ed9726034adbb439515ccb3d576311603bfe2b
Author: Chris Rittenhouse <dev@litritt.com>
Date: Thu Aug 3 15:31:47 2023 -0400
Removes `experimentalFeatures` property from `Settings`
commit 4e31d22d56d68439a340707d2fdbac3763df1e3a
Author: Chris Rittenhouse <dev@litritt.com>
Date: Thu Aug 3 15:17:47 2023 -0400
Reorder icons
commit 7509eaa29946b622ce0ad920a740f0212db5e771
Author: Chris Rittenhouse <dev@litritt.com>
Date: Thu Aug 3 15:17:24 2023 -0400
Moves icon changing code to `AlternateAppIcons`
commit 84821ef99ded74c066563041618aec3c4acb30d7
Author: Chris Rittenhouse <dev@litritt.com>
Date: Thu Aug 3 14:33:15 2023 -0400
Fixes icon authors
commit 0b821830f09b434fa375c37a2504c21ee5f90bdb
Author: Chris Rittenhouse <dev@litritt.com>
Date: Fri Jul 21 01:44:00 2023 -0400
Adds comments for functional alternate app icon code
commit e8dd6165619bc143f13dde51e5160af2695ef17e
Author: Chris Rittenhouse <dev@litritt.com>
Date: Fri Jul 21 01:30:03 2023 -0400
Adds implementation for changing app icon
commit 1e07483d18d1e865804ae34d6011844b63cb33cc
Author: Chris Rittenhouse <dev@litritt.com>
Date: Fri Jul 21 01:28:24 2023 -0400
Adds Alternate App Icon experimental feature and options
commit 46ed2ce3ae9593d4cc95f29d6caa09f33f2ce62d
Author: Chris Rittenhouse <dev@litritt.com>
Date: Fri Jul 21 01:26:51 2023 -0400
Adds extension to handle retrieving app icon images
commit 9dddb5f98f4523e7877f97c66a5ae95e6513cc09
Author: Chris Rittenhouse <dev@litritt.com>
Date: Fri Jul 21 01:24:19 2023 -0400
Adds experimental features as a property of `Settings`
commit d74038fd418a0e6fcd45942cb67e722904fc3e24
Author: Chris Rittenhouse <dev@litritt.com>
Date: Fri Jul 21 01:23:02 2023 -0400
Adds alternate icon assets
Co-authored-by: Chris Rittenhouse <dev@litritt.com>
This commit is contained in:
parent
061f5abd3e
commit
f3534e4415
@ -34,6 +34,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
1FA4ABA79AB72914FE414A61 /* libPods-Delta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC866E433B3BA9AE18ABA1EC /* libPods-Delta.a */; };
|
||||
87343D7B985519A5890A61C6 /* libPods-DeltaPreviews.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E3E5A45AB20C8A87754453B /* libPods-DeltaPreviews.a */; };
|
||||
AC1AE30A2A69BD3A00956EB9 /* AlternateAppIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1AE3092A69BD3A00956EB9 /* AlternateAppIcons.swift */; };
|
||||
AC1AE30C2A6A068F00956EB9 /* Bundle+AppIconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1AE30B2A6A068F00956EB9 /* Bundle+AppIconImage.swift */; };
|
||||
AC1C991029F8B8C30020E6E4 /* ToastNotificationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1C990F29F8B8C30020E6E4 /* ToastNotificationOptions.swift */; };
|
||||
AC1C992729F9F1CF0020E6E4 /* GameViewController+ExperimentalToasts.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1C992629F9F1CF0020E6E4 /* GameViewController+ExperimentalToasts.swift */; };
|
||||
ACF7E30D29F73D03000FE071 /* GameScreenshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACF7E30C29F73D03000FE071 /* GameScreenshots.swift */; };
|
||||
@ -272,6 +274,8 @@
|
||||
8ECE6641DE30D01EA30FE7F6 /* Pods-DeltaPreviews.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DeltaPreviews.release.xcconfig"; path = "Pods/Target Support Files/Pods-DeltaPreviews/Pods-DeltaPreviews.release.xcconfig"; sourceTree = "<group>"; };
|
||||
A01281C7023C0041B25963BE /* Pods-DeltaPreviews.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DeltaPreviews.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DeltaPreviews/Pods-DeltaPreviews.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A19FF50F55441BC2B2248241 /* Pods-Delta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.release.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.release.xcconfig"; sourceTree = "<group>"; };
|
||||
AC1AE3092A69BD3A00956EB9 /* AlternateAppIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlternateAppIcons.swift; sourceTree = "<group>"; };
|
||||
AC1AE30B2A6A068F00956EB9 /* Bundle+AppIconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AppIconImage.swift"; sourceTree = "<group>"; };
|
||||
AC1C990F29F8B8C30020E6E4 /* ToastNotificationOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastNotificationOptions.swift; sourceTree = "<group>"; };
|
||||
AC1C992629F9F1CF0020E6E4 /* GameViewController+ExperimentalToasts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GameViewController+ExperimentalToasts.swift"; sourceTree = "<group>"; };
|
||||
ACF7E30C29F73D03000FE071 /* GameScreenshots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScreenshots.swift; sourceTree = "<group>"; };
|
||||
@ -561,6 +565,7 @@
|
||||
AC1C992629F9F1CF0020E6E4 /* GameViewController+ExperimentalToasts.swift */,
|
||||
D5CDCCC32A85765900E22131 /* OSLog+Delta.swift */,
|
||||
D5CDCCEA2A8593FC00E22131 /* UserDefaults+Delta.swift */,
|
||||
AC1AE30B2A6A068F00956EB9 /* Bundle+AppIconImage.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -1120,6 +1125,7 @@
|
||||
ACF7E30C29F73D03000FE071 /* GameScreenshots.swift */,
|
||||
AC1C990F29F8B8C30020E6E4 /* ToastNotificationOptions.swift */,
|
||||
D5147EC72A817B4A00D6CD64 /* ReviewSaveStatesOptions.swift */,
|
||||
AC1AE3092A69BD3A00956EB9 /* AlternateAppIcons.swift */,
|
||||
);
|
||||
path = Features;
|
||||
sourceTree = "<group>";
|
||||
@ -1636,8 +1642,10 @@
|
||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
||||
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */,
|
||||
BFDCA1E9244F7E1000B8FBDB /* Delta5ToDelta6.xcmappingmodel in Sources */,
|
||||
AC1AE30A2A69BD3A00956EB9 /* AlternateAppIcons.swift in Sources */,
|
||||
D5A817B329DF6C6C00904AFE /* ExperimentalFeatures.swift in Sources */,
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||
AC1AE30C2A6A068F00956EB9 /* Bundle+AppIconImage.swift in Sources */,
|
||||
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */,
|
||||
BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */,
|
||||
D5F82FB82981D3AC00B229AF /* LegacySearchBar.swift in Sources */,
|
||||
@ -1919,6 +1927,7 @@
|
||||
baseConfigurationReference = C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_MODULES_AUTOLINK = NO;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
@ -1952,6 +1961,7 @@
|
||||
baseConfigurationReference = A19FF50F55441BC2B2248241 /* Pods-Delta.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_MODULES_AUTOLINK = NO;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
|
||||
@ -41,6 +41,11 @@ struct ExperimentalFeatures: FeatureContainer
|
||||
options: ReviewSaveStatesOptions())
|
||||
var reviewSaveStates
|
||||
|
||||
@Feature(name: "Alternate App Icon",
|
||||
description: "Change the app icon.",
|
||||
options: AlternateAppIconOptions())
|
||||
var alternateAppIcons
|
||||
|
||||
private init()
|
||||
{
|
||||
self.prepareFeatures()
|
||||
|
||||
124
Delta/Experimental Features/Features/AlternateAppIcons.swift
Normal file
124
Delta/Experimental Features/Features/AlternateAppIcons.swift
Normal file
@ -0,0 +1,124 @@
|
||||
//
|
||||
// AlternateAppIcons.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Chris Rittenhouse on 5/2/23.
|
||||
// Copyright © 2023 LitRitt. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
import DeltaFeatures
|
||||
|
||||
enum AppIcon: String, CaseIterable, CustomStringConvertible, Identifiable
|
||||
{
|
||||
case normal = "Default"
|
||||
case gba4ios = "GBA4iOS"
|
||||
case inverted = "Inverted"
|
||||
case pixelated = "Pixelated"
|
||||
case skin = "Controller Skin"
|
||||
|
||||
var description: String {
|
||||
return self.rawValue
|
||||
}
|
||||
|
||||
var id: String {
|
||||
return self.rawValue
|
||||
}
|
||||
|
||||
var author: String {
|
||||
switch self
|
||||
{
|
||||
case .normal: return "Caroline Moore"
|
||||
case .gba4ios: return "Paul Thorsen"
|
||||
case .inverted, .skin, .pixelated: return "LitRitt"
|
||||
}
|
||||
}
|
||||
|
||||
var assetName: String {
|
||||
switch self
|
||||
{
|
||||
case .normal: return "AppIcon"
|
||||
case .gba4ios: return "IconGBA4iOS"
|
||||
case .inverted: return "IconInverted"
|
||||
case .pixelated: return "IconPixelated"
|
||||
case .skin: return "IconSkin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppIcon: Equatable
|
||||
{
|
||||
static func == (lhs: AppIcon, rhs: AppIcon) -> Bool
|
||||
{
|
||||
return lhs.description == rhs.description
|
||||
}
|
||||
}
|
||||
|
||||
extension AppIcon: LocalizedOptionValue
|
||||
{
|
||||
var localizedDescription: Text {
|
||||
Text(self.description)
|
||||
}
|
||||
}
|
||||
|
||||
struct AlternateAppIconOptions
|
||||
{
|
||||
@Option(name: "Alternate App Icon",
|
||||
description: "Choose from alternate app icons created by the community.",
|
||||
detailView: { value in
|
||||
List {
|
||||
ForEach(AppIcon.allCases) { icon in
|
||||
HStack {
|
||||
if icon == value.wrappedValue
|
||||
{
|
||||
Text("✓")
|
||||
}
|
||||
icon.localizedDescription
|
||||
Text("- by \(icon.author)")
|
||||
.font(.system(size: 15))
|
||||
.foregroundColor(.gray)
|
||||
Spacer()
|
||||
Image(uiImage: Bundle.appIcon(for: icon) ?? UIImage())
|
||||
.cornerRadius(13)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
value.wrappedValue = icon
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: value.wrappedValue) { _ in
|
||||
updateAppIcon()
|
||||
}
|
||||
.displayInline()
|
||||
})
|
||||
var icon: AppIcon = .normal
|
||||
}
|
||||
|
||||
extension AlternateAppIconOptions
|
||||
{
|
||||
static func updateAppIcon()
|
||||
{
|
||||
// Get current icon
|
||||
let currentIcon = UIApplication.shared.alternateIconName
|
||||
|
||||
// Apply chosen icon if feature is enabled
|
||||
if ExperimentalFeatures.shared.alternateAppIcons.isEnabled
|
||||
{
|
||||
let icon = ExperimentalFeatures.shared.alternateAppIcons.icon
|
||||
|
||||
// Only apply new icon if it's not already the current icon
|
||||
switch icon
|
||||
{
|
||||
case .normal: if currentIcon != nil { UIApplication.shared.setAlternateIconName(nil) } // Default app icon
|
||||
default: if currentIcon != icon.assetName { UIApplication.shared.setAlternateIconName(icon.assetName) } // Alternate app icon
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove alternate icons if feature is disabled
|
||||
if currentIcon != nil { UIApplication.shared.setAlternateIconName(nil) }
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Delta/Extensions/Bundle+AppIconImage.swift
Normal file
34
Delta/Extensions/Bundle+AppIconImage.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Bundle+AppIconImage.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Chris Rittenhouse on 7/20/23.
|
||||
// Copyright © 2023 LitRitt. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension Bundle
|
||||
{
|
||||
static func appIcon(for icon: AppIcon = .normal) -> UIImage? {
|
||||
guard let appIcons = Bundle.main.infoDictionary?["CFBundleIcons"] as? [String: Any] else { return nil }
|
||||
|
||||
switch icon
|
||||
{
|
||||
case .normal:
|
||||
guard let primaryAppIcon = appIcons["CFBundlePrimaryIcon"] as? [String: Any],
|
||||
let appIconFiles = primaryAppIcon["CFBundleIconFiles"] as? [String],
|
||||
let appIcon = appIconFiles.first else { return nil }
|
||||
|
||||
return UIImage(named:appIcon)
|
||||
|
||||
default:
|
||||
guard let alternateAppIcons = appIcons["CFBundleAlternateIcons"] as? [String: Any],
|
||||
let alternateAppIcon = alternateAppIcons[icon.assetName] as? [String: Any],
|
||||
let appIconFiles = alternateAppIcon["CFBundleIconFiles"] as? [String],
|
||||
let appIcon = appIconFiles.first else { return nil }
|
||||
|
||||
return UIImage(named:appIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Resources/Assets.xcassets/Alternate Icons/Contents.json
Normal file
6
Resources/Assets.xcassets/Alternate Icons/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "GBA4iOS.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Inverted.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 141 KiB |
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Pixelated.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 428 KiB |
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Skin.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 460 KiB |
Loading…
Reference in New Issue
Block a user