Merge branch 'ios-notifications' into ep/ios-file-provider

This commit is contained in:
Evgeny Poberezkin 2022-06-02 12:41:07 +01:00
commit e15d4ac6b6
12 changed files with 145 additions and 139 deletions

View File

@ -11,7 +11,7 @@ android {
applicationId "chat.simplex.app"
minSdk 29
targetSdk 32
versionCode 35
versionCode 36
versionName "2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@ -100,6 +100,7 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
userAuthorized.value = true
} else {
userAuthorized.value = false
ModalManager.shared.closeModals()
authenticate(
generalGetString(R.string.auth_unlock),
generalGetString(R.string.auth_log_in_using_credential),
@ -241,7 +242,9 @@ fun MainPage(
// this with LaunchedEffect(userAuthorized.value) fixes bottom sheet visibly collapsing after authentication
var chatsAccessAuthorized by remember { mutableStateOf(false) }
LaunchedEffect(userAuthorized.value) {
delay(500L)
if (chatModel.controller.appPrefs.performLA.get()) {
delay(500L)
}
chatsAccessAuthorized = userAuthorized.value == true
}
var showAdvertiseLAAlert by remember { mutableStateOf(false) }

View File

@ -70,18 +70,21 @@ struct ContentView: View {
if !prefPerformLA {
userAuthorized = true
} else {
userAuthorized = false
authenticate(reason: NSLocalizedString("Unlock", comment: "authentication reason")) { laResult in
switch (laResult) {
case .success:
userAuthorized = true
case .failed:
laFailed = true
AlertManager.shared.showAlert(laFailedAlert())
case .unavailable:
userAuthorized = true
prefPerformLA = false
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
chatModel.showChatInfo = false
DispatchQueue.main.async() {
userAuthorized = false
authenticate(reason: NSLocalizedString("Unlock", comment: "authentication reason")) { laResult in
switch (laResult) {
case .success:
userAuthorized = true
case .failed:
laFailed = true
AlertManager.shared.showAlert(laFailedAlert())
case .unavailable:
userAuthorized = true
prefPerformLA = false
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
}
}
}
}

View File

@ -15,6 +15,7 @@ import SimpleXChat
final class ChatModel: ObservableObject {
@Published var onboardingStage: OnboardingStage?
@Published var currentUser: User?
@Published var showChatInfo: Bool = false // TODO comprehensively close modal views on authentication
// list of chat "previews"
@Published var chats: [Chat] = []
// current chat

View File

@ -11,7 +11,6 @@ import UIKit
import Dispatch
import BackgroundTasks
import SwiftUI
import CallKit
import SimpleXChat
private var chatController: chat_ctrl?
@ -681,7 +680,7 @@ func processReceivedMsg(_ res: ChatResponse) {
}
withCall(contact) { call in
m.callCommand = .end
CallController.shared.reportCallRemoteEnded(call: call)
// CallController.shared.reportCallRemoteEnded(call: call)
}
default:
logger.debug("unsupported event: \(res.responseType)")

View File

@ -94,9 +94,9 @@ struct ActiveCallView: View {
case let .connection(state):
if let callStatus = WebRTCCallStatus.init(rawValue: state.connectionState),
case .connected = callStatus {
if case .outgoing = call.direction {
CallController.shared.reportOutgoingCall(call: call, connectedAt: nil)
}
// if case .outgoing = call.direction {
// CallController.shared.reportOutgoingCall(call: call, connectedAt: nil)
// }
call.callState = .connected
// CallKit doesn't work well with WKWebView
// This is a hack to enable microphone in WKWebView after CallKit takes over it

View File

@ -7,91 +7,92 @@
//
import Foundation
import CallKit
//import CallKit
import AVFoundation
import SimpleXChat
class CallController: NSObject, CXProviderDelegate, ObservableObject {
//class CallController: NSObject, CXProviderDelegate, ObservableObject {
class CallController: NSObject, ObservableObject {
static let useCallKit = false
static let shared = CallController()
private let provider = CXProvider(configuration: CallController.configuration)
private let controller = CXCallController()
// private let provider = CXProvider(configuration: CallController.configuration)
// private let controller = CXCallController()
private let callManager = CallManager()
@Published var activeCallInvitation: CallInvitation?
// PKPushRegistry will be used from notification service extension
// let registry = PKPushRegistry(queue: nil)
static let configuration: CXProviderConfiguration = {
let configuration = CXProviderConfiguration()
configuration.supportsVideo = true
configuration.supportedHandleTypes = [.generic]
configuration.includesCallsInRecents = true // TODO disable or add option
configuration.maximumCallsPerCallGroup = 1
return configuration
}()
// static let configuration: CXProviderConfiguration = {
// let configuration = CXProviderConfiguration()
// configuration.supportsVideo = true
// configuration.supportedHandleTypes = [.generic]
// configuration.includesCallsInRecents = true // TODO disable or add option
// configuration.maximumCallsPerCallGroup = 1
// return configuration
// }()
override init() {
super.init()
self.provider.setDelegate(self, queue: nil)
// self.provider.setDelegate(self, queue: nil)
// self.registry.delegate = self
// self.registry.desiredPushTypes = [.voIP]
}
func providerDidReset(_ provider: CXProvider) {
}
// func providerDidReset(_ provider: CXProvider) {
// }
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
logger.debug("CallController.provider CXStartCallAction")
if callManager.startOutgoingCall(callUUID: action.callUUID) {
action.fulfill()
provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
} else {
action.fail()
}
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
logger.debug("CallController.provider CXAnswerCallAction")
if callManager.answerIncomingCall(callUUID: action.callUUID) {
action.fulfill()
} else {
action.fail()
}
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
logger.debug("CallController.provider CXEndCallAction")
callManager.endCall(callUUID: action.callUUID) { ok in
if ok {
action.fulfill()
} else {
action.fail()
}
}
}
func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
print("timed out", #function)
action.fulfill()
}
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
print("received", #function)
// do {
// try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: .mixWithOthers)
// logger.debug("audioSession category set")
// try audioSession.setActive(true)
// logger.debug("audioSession activated")
// } catch {
// print(error)
// logger.error("failed activating audio session")
// func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
// logger.debug("CallController.provider CXStartCallAction")
// if callManager.startOutgoingCall(callUUID: action.callUUID) {
// action.fulfill()
// provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
// } else {
// action.fail()
// }
}
// }
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
print("received", #function)
}
// func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// logger.debug("CallController.provider CXAnswerCallAction")
// if callManager.answerIncomingCall(callUUID: action.callUUID) {
// action.fulfill()
// } else {
// action.fail()
// }
// }
// func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
// logger.debug("CallController.provider CXEndCallAction")
// callManager.endCall(callUUID: action.callUUID) { ok in
// if ok {
// action.fulfill()
// } else {
// action.fail()
// }
// }
// }
// func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
// print("timed out", #function)
// action.fulfill()
// }
// func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
// print("received", #function)
//// do {
//// try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: .mixWithOthers)
//// logger.debug("audioSession category set")
//// try audioSession.setActive(true)
//// logger.debug("audioSession activated")
//// } catch {
//// print(error)
//// logger.error("failed activating audio session")
//// }
// }
// func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
// print("received", #function)
// }
// func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
//
@ -122,48 +123,49 @@ class CallController: NSObject, CXProviderDelegate, ObservableObject {
func reportNewIncomingCall(invitation: CallInvitation, completion: @escaping (Error?) -> Void) {
logger.debug("CallController.reportNewIncomingCall")
if !UserDefaults.standard.bool(forKey: DEFAULT_EXPERIMENTAL_CALLS) { return }
if CallController.useCallKit, let uuid = invitation.callkitUUID {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: invitation.contact.displayName)
update.hasVideo = invitation.peerMedia == .video
provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
} else {
// if CallController.useCallKit, let uuid = invitation.callkitUUID {
// let update = CXCallUpdate()
// update.remoteHandle = CXHandle(type: .generic, value: invitation.contact.displayName)
// update.hasVideo = invitation.peerMedia == .video
// provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
// } else {
NtfManager.shared.notifyCallInvitation(invitation)
if invitation.callTs.timeIntervalSinceNow >= -180 {
activeCallInvitation = invitation
}
}
// }
}
func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
if CallController.useCallKit, let uuid = call.callkitUUID {
provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
}
}
// func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
// if CallController.useCallKit, let uuid = call.callkitUUID {
// provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
// }
// }
func reportCallRemoteEnded(invitation: CallInvitation) {
if CallController.useCallKit, let uuid = invitation.callkitUUID {
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
} else if invitation.contact.id == activeCallInvitation?.contact.id {
// if CallController.useCallKit, let uuid = invitation.callkitUUID {
// provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
// } else if invitation.contact.id == activeCallInvitation?.contact.id {
activeCallInvitation = nil
}
// }
}
func reportCallRemoteEnded(call: Call) {
if CallController.useCallKit, let uuid = call.callkitUUID {
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
}
}
// func reportCallRemoteEnded(call: Call) {
// if CallController.useCallKit, let uuid = call.callkitUUID {
// provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
// }
// }
func startCall(_ contact: Contact, _ media: CallMediaType) {
logger.debug("CallController.startCall")
let uuid = callManager.newOutgoingCall(contact, media)
if CallController.useCallKit {
let handle = CXHandle(type: .generic, value: contact.displayName)
let action = CXStartCallAction(call: uuid, handle: handle)
action.isVideo = media == .video
requestTransaction(with: action)
} else if callManager.startOutgoingCall(callUUID: uuid) {
// if CallController.useCallKit {
// let handle = CXHandle(type: .generic, value: contact.displayName)
// let action = CXStartCallAction(call: uuid, handle: handle)
// action.isVideo = media == .video
// requestTransaction(with: action)
// } else if callManager.startOutgoingCall(callUUID: uuid) {
if callManager.startOutgoingCall(callUUID: uuid) {
logger.debug("CallController.startCall: call started")
} else {
logger.error("CallController.startCall: no active call")
@ -178,9 +180,9 @@ class CallController: NSObject, CXProviderDelegate, ObservableObject {
}
func endCall(callUUID: UUID) {
if CallController.useCallKit {
requestTransaction(with: CXEndCallAction(call: callUUID))
} else {
// if CallController.useCallKit {
// requestTransaction(with: CXEndCallAction(call: callUUID))
// } else {
callManager.endCall(callUUID: callUUID) { ok in
if ok {
logger.debug("CallController.endCall: call ended")
@ -188,7 +190,7 @@ class CallController: NSObject, CXProviderDelegate, ObservableObject {
logger.error("CallController.endCall: no actove call pr call invitation to end")
}
}
}
// }
}
func endCall(invitation: CallInvitation) {
@ -205,15 +207,15 @@ class CallController: NSObject, CXProviderDelegate, ObservableObject {
callManager.endCall(call: call, completed: completed)
}
private func requestTransaction(with action: CXAction) {
let t = CXTransaction()
t.addAction(action)
controller.request(t) { error in
if let error = error {
logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)")
} else {
logger.debug("CallController.requestTransaction requested transaction successfully")
}
}
}
// private func requestTransaction(with action: CXAction) {
// let t = CXTransaction()
// t.addAction(action)
// controller.request(t) { error in
// if let error = error {
// logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)")
// } else {
// logger.debug("CallController.requestTransaction requested transaction successfully")
// }
// }
// }
}

View File

@ -13,7 +13,6 @@ struct ChatInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var alertManager = AlertManager.shared
@ObservedObject var chat: Chat
@Binding var showChatInfo: Bool
@State var alert: ChatInfoViewAlert? = nil
@State var deletingContact: Contact?
@ -100,7 +99,7 @@ struct ChatInfoView: View {
try await apiDeleteChat(type: .direct, id: contact.apiId)
DispatchQueue.main.async {
chatModel.removeChat(contact.id)
showChatInfo = false
chatModel.showChatInfo = false
}
} catch let error {
logger.error("ChatInfoView.deleteContactAlert apiDeleteChat error: \(error.localizedDescription)")
@ -119,7 +118,7 @@ struct ChatInfoView: View {
Task {
await clearChat(chat)
DispatchQueue.main.async {
showChatInfo = false
chatModel.showChatInfo = false
}
}
},
@ -131,6 +130,6 @@ struct ChatInfoView: View {
struct ChatInfoView_Previews: PreviewProvider {
static var previews: some View {
@State var showChatInfo = true
return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showChatInfo: $showChatInfo)
return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []))
}
}

View File

@ -19,7 +19,6 @@ struct ChatView: View {
@State private var composeState = ComposeState()
@State private var deletingItem: ChatItem? = nil
@FocusState private var keyboardVisible: Bool
@State private var showChatInfo = false
@State private var showDeleteMessage = false
var body: some View {
@ -99,12 +98,12 @@ struct ChatView: View {
}
ToolbarItem(placement: .principal) {
Button {
showChatInfo = true
chatModel.showChatInfo = true
} label: {
ChatInfoToolbar(chat: chat)
}
.sheet(isPresented: $showChatInfo) {
ChatInfoView(chat: chat, showChatInfo: $showChatInfo)
.sheet(isPresented: $chatModel.showChatInfo) {
ChatInfoView(chat: chat)
}
}
ToolbarItem(placement: .navigationBarTrailing) {

View File

@ -1505,7 +1505,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 49;
CURRENT_PROJECT_VERSION = 51;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@ -1548,7 +1548,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 49;
CURRENT_PROJECT_VERSION = 51;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@ -1629,7 +1629,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 49;
CURRENT_PROJECT_VERSION = 51;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@ -1668,7 +1668,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 49;
CURRENT_PROJECT_VERSION = 51;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;

View File

@ -1,5 +1,5 @@
name: simplex-chat
version: 2.1.0
version: 2.2.0
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme

View File

@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
version: 2.1.0
version: 2.2.0
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat