diff --git a/LiveContainerSwiftUI/LCAppBanner.swift b/LiveContainerSwiftUI/LCAppBanner.swift index 5a84af3..eac23ed 100644 --- a/LiveContainerSwiftUI/LCAppBanner.swift +++ b/LiveContainerSwiftUI/LCAppBanner.swift @@ -8,6 +8,7 @@ import Foundation import SwiftUI import UniformTypeIdentifiers +import UIKit protocol LCAppBannerDelegate { func removeApp(app: LCAppModel) @@ -33,6 +34,7 @@ struct LCAppBanner : View { @State private var errorShow = false @State private var errorInfo = "" + @AppStorage("dynamicColors") var dynamicColors = true @EnvironmentObject private var sharedModel : SharedModel @@ -44,6 +46,7 @@ struct LCAppBanner : View { _model = ObservedObject(wrappedValue: appModel) } + @State private var mainHueColor: CGFloat? = nil var body: some View { @@ -81,8 +84,8 @@ struct LCAppBanner : View { } } - Text("\(appInfo.version()) - \(appInfo.bundleIdentifier())").font(.system(size: 12)).foregroundColor(Color("FontColor")) - Text(LocalizedStringKey(model.uiDataFolder == nil ? "lc.appBanner.noDataFolder".loc : model.uiDataFolder!)).font(.system(size: 8)).foregroundColor(Color("FontColor")) + Text("\(appInfo.version()) - \(appInfo.bundleIdentifier())").font(.system(size: 12)).foregroundColor(dynamicColors ? extractMainHueColor(from: appInfo.icon()) : Color("FontColor")) + Text(LocalizedStringKey(model.uiDataFolder == nil ? "lc.appBanner.noDataFolder".loc : model.uiDataFolder!)).font(.system(size: 8)).foregroundColor(dynamicColors ? extractMainHueColor(from: appInfo.icon()) : Color("FontColor")) }) } Spacer() @@ -102,14 +105,14 @@ struct LCAppBanner : View { .fixedSize() .background(GeometryReader { g in if !model.isSigningInProgress { - Capsule().fill(Color("FontColor")) + Capsule().fill(dynamicColors ? extractMainHueColor(from: appInfo.icon()) : Color("FontColor")) } else { let w = g.size.width let h = g.size.height Capsule() - .fill(Color("FontColor")).opacity(0.2) + .fill(dynamicColors ? extractMainHueColor(from: appInfo.icon()) : Color("FontColor")).opacity(0.2) Circle() - .fill(Color("FontColor")) + .fill(dynamicColors ? extractMainHueColor(from: appInfo.icon()) : Color("FontColor")) .frame(width: w * 2, height: w * 2) .offset(x: (model.signProgress - 2) * w, y: h/2-w) } @@ -121,7 +124,7 @@ struct LCAppBanner : View { } .padding() .frame(height: 88) - .background(RoundedRectangle(cornerSize: CGSize(width:22, height: 22)).fill(Color("AppBannerBG"))) + .background(RoundedRectangle(cornerSize: CGSize(width:22, height: 22)).fill(dynamicColors ? extractMainHueColor(from: appInfo.icon()).opacity(0.5) : Color("AppBannerBG"))) .onAppear() { handleOnAppear() } @@ -337,6 +340,47 @@ struct LCAppBanner : View { self.saveIconExporterShow = true } + func extractMainHueColor(from image: UIImage) -> Color { + guard let cgImage = image.cgImage else { return Color.clear } + + let width = 1 + let height = 1 + let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue + let colorSpace = CGColorSpaceCreateDeviceRGB() + var pixelData = [UInt8](repeating: 0, count: 4) + + guard let context = CGContext(data: &pixelData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo) else { + return Color.clear + } + + context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) + + let red = CGFloat(pixelData[0]) / 255.0 + let green = CGFloat(pixelData[1]) / 255.0 + let blue = CGFloat(pixelData[2]) / 255.0 + + let averageColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0) + + var hue: CGFloat = 0 + var saturation: CGFloat = 0 + var brightness: CGFloat = 0 + var alpha: CGFloat = 0 + averageColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) + + if brightness < 0.1 && saturation < 0.1 { + return Color.red + } + + if brightness < 0.3 { + brightness = 0.3 + } + + return Color(hue: hue, saturation: saturation, brightness: brightness) + } + + + + } @@ -372,4 +416,6 @@ struct LCAppSkeletonBanner: View { .frame(height: 88) .background(RoundedRectangle(cornerRadius: 22).fill(Color.gray.opacity(0.1))) } + } + diff --git a/LiveContainerSwiftUI/LCSettingsView.swift b/LiveContainerSwiftUI/LCSettingsView.swift index 79c5053..7bd645a 100644 --- a/LiveContainerSwiftUI/LCSettingsView.swift +++ b/LiveContainerSwiftUI/LCSettingsView.swift @@ -32,6 +32,7 @@ struct LCSettingsView: View { @State var silentSwitchApp = false @State var injectToLCItelf = false @State var strictHiding = false + @AppStorage("dynamicColors") var dynamicColors = true @State var sideJITServerAddress : String @State var deviceUDID: String @@ -152,6 +153,15 @@ struct LCSettingsView: View { Text("lc.settings.JitDesc".loc) } + Section{ + Toggle(isOn: $dynamicColors) { + Text("lc.settings.dynamicColors".loc) + } + } header: { + Text("lc.settings.interface".loc) + } footer: { + Text("lc.settings.dynamicColors.desc".loc) + } Section{ Toggle(isOn: $frameShortIcon) { Text("lc.settings.FrameIcon".loc) diff --git a/LiveContainerSwiftUI/Localizable.xcstrings b/LiveContainerSwiftUI/Localizable.xcstrings index c786f4f..263f79c 100644 --- a/LiveContainerSwiftUI/Localizable.xcstrings +++ b/LiveContainerSwiftUI/Localizable.xcstrings @@ -137,6 +137,23 @@ } } }, + "lc.appBanner.locked" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "LOCKED" + } + }, + "zh_CN" : { + "stringUnit" : { + "state" : "translated", + "value" : "锁定" + } + } + } + }, "lc.appBanner.noDataFolder" : { "extractionState" : "manual", "localizations" : { @@ -222,23 +239,6 @@ } } }, - "lc.appBanner.locked" : { - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "LOCKED" - } - }, - "zh_CN" : { - "stringUnit" : { - "state" : "translated", - "value" : "锁定" - } - } - } - }, "lc.appBanner.uninstall" : { "extractionState" : "manual", "localizations" : { @@ -807,87 +807,87 @@ } } }, - "lc.appSettings.lockApp" : { + "lc.appSettings.hideApp" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Lock App" + "value" : "Hide App" } }, "zh_CN" : { "stringUnit" : { "state" : "translated", - "value" : "锁定App" + "value" : "隐藏App" } } } }, - "lc.appSettings.hideApp" : { + "lc.appSettings.hideAppDesc" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Hide App" + "value" : "To completely hide apps, enable Strict Hiding mode in settings." } }, "zh_CN" : { "stringUnit" : { "state" : "translated", - "value" : "隐藏App" + "value" : "要完全隐藏App,请在设置中启用严格隐藏模式。" } } } }, - "lc.appSettings.hideAppDesc" : { + "lc.appSettings.launchWithJit" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "To completely hide apps, enable Strict Hiding mode in settings." + "value" : "Launch with JIT" } }, "zh_CN" : { "stringUnit" : { "state" : "translated", - "value" : "要完全隐藏App,请在设置中启用严格隐藏模式。" + "value" : "带JIT启动" } } } }, - "lc.appSettings.launchWithJit" : { + "lc.appSettings.launchWithJitDesc" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Launch with JIT" + "value" : "LiveContainer will try to acquire JIT permission before launching the app." } }, "zh_CN" : { "stringUnit" : { "state" : "translated", - "value" : "带JIT启动" + "value" : "在启动App前LiveContainer会尝试获取JIT权限。" } } } }, - "lc.appSettings.launchWithJitDesc" : { + "lc.appSettings.lockApp" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "LiveContainer will try to acquire JIT permission before launching the app." + "value" : "Lock App" } }, "zh_CN" : { "stringUnit" : { "state" : "translated", - "value" : "在启动App前LiveContainer会尝试获取JIT权限。" + "value" : "锁定App" } } } @@ -1647,6 +1647,28 @@ } } }, + "lc.settings.dynamicColors" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dynamic colors" + } + } + } + }, + "lc.settings.dynamicColors.desc" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable dynamic colors for app boxes" + } + } + } + }, "lc.settings.error.altstoreNotPatched" : { "extractionState" : "manual", "localizations" : { @@ -1766,6 +1788,17 @@ } } }, + "lc.settings.interface" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "interface" + } + } + } + }, "lc.settings.JitAddress" : { "extractionState" : "manual", "localizations" : {