diff --git a/mobile-sdk/build.gradle b/mobile-sdk/build.gradle index 4581fd9..47c6dfa 100644 --- a/mobile-sdk/build.gradle +++ b/mobile-sdk/build.gradle @@ -122,7 +122,7 @@ afterEvaluate { groupId = "com.webitel" artifactId = "mobile-sdk" - version = "0.13.1" + version = "0.14.1" } } } diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/auth/AuthRepository.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/auth/AuthRepository.kt index 27b82e8..75cf434 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/auth/AuthRepository.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/auth/AuthRepository.kt @@ -119,8 +119,8 @@ internal class AuthRepository( } - fun setAccessTokenHeader(token: String) { - authApi.setAccessTokenHeader(token) + fun setAccessTokenHeader(token: String, callback: CallbackListener?) { + authApi.setAccessTokenHeader(token, callback) } diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/AuthApi.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/AuthApi.kt index 69c3017..5452920 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/AuthApi.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/AuthApi.kt @@ -15,6 +15,6 @@ internal interface AuthApi: BaseApi { fun logout(callback: CallbackListener) fun inspect(callback: CallbackListener) fun setSession(auth: String, callback: CallbackListener) - fun setAccessTokenHeader(auth: String) + fun setAccessTokenHeader(auth: String, callback: CallbackListener?) fun registerFcm(token: String, callback: CallbackListener) } \ No newline at end of file diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/ClientGrpc.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/ClientGrpc.kt index 2925ac1..a4089ba 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/ClientGrpc.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/ClientGrpc.kt @@ -30,11 +30,11 @@ import webitel.chat.MessageOuterClass import webitel.portal.Account.Identity import webitel.portal.Auth import webitel.portal.Auth.TokenRequest -import webitel.portal.Connect import webitel.portal.Connect.Echo import webitel.portal.Connect.Request import webitel.portal.Connect.Response import webitel.portal.Connect.Update +import webitel.portal.Connect.UpdateDisconnect import webitel.portal.CustomerGrpc import webitel.portal.CustomerOuterClass import webitel.portal.CustomerOuterClass.RegisterDeviceResponse @@ -68,7 +68,7 @@ internal class ClientGrpc( Handler(thread.looper) } - private var connectState = ConnectState.DISCONNECTED + private var connectState: ConnectState = ConnectState.None init { @@ -149,32 +149,42 @@ internal class ClientGrpc( } - override fun setAccessTokenHeader(auth: String) { + override fun setAccessTokenHeader(auth: String, callback: CallbackListener?) { make { setAccessToken(auth) - updateConnect() - } - } - - - private fun updateConnect() { - if (requestObserver != null) { - try { - resetBackoff() - val stub = CustomerGrpc.newStub(channel.channel) - val m = CustomerOuterClass.InspectRequest - .newBuilder() - .build() - - stub.inspect(m, object : StreamObserver { - override fun onNext(value: Auth.AccessToken?) {} - override fun onError(t: Throwable) { - logger.error("setAccessTokenHeader", "${t.message}") + if (requestObserver != null) { + try { + resetBackoff() + val stub = CustomerGrpc.newStub(channel.channel) + val m = CustomerOuterClass.InspectRequest + .newBuilder() + .build() + + stub.inspect(m, object : StreamObserver { + override fun onNext(value: Auth.AccessToken?) { + logger.debug("token", "token updated in headers and stream") + callback?.onSuccess(Unit) + } + override fun onError(t: Throwable) { + logger.error("token", "token not updated in stream. ${t.message}") + callback?.let { + val err = parseError(t) + it.onError(err) + } + } + override fun onCompleted() {} + }) + } catch (e: Exception) { + logger.error("token", "token not updated in stream. ${e.message}") + callback?.let { + val err = parseError(e) + it.onError(err) } - override fun onCompleted() {} - }) - } catch (e: Exception) { - logger.error("setAccessTokenHeader", "${e.message}") + } + + }else { + logger.debug("token", "token updated in headers") + callback?.onSuccess(Unit) } } } @@ -257,8 +267,8 @@ internal class ClientGrpc( .newBuilder() .build() - stub.logout(m, object : StreamObserver { - override fun onNext(value: Connect.UpdateSignedOut?) { + stub.logout(m, object : StreamObserver { + override fun onNext(value: UpdateDisconnect?) { callback.onSuccess(Unit) } @@ -490,18 +500,11 @@ internal class ClientGrpc( } - @Synchronized private fun stopStream() { - make { - val s = requestObserver - requestObserver = null - try { - s?.onCompleted() - } catch (_: Exception) { - } - timer?.cancel() - timer = null - } + requestObserver?.onCompleted() + requestObserver = null + timer?.cancel() + timer = null } @@ -658,6 +661,7 @@ internal class ClientGrpc( private fun openBiDirectionalConnect() { try { resetBackoff() + logger.debug("connect", "create new connection...") val stub = CustomerGrpc.newStub(channel.channel) requestObserver?.onCompleted() requestObserver = stub.connect(object : StreamObserver { @@ -719,6 +723,10 @@ internal class ClientGrpc( message?.let { chatListener?.onNewMessage(it) } + + } else if (update.data.`is`(UpdateDisconnect::class.java)) { + logger.debug("UpdateDisconnect", "Server closes connection...") + } else { logger.debug("notImplementedEvent", update.toString()) } @@ -778,26 +786,31 @@ internal class ClientGrpc( override fun onStart(methodName: String) { if (methodName != CustomerGrpc.getConnectMethod().bareMethodName) return - logger.debug("onStateChanged", "from = $connectState, to = ${ConnectState.CONNECTING}") - connectionListeners.onStateChanged(from = connectState, to = ConnectState.CONNECTING) - connectState = ConnectState.CONNECTING + logger.debug("onStateChanged", "from = $connectState, to = ${ConnectState.Connecting}") + connectionListeners.onStateChanged(from = connectState, to = ConnectState.Connecting) + connectState = ConnectState.Connecting } override fun onReady(methodName: String) { if (methodName != CustomerGrpc.getConnectMethod().bareMethodName) return - logger.debug("onStateChanged", "from = $connectState, to = ${ConnectState.READY}") - connectionListeners.onStateChanged(from = connectState, to = ConnectState.READY) - connectState = ConnectState.READY + logger.debug("onStateChanged", "from = $connectState, to = ${ConnectState.Ready}") + connectionListeners.onStateChanged(from = connectState, to = ConnectState.Ready) + connectState = ConnectState.Ready } override fun onClose(methodName: String, status: Status?, trailers: Metadata?) { if (methodName != CustomerGrpc.getConnectMethod().bareMethodName) return - ConnectState.DISCONNECTED.message = status.toString() - logger.debug("onStateChanged", "from = $connectState, to = ${ConnectState.DISCONNECTED}") - connectionListeners.onStateChanged(from = connectState, to = ConnectState.DISCONNECTED) - connectState = ConnectState.DISCONNECTED + val statusCode = Code.forNumber(status?.code?.value() ?: 2) + val reason = Error( + message = status?.description ?: status.toString(), + code = statusCode + ) + val state = ConnectState.Disconnected(reason) + logger.debug("onStateChanged", "from = ${connectState}, to = $state") + connectionListeners.onStateChanged(from = connectState, to = state) + connectState = state } } diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/GrpcInterceptor.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/GrpcInterceptor.kt index c1faa8c..b12a8d2 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/GrpcInterceptor.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/grps/GrpcInterceptor.kt @@ -1,5 +1,6 @@ package com.webitel.mobile_sdk.data.grps +import com.webitel.mobile_sdk.data.portal.WebitelPortalClient import io.grpc.CallOptions import io.grpc.Channel import io.grpc.ClientCall @@ -32,11 +33,23 @@ internal class GrpcInterceptor( return object : ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { override fun sendMessage(message: ReqT) { super.sendMessage(message) + WebitelPortalClient.logger.debug( + "intercept", + "Method: ${method?.bareMethodName ?: "undefined bareMethodName"}, Message: ${message.toString()}" + ) } + + override fun start(responseListener: Listener?, headers: Metadata?) { setupHeaders(headers, method?.bareMethodName ?: "undefined bareMethodName") listener?.onStart(method?.bareMethodName.toString()) + + WebitelPortalClient.logger.debug( + "connect.start", + "${method?.bareMethodName.toString()} - ${headers.toString()}" + ) + super.start(object : ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { override fun onMessage(message: RespT) { super.onMessage(message) @@ -48,8 +61,8 @@ internal class GrpcInterceptor( } override fun onClose(status: Status?, trailers: Metadata?) { - listener?.onClose(method?.bareMethodName.toString(), status, trailers) super.onClose(status, trailers) + listener?.onClose(method?.bareMethodName.toString(), status, trailers) } }, headers) } diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WLogger.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WLogger.kt index 949e81e..7c6913f 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WLogger.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WLogger.kt @@ -3,8 +3,8 @@ package com.webitel.mobile_sdk.data.portal import android.util.Log import com.webitel.mobile_sdk.domain.LogLevel -internal class WLogger(private val level: LogLevel) { - +internal class WLogger { + var level: LogLevel = LogLevel.ERROR fun info(tag: String, message: String) { if (level <= LogLevel.INFO) { Log.i(tag, message) diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WebitelPortalClient.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WebitelPortalClient.kt index e6eb6c1..0ee6434 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WebitelPortalClient.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/data/portal/WebitelPortalClient.kt @@ -39,7 +39,10 @@ internal class WebitelPortalClient( private val voice: VoiceClient private var userSession: UserSession? = null - private val logger: WLogger + + companion object { + val logger: WLogger = WLogger() + } init { @@ -47,7 +50,7 @@ internal class WebitelPortalClient( userSession } - logger = WLogger(client.logLevel) + logger.level = client.logLevel grpc = ClientGrpc( getChannelConfig(), @@ -163,7 +166,12 @@ internal class WebitelPortalClient( override fun setAccessTokenHeader(token: String) { - authRepository.setAccessTokenHeader(token) + authRepository.setAccessTokenHeader(token, null) + } + + + override fun setAccessTokenHeader(token: String, callback: CallbackListener) { + authRepository.setAccessTokenHeader(token, callback) } diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/ConnectState.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/ConnectState.kt index 29b28a2..f5182da 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/ConnectState.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/ConnectState.kt @@ -1,4 +1,24 @@ package com.webitel.mobile_sdk.domain -enum class ConnectState(var message: String = "") { CONNECTING, READY, DISCONNECTED } \ No newline at end of file +sealed class ConnectState { + /** + * The initial state, indicating no connection attempt has been made yet. + */ + object None : ConnectState() + + /** + * The state when a connection attempt is currently in progress. + */ + object Connecting : ConnectState() + + /** + * The state indicating that the connection is successfully established and ready for use. + */ + object Ready : ConnectState() + + /** + * The state indicating that the connection has been disconnected, with an associated error describing the reason for disconnection. + */ + data class Disconnected(val reason: Error) : ConnectState() +} \ No newline at end of file diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/Dialog.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/Dialog.kt index 67bbb0a..508fab9 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/Dialog.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/Dialog.kt @@ -41,7 +41,7 @@ interface Dialog { * @param request request to send a file. * @param callback receive result from server onSent(Message)/onError. * - * * @return Transfer control: .pause()/.resume()/.cancel() + * @return A CancellationToken that can be used to cancel the sending operation. */ fun sendFile(request: FileTransferRequest, callback: MessageCallbackListener): CancellationToken diff --git a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/PortalClient.kt b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/PortalClient.kt index 11a0ace..6a98613 100644 --- a/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/PortalClient.kt +++ b/mobile-sdk/src/main/java/com/webitel/mobile_sdk/domain/PortalClient.kt @@ -26,6 +26,19 @@ interface PortalClient { */ fun setAccessTokenHeader(token: String) + /** + * Sets the access token. + * + * This function stores the provided access token and calls the callback upon completion of the operation. + * + * @param token The access token to be set. + * @param callback called when the operation is finished, where: + * + * `.onSuccess`: If the token was successfully set. + * `.onError`: If an error occurred while setting the token, along with an `Error` object describing the error. + */ + fun setAccessTokenHeader(token: String, callback: CallbackListener) + /** * Register device PUSH subscription * */ diff --git a/mobile-sdk/src/main/proto/portal/connect.proto b/mobile-sdk/src/main/proto/portal/connect.proto index f777ac1..2810374 100644 --- a/mobile-sdk/src/main/proto/portal/connect.proto +++ b/mobile-sdk/src/main/proto/portal/connect.proto @@ -39,10 +39,13 @@ message Response { google.rpc.Status err = 2; } -message UpdateSignedOut { - google.rpc.Status cause = 1; -} - message Echo { bytes data = 1; } + +// UpdateDisconnect notifies the client about +// an imminent disconnect due to specified reason. +message UpdateDisconnect { + // The disconnect reason + google.rpc.Status cause = 1; +} diff --git a/mobile-sdk/src/main/proto/portal/customer.proto b/mobile-sdk/src/main/proto/portal/customer.proto index 0c0b210..a824765 100644 --- a/mobile-sdk/src/main/proto/portal/customer.proto +++ b/mobile-sdk/src/main/proto/portal/customer.proto @@ -16,7 +16,7 @@ service Customer { rpc Token(TokenRequest) returns (AccessToken); // Logout session request - rpc Logout(LogoutRequest) returns (UpdateSignedOut); + rpc Logout(LogoutRequest) returns (UpdateDisconnect); // Inspect your authorization access token rpc Inspect(InspectRequest) returns (AccessToken);