/*
Copyright (C) 2025 Rohith Namboothiri
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "ios_live_activity.h"
#include
#if defined(Q_OS_IOS)
#import
#import
#import
static inline Class liveActivityManagerClass(void)
{
// Swift class runtime name can be "." OR explicit @objc name.
Class cls = NSClassFromString(@"LiveActivityManager"); // @objc(LiveActivityManager)
if (!cls) cls = NSClassFromString(@"DroidStar.LiveActivityManager");
return cls;
}
bool ios_live_activity_is_available(void)
{
if (@available(iOS 16.1, *)) {
Class cls = liveActivityManagerClass();
if (!cls) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager class not found. "
@"Make sure LiveActivityManager.swift is part of the MAIN app target.");
return false;
}
SEL sel = NSSelectorFromString(@"isDynamicIslandAvailable");
if (![cls respondsToSelector:sel]) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager missing selector isDynamicIslandAvailable");
return false;
}
typedef BOOL (*MsgSendBool)(id, SEL);
BOOL enabled = ((MsgSendBool)objc_msgSend)((id)cls, sel);
if (!enabled) {
NSLog(@"[DroidStar][LiveActivity] ActivityAuthorizationInfo().areActivitiesEnabled == false "
@"(Settings → DroidStar → Live Activities might be OFF).");
}
return enabled;
}
return false;
}
static inline NSString *ns(const char *s)
{
if (!s) return @"";
return [NSString stringWithUTF8String:s] ?: @"";
}
void ios_live_activity_start_or_update(const char *mode,
const char *callsign,
const char *handle,
const char *country,
const char *tgid)
{
if (@available(iOS 16.1, *)) {
Class cls = liveActivityManagerClass();
if (!cls) {
NSLog(@"[DroidStar][LiveActivity] start/update called but LiveActivityManager class not found");
return;
}
// Get singleton: +shared
SEL sharedSel = NSSelectorFromString(@"shared");
if (![cls respondsToSelector:sharedSel]) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager missing selector +shared");
return;
}
typedef id (*MsgSendId)(id, SEL);
id mgr = ((MsgSendId)objc_msgSend)((id)cls, sharedSel);
if (!mgr) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager.shared returned nil");
return;
}
SEL updSel = NSSelectorFromString(@"startOrUpdateLiveActivityWithMode:callsign:handle:country:tgid:");
if (![mgr respondsToSelector:updSel]) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager missing selector startOrUpdateLiveActivityWithMode:callsign:handle:country:tgid:");
return;
}
typedef void (*MsgSendUpdate)(id, SEL, NSString*, NSString*, NSString*, NSString*, NSString*);
((MsgSendUpdate)objc_msgSend)(mgr,
updSel,
ns(mode),
ns(callsign),
ns(handle),
ns(country),
ns(tgid));
}
}
void ios_live_activity_end(void)
{
if (@available(iOS 16.1, *)) {
Class cls = liveActivityManagerClass();
if (!cls) {
NSLog(@"[DroidStar][LiveActivity] end called but LiveActivityManager class not found");
return;
}
SEL sharedSel = NSSelectorFromString(@"shared");
if (![cls respondsToSelector:sharedSel]) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager missing selector +shared");
return;
}
typedef id (*MsgSendId)(id, SEL);
id mgr = ((MsgSendId)objc_msgSend)((id)cls, sharedSel);
if (!mgr) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager.shared returned nil");
return;
}
SEL endSel = NSSelectorFromString(@"endLiveActivity");
if (![mgr respondsToSelector:endSel]) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager missing selector endLiveActivity");
return;
}
typedef void (*MsgSendVoid)(id, SEL);
((MsgSendVoid)objc_msgSend)(mgr, endSel);
}
}
void ios_live_activity_end_all(void)
{
if (@available(iOS 16.1, *)) {
Class cls = liveActivityManagerClass();
if (!cls) {
NSLog(@"[DroidStar][LiveActivity] endAll called but LiveActivityManager class not found");
return;
}
SEL sharedSel = NSSelectorFromString(@"shared");
if (![cls respondsToSelector:sharedSel]) return;
typedef id (*MsgSendId)(id, SEL);
id mgr = ((MsgSendId)objc_msgSend)((id)cls, sharedSel);
if (!mgr) return;
SEL endAllSel = NSSelectorFromString(@"endAllActivities");
if (![mgr respondsToSelector:endAllSel]) {
NSLog(@"[DroidStar][LiveActivity] LiveActivityManager missing selector endAllActivities");
return;
}
typedef void (*MsgSendVoid)(id, SEL);
((MsgSendVoid)objc_msgSend)(mgr, endAllSel);
}
}
#else
bool ios_live_activity_is_available(void) { return false; }
void ios_live_activity_start_or_update(const char *, const char *, const char *, const char *, const char *) {}
void ios_live_activity_end(void) {}
void ios_live_activity_end_all(void) {}
#endif