Skip to content

Commit

Permalink
fix: notification displaying issues on non-English systems (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
Celve authored Dec 14, 2024
1 parent 3022cda commit bc76e36
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 71 deletions.
54 changes: 19 additions & 35 deletions Peninsula/Badges/BadgeMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ class BadgeMonitor {
timer?.fire()
}

func observe(appName: String, onUpdate: @escaping (String?) -> Void) {
let app = ObservedApp(appName: appName, onBadgeUpdate: onUpdate)
observedApps[appName] = app
func observe(bundleId: String, onUpdate: @escaping (String?) -> Void) {
let app = ObservedApp(bundleId: bundleId, onBadgeUpdate: onUpdate)
observedApps[bundleId] = app
app.tryUpdateElement()
}

public func open(appName: String) {
if let cachedTargetAppElement = observedApps[appName]?.appElement {
public func open(bundleId: String) {
if let cachedTargetAppElement = observedApps[bundleId]?.appElement {
AXUIElementPerformAction(cachedTargetAppElement, kAXPressAction as CFString)
}
}
Expand Down Expand Up @@ -121,14 +121,14 @@ class BadgeMonitor {
}

class ObservedApp {
let appName: String
let bundleId: String
var appElement: AXUIElement?
let onBadgeUpdate: (String?) -> Void

init(
appName: String, appElement: AXUIElement? = nil, onBadgeUpdate: @escaping (String?) -> Void
bundleId: String, appElement: AXUIElement? = nil, onBadgeUpdate: @escaping (String?) -> Void
) {
self.appName = appName
self.bundleId = bundleId
self.appElement = appElement
self.onBadgeUpdate = onBadgeUpdate
if self.appElement == nil {
Expand All @@ -138,39 +138,25 @@ class ObservedApp {

func updateBadge() {
guard let appElement = self.appElement else { return }

var statusLabel: AnyObject?
AXUIElementCopyAttributeValue(appElement, "AXStatusLabel" as CFString, &statusLabel)

onBadgeUpdate(statusLabel as? String)
}

func tryUpdateElement() {
guard
let dockProcessId = NSRunningApplication.runningApplications(
withBundleIdentifier: "com.apple.dock"
).last?.processIdentifier
else {
return
}

let dock = AXUIElementCreateApplication(dockProcessId)
guard let dockChildren = getSubElements(root: dock) else {
return
}

// Get badge text by lookup dock elements
for child in dockChildren {
var title: AnyObject?

AXUIElementCopyAttributeValue(child, kAXTitleAttribute as CFString, &title)
if let titleStr = title as? String, titleStr == appName {
self.appElement = child
// the AXTitle is equivalent to localizedName in NSRunningApp, so we have to retrieve from there

var localizedName: String? = nil
for app in Applications.shared.inner {
if app.bundleId == self.bundleId {
localizedName = app.name
}
}
}

private func tryGetAppElement() {
guard let appName = localizedName else { return }
guard
let dockProcessId = NSRunningApplication.runningApplications(
withBundleIdentifier: "com.apple.dock"
Expand All @@ -189,9 +175,7 @@ class ObservedApp {
var title: AnyObject?

AXUIElementCopyAttributeValue(child, kAXTitleAttribute as CFString, &title)
if let titleStr = title as? String,
titleStr == appName
{
if let titleStr = title as? String, titleStr == appName {
self.appElement = child
}
}
Expand Down
2 changes: 1 addition & 1 deletion Peninsula/Notch/NotchViewModel+Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension NotchViewModel {
if true {
if abstractRect.insetBy(dx: inset, dy: inset).contains(mouseLocation) {
if nm.displayedName != "" {
nm.open(name: nm.displayedName)
nm.open(bundleId: nm.displayedName)
} else {
notchOpen(.notification)
}
Expand Down
10 changes: 5 additions & 5 deletions Peninsula/Notch/Notification/NotificationContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ struct NotificationContentView: View {
var body: some View {
HStack(alignment: .top, spacing: nvm.spacing) {
ForEach(Array(nm.items.keys), id: \.self) { key in
AppIcon(name: key, image: nm.items[key]!.icon, vm: nvm)
AppIcon(bundleId: key, image: nm.items[key]!.icon, vm: nvm)
}
}.animation(nvm.normalAnimation, value: nm.items)
}
}

private struct AppIcon: View {
let name: String
let bundleId: String
let image: NSImage
@ObservedObject var nm = NotificationModel.shared
@StateObject var vm: NotchViewModel
Expand Down Expand Up @@ -57,7 +57,7 @@ private struct AppIcon: View {
)
.overlay(
ZStack {
if let badge = nm.items.filter({ $0.value.name == name }).first?.value.badge
if let badge = nm.items.filter({ $0.value.bundleId == bundleId }).first?.value.badge
{
switch badge {
case .count(let count):
Expand Down Expand Up @@ -98,10 +98,10 @@ private struct AppIcon: View {
}
.onTapGesture {
if vm.mode == .delete {
nm.unobserve(name: name)
nm.unobserve(bundleId: bundleId)
} else {
vm.notchClose()
nm.open(name: name)
nm.open(bundleId: bundleId)
}
}
.onChange(of: vm.mode) { newMode in
Expand Down
3 changes: 1 addition & 2 deletions Peninsula/Notch/Notification/NotificationMenubarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ struct NotificationMenubarView: View {
else {
return
}
nm.observe(
name: appName, bundleId: appBundle.bundleIdentifier ?? "")
nm.observe(bundleId: appBundle.bundleIdentifier ?? "")
} else {
vm.isExternal = false
}
Expand Down
45 changes: 17 additions & 28 deletions Peninsula/Notch/Notification/NotificationModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,18 @@ enum NotificationBadge: Equatable {
}

class NotificationItem: Equatable {
var name: String
var bundleId: String
var badge: NotificationInfo
var icon: NSImage

init(name: String, bundleId: String, badge: NotificationInfo, icon: NSImage) {
self.name = name
init(bundleId: String, badge: NotificationInfo, icon: NSImage) {
self.bundleId = bundleId
self.badge = badge
self.icon = icon
}

static func == (lhs: NotificationItem, rhs: NotificationItem) -> Bool {
return lhs.name == rhs.name && lhs.bundleId == rhs.bundleId
return lhs.bundleId == rhs.bundleId
}
}

Expand All @@ -87,22 +85,13 @@ class NotificationModel: ObservableObject {
init() {
let monitoredAppIds = UserDefaults.standard.stringArray(forKey: "monitoredAppIds") ?? []
for bundleId in monitoredAppIds {
var appName = bundleId
if let appFullPath = NSWorkspace.shared.urlForApplication(
withBundleIdentifier: bundleId)?.absoluteURL.path,
let targetBundle = Bundle(path: appFullPath),
let name = targetBundle.object(forInfoDictionaryKey: kCFBundleNameKey as String)
as? String
{
appName = name
}
self.observe(name: appName, bundleId: bundleId)
self.observe(bundleId: bundleId)
}
}

private func updateBadge(name: String, text: String?) {
private func updateBadge(bundleId: String, text: String?) {
let badge = NotificationInfo.fromString(str: text)
guard let item = self.items[name] else { return }
guard let item = self.items[bundleId] else { return }

if item.badge == badge {
return
Expand All @@ -119,7 +108,7 @@ class NotificationModel: ObservableObject {
if !self.occupied {
self.displayedBadge = .icon(item.icon)
self.displayedNum = self.total
self.displayedName = name
self.displayedName = bundleId

let version = self.version
self.displayedVersion = version
Expand Down Expand Up @@ -151,9 +140,9 @@ class NotificationModel: ObservableObject {
}
}

func open(name: String) {
func open(bundleId: String) {
for app in self.apps.inner {
if app.name == name {
if app.bundleId == bundleId {
if let window = app.focusedWindow {
window.focus()
return
Expand All @@ -163,11 +152,11 @@ class NotificationModel: ObservableObject {
}
}
}
self.monitor.open(appName: name)
self.monitor.open(bundleId: bundleId)
}

func observe(name: String, bundleId: String) {
if self.items.keys.contains(name) {
func observe(bundleId: String) {
if self.items.keys.contains(bundleId) {
return
}

Expand All @@ -194,17 +183,17 @@ class NotificationModel: ObservableObject {
} else {
icon = NSImage(systemSymbolName: "app.badge", accessibilityDescription: nil)!
}
self.items[name] = NotificationItem(
name: name, bundleId: bundleId, badge: NotificationInfo.null, icon: icon)
self.items[bundleId] = NotificationItem(
bundleId: bundleId, badge: NotificationInfo.null, icon: icon)
monitor.observe(
appName: name,
bundleId: bundleId,
onUpdate: { text in
self.updateBadge(name: name, text: text)
self.updateBadge(bundleId: bundleId, text: text)
})
}

func unobserve(name: String) {
if let item = self.items.removeValue(forKey: name),
func unobserve(bundleId: String) {
if let item = self.items.removeValue(forKey: bundleId),
let monitoredAppIds = UserDefaults.standard.stringArray(forKey: "monitoredAppIds")
{
let bundleId = item.bundleId
Expand Down
2 changes: 2 additions & 0 deletions Peninsula/Switch/Model/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Application {
var isHidden: Bool = false
var label: String? = nil
var name: String? = nil
var bundleId: String? = nil

static let notifications = [
kAXApplicationActivatedNotification,
Expand All @@ -28,6 +29,7 @@ class Application {
self.runningApplication = runningApplication
self.icon = runningApplication.icon
self.name = runningApplication.localizedName
self.bundleId = runningApplication.bundleIdentifier
self.addObserver()
manuallyUpdateWindows()
}
Expand Down

0 comments on commit bc76e36

Please sign in to comment.