forked from mirrors/linphone-iphone
Local Push Notification - Network extension
This commit is contained in:
parent
1fb44f32f0
commit
31c57e5b28
18 changed files with 1126 additions and 76 deletions
|
|
@ -121,7 +121,6 @@
|
|||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
LOGI(@"%@", NSStringFromSelector(_cmd));
|
||||
|
||||
if (!startedInBackground || PhoneMainView.instance.currentView == nil) {
|
||||
startedInBackground = TRUE;
|
||||
// initialize UI
|
||||
|
|
@ -295,7 +294,6 @@
|
|||
[FIRApp configure];
|
||||
#endif
|
||||
|
||||
|
||||
if ([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId]) {
|
||||
if (TARGET_IPHONE_SIMULATOR) {
|
||||
LOGW(@"[VFS] Can not active for simulators.");
|
||||
|
|
|
|||
|
|
@ -505,6 +505,8 @@
|
|||
[self setInteger:linphone_core_get_download_bandwidth(LC) forKey:@"download_bandwidth_preference"];
|
||||
[self setBool:linphone_core_adaptive_rate_control_enabled(LC) forKey:@"adaptive_rate_control_preference"];
|
||||
[self setObject:[lm lpConfigStringForKey:@"dns_server_ip"] forKey:@"dns_server_preference"];
|
||||
[self setCString:[lm lpConfigStringForKey:@"ssids" inSection:@"local_push"].UTF8String forKey:@"local_push_ssids"];
|
||||
|
||||
}
|
||||
|
||||
// tunnel section
|
||||
|
|
@ -1061,6 +1063,13 @@
|
|||
[lm setDnsServer];
|
||||
}
|
||||
|
||||
[lm lpConfigSetString:[self stringForKey:@"local_push_ssids"] forKey:@"ssids" inSection:@"local_push"];
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[LocalPushManager.shared configureLocalPushWithCCoreConfig:lm.configDb];
|
||||
} else {
|
||||
LOGW(@"Local push notifications not available for this ios version (iOS 15 minimum)");
|
||||
}
|
||||
|
||||
|
||||
// tunnel section
|
||||
if (linphone_core_tunnel_available()) {
|
||||
|
|
|
|||
|
|
@ -686,6 +686,11 @@ static void linphone_iphone_global_state_changed(LinphoneCore *lc, LinphoneGloba
|
|||
if (state == LinphoneGlobalOn) {
|
||||
// reload friends
|
||||
[self.fastAddressBook fetchContactsInBackGroundThread];
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[LocalPushManager.shared configureLocalPushWithCCoreConfig:linphone_core_get_config(LC)];
|
||||
} else {
|
||||
LOGW(@"Local push notifications not available for this ios version (iOS 15 minimum)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -715,6 +720,14 @@ static void linphone_iphone_configuring_status_changed(LinphoneCore *lc, Linphon
|
|||
object:self
|
||||
userInfo:dict];
|
||||
});
|
||||
|
||||
if (status == LinphoneConfiguringSuccessful) {
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[LocalPushManager.shared configureLocalPushWithCCoreConfig:linphone_core_get_config(LC)];
|
||||
} else {
|
||||
LOGW(@"Local push notifications not available for this ios version (iOS 15 minimum)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Registration State Functions
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
<outlet property="callQualityButton" destination="SKk-s0-5HE" id="22M-FN-kRs"/>
|
||||
<outlet property="callSecurityButton" destination="27" id="29"/>
|
||||
<outlet property="incallView" destination="0Vp-VF-wmX" id="mLI-RY-bfW"/>
|
||||
<outlet property="localpushIndicator" destination="Lbe-fc-SC9" id="UZc-RF-pLQ"/>
|
||||
<outlet property="outcallView" destination="lfO-I4-PXi" id="04e-SG-ViY"/>
|
||||
<outlet property="registrationState" destination="Mhg-P6-RfU" id="xTR-Af-XBY"/>
|
||||
<outlet property="view" destination="4" id="11"/>
|
||||
|
|
@ -94,6 +95,13 @@
|
|||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="local push active" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Lbe-fc-SC9" userLabel="LocalPushActive">
|
||||
<rect key="frame" x="65" y="27" width="82" height="12"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" type="italicSystem" pointSize="10"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mhg-P6-RfU" userLabel="registrationState" customClass="UIIconButton">
|
||||
<rect key="frame" x="46" y="0.0" width="194" height="42"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
@property(nonatomic, strong) IBOutlet UIButton *callSecurityButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *voicemailButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *callQualityButton;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *localpushIndicator;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIView *incallView;
|
||||
@property(weak, nonatomic) IBOutlet UIView *outcallView;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,16 @@
|
|||
[self accountUpdate:account];
|
||||
[self updateUI:linphone_core_get_calls_nb(LC)];
|
||||
[self updateVoicemail];
|
||||
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[LocalPushManager.shared addActiveCallBackObserverWithAction:^(BOOL active) {
|
||||
_localpushIndicator.hidden = !active;
|
||||
}];
|
||||
} else {
|
||||
_localpushIndicator.hidden = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
|
|
|||
|
|
@ -806,6 +806,12 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
|
|||
[hiddenKeys addObject:@"vfs_enabled"];
|
||||
}
|
||||
|
||||
|
||||
if (@available(iOS 15.0, *)) {} else {
|
||||
[hiddenKeys addObject:@"local_push"];
|
||||
[hiddenKeys addObject:@"local_ssids"];
|
||||
}
|
||||
|
||||
return hiddenKeys;
|
||||
}
|
||||
|
||||
|
|
|
|||
123
Classes/Swift/LocalPushManager.swift
Normal file
123
Classes/Swift/LocalPushManager.swift
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
import linphonesw
|
||||
import Combine
|
||||
|
||||
|
||||
let localPushProviderBundleIdentifier = "org.linphone.phone.localpushprovider"
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
@objc class LocalPushManager : NSObject, NEAppPushDelegate {
|
||||
|
||||
@objc static let shared = LocalPushManager()
|
||||
private var appPushManager: NEAppPushManager?
|
||||
private let isInitialized = MutableLiveData(false)
|
||||
let isActive = MutableLiveData(false)
|
||||
private let dispatchQueue = DispatchQueue(label: "DirectoryViewModel.dispatchQueue")
|
||||
private let pushManagerIsActiveSubject = CurrentValueSubject<Bool, Never>(false)
|
||||
private var pushManagerIsActiveCancellable: AnyCancellable?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private(set) lazy var pushManagerIsActivePublisher = {
|
||||
pushManagerIsActiveSubject
|
||||
.debounce(for: .milliseconds(500), scheduler: dispatchQueue)
|
||||
.eraseToAnyPublisher()
|
||||
}()
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
NEAppPushManager.loadAllFromPreferences { managers, error in
|
||||
if let error = error {
|
||||
Log.e("[LocalPushManager] Failed to load all NEAppPushManager's from preferences: \(error)")
|
||||
self.isInitialized.value = true
|
||||
return
|
||||
}
|
||||
self.appPushManager = managers?.first ?? NEAppPushManager()
|
||||
let appPushManager = self.appPushManager!
|
||||
self.pushManagerIsActiveCancellable = NSObject.KeyValueObservingPublisher(object: appPushManager, keyPath: \.isActive, options: [.initial, .new])
|
||||
.subscribe(self.pushManagerIsActiveSubject)
|
||||
self.pushManagerIsActivePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] isAppPushManagerActive in
|
||||
self?.isActive.value = isAppPushManagerActive
|
||||
}
|
||||
.store(in: &self.cancellables)
|
||||
|
||||
appPushManager.delegate = self
|
||||
|
||||
self.isInitialized.value = true
|
||||
Log.i("[LocalPushManager] NEAppPushManager initialisation : enabled=\(String(describing: appPushManager.isEnabled)) ssids=\(String(describing: appPushManager.matchSSIDs))")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func extensionIsActive() -> Bool {
|
||||
return appPushManager?.isActive == true
|
||||
}
|
||||
|
||||
private func applyConfig(coreConfig:Config) {
|
||||
let appPushManager = self.appPushManager!
|
||||
let ssids = coreConfig.getStringList(section: "local_push", key: "ssids", defaultList: []) // csv
|
||||
let enabled = !ssids.isEmpty
|
||||
appPushManager.isEnabled = enabled
|
||||
appPushManager.matchSSIDs = ssids
|
||||
appPushManager.providerConfiguration = [
|
||||
"coreconfig": coreConfig.dump()
|
||||
]
|
||||
appPushManager.localizedDescription = NSLocalizedString("Local Push Manager", comment: "")
|
||||
appPushManager.providerBundleIdentifier = localPushProviderBundleIdentifier
|
||||
|
||||
if (appPushManager.isEnabled) {
|
||||
self.pushManagerIsActiveCancellable = NSObject.KeyValueObservingPublisher(object: appPushManager, keyPath: \.isActive, options: [.initial, .new])
|
||||
.subscribe(self.pushManagerIsActiveSubject)
|
||||
appPushManager.saveToPreferences { error in
|
||||
if (error != nil) {
|
||||
Log.e("[LocalPushManager] error saving Local Push preferences \(String(describing: error))")
|
||||
} else {
|
||||
Log.i("[LocalPushManager] NEAppPushManager saved : enabled=\(String(describing: appPushManager.isEnabled)) ssids=\(String(describing: appPushManager.matchSSIDs))")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pushManagerIsActiveSubject.send(false)
|
||||
Log.i("[LocalPushManager] NEAppPushManager disabled.")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func configureLocalPush(cCoreConfig:OpaquePointer) {
|
||||
if (self.isInitialized.value != true ) {
|
||||
self.isInitialized.observeOnce { _ in
|
||||
self.applyConfig(coreConfig: Config.getSwiftObject(cObject: cCoreConfig))
|
||||
}
|
||||
} else {
|
||||
applyConfig(coreConfig: Config.getSwiftObject(cObject: cCoreConfig))
|
||||
}
|
||||
|
||||
}
|
||||
func appPushManager(_ manager: NEAppPushManager, didReceiveIncomingCallWithUserInfo userInfo: [AnyHashable : Any] = [:]) {
|
||||
// Call handling
|
||||
}
|
||||
|
||||
@objc func addActiveCallBackObserver (action:@escaping(Bool) -> Void) {
|
||||
isActive.readCurrentAndObserve { active in
|
||||
action(active!)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
13
LocalPushProvider/Info.plist
Normal file
13
LocalPushProvider/Info.plist
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.networkextension.app-push</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).LocalPushProvider</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
14
LocalPushProvider/LocalPushProvider.entitlements
Normal file
14
LocalPushProvider/LocalPushProvider.entitlements
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>app-push-provider</string>
|
||||
</array>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.linphone.phone.msgNotification</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
132
LocalPushProvider/LocalPushProvider.swift
Normal file
132
LocalPushProvider/LocalPushProvider.swift
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import NetworkExtension
|
||||
import UserNotifications
|
||||
import linphonesw
|
||||
import os
|
||||
|
||||
let APP_GROUP_ID = "group.org.linphone.phone.msgNotification"
|
||||
|
||||
class LocalPushProvider: NEAppPushProvider, CoreDelegate {
|
||||
var core: Core? = nil
|
||||
let log = LoggingService.Instance
|
||||
var logDelegate: LinphoneLoggingServiceManager!
|
||||
var coreDelegateStub : CoreDelegateStub? = nil
|
||||
let defaults = UserDefaults.init(suiteName: APP_GROUP_ID)
|
||||
|
||||
func createAndStartCore() {
|
||||
guard let configString = providerConfiguration?["coreconfig"] as? String, let config = Config.newFromBuffer(buffer: configString) else {
|
||||
self.log.error(message: "Unable to get core config through provider configuration")
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure a separate UUID from app is used, use previously generated one or a new one if n/a.
|
||||
if let uuid = defaults?.string(forKey: "misc_uuid") {
|
||||
config.setString(section: "misc", key: "uuid", value: uuid)
|
||||
} else {
|
||||
config.cleanEntry(section: "misc", key: "uuid")
|
||||
}
|
||||
logDelegate = try! LinphoneLoggingServiceManager(config: config, log: log, domain: "LocalPushProvider")
|
||||
core = try! Factory.Instance.createCoreWithConfig(config: config, systemContext: nil)
|
||||
core?.autoIterateEnabled = false // 20ms auto-iterations are too frequent for NE, it gets killed by the OS. (limit of 150 wakeups per second over 300 seconds)
|
||||
core!.addDelegate(delegate: self)
|
||||
let timer = Timer(timeInterval: 0.5, target: self, selector: #selector(iterate), userInfo: nil, repeats: true) // 0.5 second
|
||||
RunLoop.main.add(timer, forMode: .default)
|
||||
try?core?.start()
|
||||
core?.addDelegate(delegate: coreDelegateStub!)
|
||||
log.message(message: "core started")
|
||||
|
||||
// Keep generated UUID to avoid re-creating one every time the NE is starting.
|
||||
if (core!.config!.hasEntry(section: "misc", key: "uuid") != 0) {
|
||||
defaults?.set(core!.config!.getString(section: "misc", key: "uuid", defaultString: ""), forKey: "misc_uuid")
|
||||
self.log.message(message: "storing generated UUID \(String(describing: defaults?.string(forKey: "misc_uuid")))")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func iterate() {
|
||||
core?.iterate()
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
coreDelegateStub = CoreDelegateStub(
|
||||
onMessageReceived: { (core:Core, chatRoom:ChatRoom, message:ChatMessage) -> Void in
|
||||
self.showLocalNotification(message: message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - NEAppPushProvider Life Cycle
|
||||
|
||||
override func start() {
|
||||
createAndStartCore()
|
||||
}
|
||||
|
||||
override func stop(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
core?.stopAsync()
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func handleTimerEvent() {
|
||||
self.log.message(message: "Refreshing registers (handleTimerEvent)")
|
||||
core?.refreshRegisters()
|
||||
}
|
||||
|
||||
// MARK: - Notify User
|
||||
|
||||
let ignoredContentTypes = ["message/imdn+xml","application/im-iscomposing+xml"]
|
||||
|
||||
func showLocalNotification(message: ChatMessage) {
|
||||
|
||||
if (ignoredContentTypes.contains(message.contentType)) {
|
||||
self.log.error(message: "Received unexpected content type.\(message.contentType)")
|
||||
return
|
||||
}
|
||||
|
||||
var messageContent = ""
|
||||
if (message.hasConferenceInvitationContent()) {
|
||||
messageContent = NSLocalizedString("📅 You are invited to a meeting", comment: "")
|
||||
} else {
|
||||
messageContent = message.hasTextContent() ? message.utf8Text : "🗻"
|
||||
}
|
||||
|
||||
let fromAddr = message.chatRoom?.peerAddress?.asStringUriOnly()
|
||||
var displayName = fromAddr?.getDisplayNameFromSipAddress(lc: core!, logger: log, groupId: APP_GROUP_ID)
|
||||
displayName = displayName != nil ? displayName : message.chatRoom?.peerAddress?.displayName
|
||||
displayName = displayName != nil && displayName?.isEmpty != true ? displayName :message.chatRoom?.peerAddress?.username
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = displayName!
|
||||
content.body = messageContent
|
||||
content.sound = .default
|
||||
content.categoryIdentifier = "app_active"
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request) { error in
|
||||
if let error = error {
|
||||
self.log.error(message: "Error submitting local notification: \(error)")
|
||||
return
|
||||
}
|
||||
self.log.message(message: "Local notification posted successfully")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
Podfile
9
Podfile
|
|
@ -64,6 +64,15 @@ target 'CallUITests' do
|
|||
|
||||
end
|
||||
|
||||
target 'LocalPushProvider' do
|
||||
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
|
||||
use_frameworks!
|
||||
|
||||
# Pods for CallUITests
|
||||
all_pods
|
||||
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
system("sed 's/fileprivate let tableView =/public let tableView =/g' ./Pods/DropDown/DropDown/src/DropDown.swift > tmp.swift && mv -f tmp.swift ./Pods/DropDown/DropDown/src/DropDown.swift")
|
||||
# Get the version of linphone-sdk
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -145,3 +145,16 @@ Any Linphone view is actually presented in the UICompositeView, with or without
|
|||
The UICompositeView consists of 3 areas laid out vertically. From top to bottom: StatusBar, Content and TabBar.
|
||||
The TabBar is usually the UIMainBar, which is used as a navigation controller: clicking on each of the buttons will trigger
|
||||
a transition to another "view".
|
||||
|
||||
|
||||
# Local Push Notifications
|
||||
- application local push network extension
|
||||
- requires local push entitlement
|
||||
- enabled either inside settings/network from app UI
|
||||
- by remote provisionning/configuration, section local_push, key ssids (CSV of SSIDs on which to enable)
|
||||
- when extension is running under "connected" appears a "local push active" label
|
||||
- provisionning profile need to be built on Apple console, the entitlement onboarding on profile is asked at last. (automatic provisionning/signing from Xcode will not pick it up - tested on 14.3.1)
|
||||
- If deploying code from Xcode the extension must not be running, otherwise need device will need reboot to run it again. Just disabling wifi prior to deploying code from Xcode does the trick (14.3.1)
|
||||
- use a unique uuid to avoid conflict with app core ([misc]uuid)
|
||||
- use replica of app config
|
||||
|
||||
|
|
|
|||
|
|
@ -198,6 +198,30 @@
|
|||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Key</key>
|
||||
<string>local_push</string>
|
||||
<key>Title</key>
|
||||
<string>Local push notifications</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Key</key>
|
||||
<string>local_push_ssids</string>
|
||||
<key>Title</key>
|
||||
<string>Enable on Wifi SSIDs:</string>
|
||||
<key>Type</key>
|
||||
<string>PSTextFieldSpecifier</string>
|
||||
<key>AutocapitalizationType</key>
|
||||
<string>None</string>
|
||||
<key>AutocorrectionType</key>
|
||||
<string>No</string>
|
||||
<key>DefaultValue</key>
|
||||
<string></string>
|
||||
<key>IASKTextAlignment</key>
|
||||
<string>IASKUITextAlignmentRight</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -30,5 +30,9 @@
|
|||
<array>
|
||||
<string>$(AppIdentifierPrefix)org.linphone.phone</string>
|
||||
</array>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>app-push-provider</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import linphonesw
|
||||
import os
|
||||
#if USE_CRASHLYTICS
|
||||
import Firebase
|
||||
#endif
|
||||
|
|
@ -43,6 +44,14 @@ class LinphoneLoggingServiceManager: LoggingServiceDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
let levelToOSleLogLevel :[Int: OSLogType] =
|
||||
[LogLevel.Debug.rawValue:.debug,
|
||||
LogLevel.Trace.rawValue:.info,
|
||||
LogLevel.Message.rawValue:.info,
|
||||
LogLevel.Warning.rawValue:.error,
|
||||
LogLevel.Error.rawValue:.error,
|
||||
LogLevel.Fatal.rawValue:.fault];
|
||||
|
||||
func onLogMessageWritten(logService: LoggingService, domain: String, level: LogLevel, message: String) {
|
||||
let levelStr: String
|
||||
|
||||
|
|
@ -66,6 +75,42 @@ class LinphoneLoggingServiceManager: LoggingServiceDelegate {
|
|||
#if USE_CRASHLYTICS
|
||||
Crashlytics.crashlytics().log("\(levelStr) [\(domain)] \(message)\n")
|
||||
#endif
|
||||
NSLog("\(levelStr) [\(domain)] \(message)\n")
|
||||
if #available(iOS 10.0, *) {
|
||||
os_log("%{public}@", type: levelToOSleLogLevel[level.rawValue] ?? .info,message)
|
||||
} else {
|
||||
NSLog("\(levelStr) [\(domain)] \(message)\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func getDisplayNameFromSipAddress(lc:Core, logger:LoggingService, groupId:String) -> String? {
|
||||
logger.message(message: "looking for display name for \(self)")
|
||||
|
||||
|
||||
let defaults = UserDefaults.init(suiteName: groupId)
|
||||
let addressBook = defaults?.dictionary(forKey: "addressBook")
|
||||
|
||||
if (addressBook == nil) {
|
||||
logger.message(message: "address book not found in userDefaults")
|
||||
return nil
|
||||
}
|
||||
|
||||
var usePrefix = true;
|
||||
if let account = lc.defaultAccount, let params = account.params {
|
||||
usePrefix = params.useInternationalPrefixForCallsAndChats
|
||||
}
|
||||
|
||||
if let simpleAddr = lc.interpretUrl(url: self, applyInternationalPrefix: usePrefix) {
|
||||
simpleAddr.clean()
|
||||
let nomalSipaddr = simpleAddr.asString()
|
||||
if let displayName = addressBook?[nomalSipaddr] as? String {
|
||||
logger.message(message: "display name for \(self): \(displayName)")
|
||||
return displayName
|
||||
}
|
||||
}
|
||||
|
||||
logger.message(message: "display name for \(self) not found in userDefaults")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
let localUri = message.localAddr?.asStringUriOnly()
|
||||
let peerUri = message.peerAddr?.asStringUriOnly()
|
||||
let from: String
|
||||
if let fromDisplayName = getDisplayNameFromSipAddress(sipAddr: message.fromAddr?.asStringUriOnly()) {
|
||||
if let fromDisplayName = message.fromAddr?.asStringUriOnly().getDisplayNameFromSipAddress(lc: lc!, logger: NotificationService.log, groupId: APP_GROUP_ID) {
|
||||
from = fromDisplayName
|
||||
} else {
|
||||
from = fromAddr!
|
||||
|
|
@ -242,37 +242,4 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
return count
|
||||
}
|
||||
|
||||
func getDisplayNameFromSipAddress(sipAddr: String?) -> String? {
|
||||
if let sipAddr = sipAddr {
|
||||
NotificationService.log.message(message: "looking for display name for \(sipAddr)")
|
||||
|
||||
if (sipAddr == "") { return nil }
|
||||
|
||||
let defaults = UserDefaults.init(suiteName: APP_GROUP_ID)
|
||||
let addressBook = defaults?.dictionary(forKey: "addressBook")
|
||||
|
||||
if (addressBook == nil) {
|
||||
NotificationService.log.message(message: "address book not found in userDefaults")
|
||||
return nil
|
||||
}
|
||||
|
||||
var usePrefix = true;
|
||||
if let account = lc?.defaultAccount, let params = account.params {
|
||||
usePrefix = params.useInternationalPrefixForCallsAndChats
|
||||
}
|
||||
|
||||
if let simpleAddr = lc?.interpretUrl(url: sipAddr, applyInternationalPrefix: usePrefix) {
|
||||
simpleAddr.clean()
|
||||
let nomalSipaddr = simpleAddr.asString()
|
||||
if let displayName = addressBook?[nomalSipaddr] as? String {
|
||||
NotificationService.log.message(message: "display name for \(sipAddr): \(displayName)")
|
||||
return displayName
|
||||
}
|
||||
}
|
||||
|
||||
NotificationService.log.message(message: "display name for \(sipAddr) not found in userDefaults")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue