369 lines
19 KiB
Swift
369 lines
19 KiB
Swift
//
|
|
// NotificationCenterDelegate.swift
|
|
//
|
|
// Siskin IM
|
|
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
|
|
//
|
|
// 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. Look for COPYING file in the top folder.
|
|
// If not, see https://www.gnu.org/licenses/.
|
|
//
|
|
|
|
import UIKit
|
|
import Shared
|
|
import WebRTC
|
|
import TigaseSwift
|
|
import UserNotifications
|
|
|
|
class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
|
|
|
|
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
|
|
switch NotificationCategory.from(identifier: notification.request.content.categoryIdentifier) {
|
|
case .MESSAGE:
|
|
let account = notification.request.content.userInfo["account"] as? String;
|
|
let sender = notification.request.content.userInfo["sender"] as? String;
|
|
if (AppDelegate.isChatVisible(account: account, with: sender) && XmppService.instance.applicationState == .active) {
|
|
completionHandler([]);
|
|
} else {
|
|
completionHandler([.alert, .sound]);
|
|
}
|
|
case .MUC_ROOM_INVITATION:
|
|
didReceive(mucInvitation: notification.request.content)
|
|
return
|
|
case .SUBSCRIPTION_REQUEST:
|
|
let account = notification.request.content.userInfo["account"] as? String;
|
|
let sender = notification.request.content.userInfo["sender"] as? String;
|
|
if let account = account, let sender = sender {
|
|
AppDelegate.subscriptionsRequest[account] = sender
|
|
NotificationCenter.default.post(name: Notification.Name("SUBSCRIPTION_REQUEST"), object: nil)
|
|
}
|
|
default:
|
|
completionHandler([.alert, .sound]);
|
|
}
|
|
}
|
|
|
|
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
let content = response.notification.request.content;
|
|
|
|
switch NotificationCategory.from(identifier: response.notification.request.content.categoryIdentifier) {
|
|
case .ERROR:
|
|
didReceive(error: content, withCompletionHandler: completionHandler);
|
|
case .SUBSCRIPTION_REQUEST:
|
|
break
|
|
//didReceive(subscriptionRequest: content, withCompletionHandler: completionHandler);
|
|
case .MUC_ROOM_INVITATION:
|
|
//didReceive(mucInvitation: content, withCompletionHandler: completionHandler);
|
|
break
|
|
case .MESSAGE:
|
|
didReceive(messageResponse: response, withCompletionHandler: completionHandler);
|
|
case .CALL:
|
|
didReceive(call: content, withCompletionHandler: completionHandler);
|
|
case .UNSENT_MESSAGES:
|
|
completionHandler();
|
|
case .UNKNOWN:
|
|
print("received unknown notification category:", response.notification.request.content.categoryIdentifier);
|
|
completionHandler();
|
|
}
|
|
}
|
|
|
|
func didReceive(error content: UNNotificationContent, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
let userInfo = content.userInfo;
|
|
if userInfo["cert-name"] != nil {
|
|
let accountJid = BareJID(userInfo["account"] as! String);
|
|
let alert = CertificateErrorAlert.create(domain: accountJid.domain, certName: userInfo["cert-name"] as! String, certHash: userInfo["cert-hash-sha1"] as! String, issuerName: userInfo["issuer-name"] as? String, issuerHash: userInfo["issuer-hash-sha1"] as? String, onAccept: {
|
|
print("accepted certificate!");
|
|
guard let account = AccountManager.getAccount(for: accountJid) else {
|
|
return;
|
|
}
|
|
var certInfo = account.serverCertificate;
|
|
certInfo?["accepted"] = true as NSObject;
|
|
account.serverCertificate = certInfo;
|
|
account.active = true;
|
|
AccountSettings.LastError(accountJid).set(string: nil);
|
|
AccountManager.save(account: account);
|
|
}, onDeny: nil);
|
|
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
|
|
topController?.present(alert, animated: true, completion: nil);
|
|
}
|
|
if let authError = userInfo["auth-error-type"] {
|
|
let accountJid = BareJID(userInfo["account"] as! String);
|
|
|
|
let alert = UIAlertController(title: "Authentication issue", message: "Authentication for account \(accountJid) failed: \(authError)\nVerify provided account password.", preferredStyle: .alert);
|
|
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil));
|
|
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
|
|
topController?.present(alert, animated: true, completion: nil);
|
|
} else {
|
|
let alert = UIAlertController(title: content.title, message: content.body, preferredStyle: .alert);
|
|
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil));
|
|
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
|
|
topController?.present(alert, animated: true, completion: nil);
|
|
}
|
|
completionHandler();
|
|
}
|
|
|
|
func didReceive(subscriptionRequest content: UNNotificationContent, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
let userInfo = content.userInfo;
|
|
let senderJid = BareJID(userInfo["sender"] as! String);
|
|
let accountJid = BareJID(userInfo["account"] as! String);
|
|
var senderName = userInfo["senderName"] as! String;
|
|
if senderName != senderJid.stringValue {
|
|
senderName = "\(senderName) (\(senderJid.stringValue))";
|
|
}
|
|
let alert = UIAlertController(title: "Subscription request", message: "Received presence subscription request from\n\(senderName)\non account \(accountJid.stringValue)", preferredStyle: .alert);
|
|
alert.addAction(UIAlertAction(title: "Accept", style: .default, handler: {(action) in
|
|
guard let client = XmppService.instance.getClient(forJid: accountJid), let presenceModule: PresenceModule = client.context.modulesManager.getModule(PresenceModule.ID) else {
|
|
return;
|
|
}
|
|
presenceModule.subscribed(by: JID(senderJid));
|
|
let subscription = RosterModule.getRosterStore(client.context.sessionObject).get(for: JID(senderJid))?.subscription ?? RosterItem.Subscription.none;
|
|
guard !subscription.isTo else {
|
|
return;
|
|
}
|
|
if (Settings.AutoSubscribeOnAcceptedSubscriptionRequest.getBool()) {
|
|
presenceModule.subscribe(to: JID(senderJid));
|
|
} else if let telephonyProvider = AccountSettings.telephonyProvider(accountJid).getString(), JID(senderJid).domain.lowercased() == telephonyProvider.lowercased() {
|
|
presenceModule.subscribe(to: JID(senderJid));
|
|
} else {
|
|
let alert2 = UIAlertController(title: "Subscribe to " + senderName, message: "Do you wish to subscribe to \n\(senderName)\non account \(accountJid.stringValue)", preferredStyle: .alert);
|
|
alert2.addAction(UIAlertAction(title: "Accept", style: .default, handler: {(action) in
|
|
presenceModule.subscribe(to: JID(senderJid));
|
|
}));
|
|
alert2.addAction(UIAlertAction(title: "Reject", style: .destructive, handler: nil));
|
|
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
|
|
topController?.present(alert2, animated: true, completion: nil);
|
|
}
|
|
}));
|
|
alert.addAction(UIAlertAction(title: "Reject", style: .destructive, handler: {(action) in
|
|
guard let client = XmppService.instance.getClient(forJid: accountJid), let presenceModule: PresenceModule = client.context.modulesManager.getModule(PresenceModule.ID) else {
|
|
return;
|
|
}
|
|
presenceModule.unsubscribed(by: JID(senderJid));
|
|
}));
|
|
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
|
|
topController?.present(alert, animated: true, completion: nil);
|
|
completionHandler();
|
|
}
|
|
|
|
func didReceive(mucInvitation content: UNNotificationContent) {
|
|
guard let account = BareJID(content.userInfo["account"] as? String), let roomJid: BareJID = BareJID(content.userInfo["roomJid"] as? String) else {
|
|
return;
|
|
}
|
|
|
|
let password = content.userInfo["password"] as? String;
|
|
|
|
|
|
let nick = AccountManager.getAccount(for: account)?.nickname ?? ""
|
|
joinMUC(account: account, channelJid: roomJid, componentType: .muc, nick: nick, password: password)
|
|
|
|
}
|
|
|
|
func didReceive(messageResponse response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
let userInfo = response.notification.request.content.userInfo;
|
|
guard let accountJid = BareJID(userInfo["account"] as? String) else {
|
|
completionHandler();
|
|
return;
|
|
}
|
|
|
|
guard let senderJid = BareJID(userInfo["sender"] as? String) else {
|
|
(UIApplication.shared.delegate as? AppDelegate)?.updateApplicationIconBadgeNumber(completionHandler: completionHandler);
|
|
return;
|
|
}
|
|
|
|
if response.actionIdentifier == UNNotificationDismissActionIdentifier {
|
|
if userInfo[AnyHashable("uid")] as? String != nil {
|
|
DBChatHistoryStore.instance.markAsRead(for: accountJid, with: senderJid, before: response.notification.date, completionHandler: {
|
|
let threadId = response.notification.request.content.threadIdentifier;
|
|
let date = response.notification.date;
|
|
UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
|
|
let toRemove = notifications.filter({ (notification) -> Bool in
|
|
notification.request.content.threadIdentifier == threadId && notification.date < date;
|
|
}).map({ (notification) -> String in
|
|
return notification.request.identifier;
|
|
});
|
|
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: toRemove);
|
|
DispatchQueue.main.async {
|
|
(UIApplication.shared.delegate as? AppDelegate)?.updateApplicationIconBadgeNumber(completionHandler: completionHandler);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
completionHandler();
|
|
}
|
|
} else {
|
|
openChatView(on: accountJid, with: senderJid, completionHandler: completionHandler);
|
|
}
|
|
}
|
|
|
|
private func openChatView(on account: BareJID, with jid: BareJID, completionHandler: @escaping ()->Void) {
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
if #available(iOS 13.0, *), let tmp = topController?.presentedViewController, tmp.modalPresentationStyle != .none {
|
|
tmp.dismiss(animated: true, completion: {
|
|
self.openChatView(on: account, with: jid, completionHandler: completionHandler);
|
|
});
|
|
return;
|
|
} else {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
}
|
|
|
|
if topController != nil {
|
|
guard let chat = DBChatStore.instance.getChat(for: account, with: jid), let controller = viewController(for: chat) else {
|
|
completionHandler();
|
|
return;
|
|
}
|
|
|
|
let navigationController = controller;
|
|
let destination = navigationController.visibleViewController ?? controller;
|
|
|
|
if let baseChatViewController = destination as? BaseChatViewController {
|
|
baseChatViewController.account = account;
|
|
baseChatViewController.jid = jid;
|
|
}
|
|
destination.hidesBottomBarWhenPushed = true;
|
|
|
|
if let chatController = AppDelegate.getChatController(visible: false), let navController = chatController.parent as? UINavigationController {
|
|
navController.pushViewController(destination, animated: true);
|
|
var viewControllers = navController.viewControllers;
|
|
if !viewControllers.isEmpty {
|
|
var i = 0;
|
|
while viewControllers[i] != chatController {
|
|
i = i + 1;
|
|
}
|
|
while (!viewControllers.isEmpty) && i > 0 && viewControllers[i] != destination {
|
|
viewControllers.remove(at: i);
|
|
i = i - 1;
|
|
}
|
|
navController.viewControllers = viewControllers;
|
|
}
|
|
} else {
|
|
topController!.showDetailViewController(controller, sender: self);
|
|
}
|
|
} else {
|
|
print("No top controller!");
|
|
}
|
|
}
|
|
|
|
private func viewController(for item: DBChatProtocol) -> UINavigationController? {
|
|
switch item {
|
|
case is DBRoom:
|
|
return UIStoryboard(name: "Groupchat", bundle: nil).instantiateViewController(withIdentifier: "RoomViewNavigationController") as? UINavigationController;
|
|
case is DBChat:
|
|
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ChatViewNavigationController") as? UINavigationController;
|
|
case is DBChannel:
|
|
return UIStoryboard(name: "MIX", bundle: nil).instantiateViewController(withIdentifier: "ChannelViewNavigationController") as? UINavigationController;
|
|
default:
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
func didReceive(call content: UNNotificationContent, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
#if targetEnvironment(simulator)
|
|
#else
|
|
let userInfo = content.userInfo;
|
|
let senderName = userInfo["senderName"] as! String;
|
|
let senderJid = JID(userInfo["sender"] as! String);
|
|
let accountJid = BareJID(userInfo["account"] as! String);
|
|
let sdp = userInfo["sdpOffer"] as! String;
|
|
let sid = userInfo["sid"] as! String;
|
|
|
|
var topController = UIApplication.shared.keyWindow?.rootViewController;
|
|
while (topController?.presentedViewController != nil) {
|
|
topController = topController?.presentedViewController;
|
|
}
|
|
|
|
if let session = JingleManager.instance.session(for: accountJid, with: senderJid, sid: sid) {
|
|
// can still can be received!
|
|
let alert = UIAlertController(title: "Incoming call", message: "Incoming call from \(senderName)", preferredStyle: .alert);
|
|
switch AVCaptureDevice.authorizationStatus(for: .video) {
|
|
case .denied, .restricted:
|
|
break;
|
|
default:
|
|
break;
|
|
// alert.addAction(UIAlertAction(title: "Video call", style: .default, handler: { action in
|
|
// // accept video
|
|
// VideoCallController.accept(session: session, sdpOffer: sdp, withAudio: true, withVideo: true, sender: topController!);
|
|
// }))
|
|
}
|
|
// alert.addAction(UIAlertAction(title: "Audio call", style: .default, handler: { action in
|
|
// VideoCallController.accept(session: session, sdpOffer: sdp, withAudio: true, withVideo: false, sender: topController!);
|
|
// }));
|
|
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: { action in
|
|
_ = session.decline();
|
|
}));
|
|
topController?.present(alert, animated: true, completion: nil);
|
|
} else {
|
|
// call missed...
|
|
let alert = UIAlertController(title: "Missed call", message: "Missed incoming call from \(senderName)", preferredStyle: .alert);
|
|
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil));
|
|
|
|
topController?.present(alert, animated: true, completion: nil);
|
|
}
|
|
#endif
|
|
completionHandler();
|
|
}
|
|
|
|
public func joinMUC(account: BareJID, channelJid: BareJID, componentType: ChannelsHelper.ComponentType, nick: String, password: String?) {
|
|
|
|
switch componentType {
|
|
case .mix:
|
|
break
|
|
case .muc:
|
|
guard let client = XmppService.instance.getClient(for: account), let mucModule: MucModule = client.modulesManager.getModule(MucModule.ID), let discoModule: DiscoveryModule = client.modulesManager.getModule(DiscoveryModule.ID) else {
|
|
return;
|
|
}
|
|
let room = channelJid;
|
|
_ = mucModule.join(roomName: room.localPart!, mucServer: room.domain, nickname: nick, password: password, onJoined: { room in
|
|
discoModule.getInfo(for: room.jid, node: nil, completionHandler: { result in
|
|
switch result {
|
|
case .success(_, _, let features):
|
|
(room as! DBRoom).supportedFeatures = features;
|
|
case .failure(_, _):
|
|
break;
|
|
}
|
|
});
|
|
room.registerForTigasePushNotification(true, completionHandler: { (result) in
|
|
print("automatically enabled push for:", room.roomJid, "result:", result);
|
|
})
|
|
});
|
|
PEPBookmarksModule.updateOrAdd(for: account, bookmark: Bookmarks.Conference(name: room.localPart!, jid: JID(room), autojoin: true, nick: nick, password: password));
|
|
}
|
|
}
|
|
}
|