diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle index 410b5d966..d0b5c8a8d 100644 --- a/apps/android/app/build.gradle +++ b/apps/android/app/build.gradle @@ -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" diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index 829839ec7..a724d0fda 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -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) } diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index f9ba1bbaa..bb5ab76aa 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -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()) + } } } } diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 0696b0b6e..f2a30ffd0 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -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 diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 530c5735c..e29a92a46 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -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)") diff --git a/apps/ios/Shared/Views/Call/ActiveCallView.swift b/apps/ios/Shared/Views/Call/ActiveCallView.swift index ad90ecf84..7e914a866 100644 --- a/apps/ios/Shared/Views/Call/ActiveCallView.swift +++ b/apps/ios/Shared/Views/Call/ActiveCallView.swift @@ -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 diff --git a/apps/ios/Shared/Views/Call/CallController.swift b/apps/ios/Shared/Views/Call/CallController.swift index 57af8acca..df103c02e 100644 --- a/apps/ios/Shared/Views/Call/CallController.swift +++ b/apps/ios/Shared/Views/Call/CallController.swift @@ -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") +// } +// } +// } } diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index fc82d543b..7bd8f6283 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -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: [])) } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 801177e47..54cc67593 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -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) { diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index a2c6b0e6d..cc6c1b409 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -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; diff --git a/package.yaml b/package.yaml index 709e8fa76..9e097678f 100644 --- a/package.yaml +++ b/package.yaml @@ -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 diff --git a/simplex-chat.cabal b/simplex-chat.cabal index fd4b1c0a2..588beb8ca 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -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