Lots of progress on new login

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-01-14 11:42:28 +01:00
parent 5e8ab11cbf
commit 7cf1cf6025
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
34 changed files with 808 additions and 208 deletions

View File

@ -159,6 +159,8 @@ ext {
lifecycle_version = '2.2.0-rc03'
coil_version = "0.9.1"
room_version = "2.2.3"
geckoviewChannel = "stable"
geckoviewVersion = "72.0.20200107212822"
}
configurations.all {
@ -185,6 +187,7 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.mozilla.geckoview:geckoview:${geckoviewVersion}"
implementation "com.github.stateless4j:stateless4j:2.6.0"
// ViewModel and LiveData

View File

@ -26,8 +26,6 @@ import android.view.View
import android.widget.ImageView
import androidx.emoji.widget.EmojiTextView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import butterknife.BindView
import butterknife.ButterKnife
import coil.api.load
import coil.transform.CircleCropTransformation
import com.nextcloud.talk.R

View File

@ -181,7 +181,7 @@ class NextcloudTalkApplication : Application(), LifecycleObserver {
startKoin {
androidContext(this@NextcloudTalkApplication)
androidLogger()
modules(listOf(CommunicationModule, StorageModule, NetworkModule, ConversationsModule, ConversationsListModule, ServiceModule, AccountModule, ServerModule))
modules(listOf(CommunicationModule, StorageModule, NetworkModule, ConversationsListModule, ServiceModule, AccountModule, ServerModule))
}
}

View File

@ -20,6 +20,7 @@
package com.nextcloud.talk.controllers
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.os.Handler
@ -113,6 +114,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo
eventBus.register(this)
}
@SuppressLint("SourceLockedOrientationActivity")
override fun onViewBound(view: View) {
super.onViewBound(view)
if (activity != null) {
@ -257,16 +259,11 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo
override fun onSubscribe(d: Disposable) {}
override fun onNext(userProfileOverall: UserProfileOverall) {
var displayName: String? = null
if (!TextUtils.isEmpty(userProfileOverall.ocs.data.displayName)) {
displayName = userProfileOverall.ocs.data.displayName
} else if (!TextUtils.isEmpty(userProfileOverall.ocs.data.displayNameAlt)) {
displayName = userProfileOverall.ocs.data.displayNameAlt
}
var displayName: String? = userProfileOverall.ocs.data.displayName
if (!TextUtils.isEmpty(displayName)) {
GlobalScope.launch {
storeProfile(displayName, userProfileOverall.ocs.data.userId)
storeProfile(displayName, userProfileOverall.ocs.data.userId!!)
}
} else {
if (activity != null) {

View File

@ -900,22 +900,22 @@ class CallController(args: Bundle) : BaseController() {
override fun onNext(signalingSettingsOverall: SignalingSettingsOverall) {
var iceServer: IceServer
if (signalingSettingsOverall.ocs != null &&
signalingSettingsOverall.ocs.settings != null
signalingSettingsOverall.ocs.signalingSettings != null
) {
externalSignalingServer = ExternalSignalingServer()
if (!TextUtils.isEmpty(
signalingSettingsOverall.ocs.settings.externalSignalingServer
signalingSettingsOverall.ocs.signalingSettings.externalSignalingServer
) && !TextUtils.isEmpty(
signalingSettingsOverall.ocs
.settings
.signalingSettings
.externalSignalingTicket
)
) {
externalSignalingServer = ExternalSignalingServer()
externalSignalingServer!!.externalSignalingServer = signalingSettingsOverall.ocs.settings.externalSignalingServer
externalSignalingServer!!.externalSignalingTicket = signalingSettingsOverall.ocs.settings.externalSignalingTicket
externalSignalingServer!!.externalSignalingServer = signalingSettingsOverall.ocs.signalingSettings.externalSignalingServer
externalSignalingServer!!.externalSignalingTicket = signalingSettingsOverall.ocs.signalingSettings.externalSignalingTicket
hasExternalSignalingServer = true
} else {
hasExternalSignalingServer = false
@ -936,9 +936,9 @@ class CallController(args: Bundle) : BaseController() {
}
if (signalingSettingsOverall.ocs.settings.stunServers != null) {
for (i in 0 until signalingSettingsOverall.ocs.settings.stunServers.size) {
iceServer = signalingSettingsOverall.ocs.settings.stunServers[i]
if (signalingSettingsOverall.ocs.signalingSettings.stunServers != null) {
for (i in 0 until signalingSettingsOverall.ocs.signalingSettings.stunServers!!.size) {
iceServer = signalingSettingsOverall.ocs.signalingSettings.stunServers!![i]
if (TextUtils.isEmpty(iceServer.username) || TextUtils.isEmpty(
iceServer
.credential
@ -956,20 +956,20 @@ class CallController(args: Bundle) : BaseController() {
}
}
if (signalingSettingsOverall.ocs.settings.turnServers != null) {
for (i in 0 until signalingSettingsOverall.ocs.settings.turnServers.size) {
iceServer = signalingSettingsOverall.ocs.settings.turnServers[i]
for (j in 0 until iceServer.urls.size) {
if (signalingSettingsOverall.ocs.signalingSettings.turnServers != null) {
for (i in 0 until signalingSettingsOverall.ocs.signalingSettings.turnServers!!.size) {
iceServer = signalingSettingsOverall.ocs.signalingSettings.turnServers!![i]
for (j in 0 until iceServer.urls!!.size) {
if (TextUtils.isEmpty(iceServer.username) || TextUtils.isEmpty(
iceServer
.credential
)
) {
iceServers!!.add(PeerConnection.IceServer(iceServer.urls[j]))
iceServers!!.add(PeerConnection.IceServer(iceServer.urls!![j]))
} else {
iceServers!!.add(
PeerConnection.IceServer(
iceServer.urls[j],
iceServer.urls!![j],
iceServer.username, iceServer.credential
)
)

View File

@ -26,7 +26,6 @@ import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.events.EventStatus
import com.nextcloud.talk.jobs.WebsocketConnectionsWorker
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
@ -69,8 +68,8 @@ class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters)
override fun onNext(signalingSettingsOverall: SignalingSettingsOverall) {
val externalSignalingServer: ExternalSignalingServer
externalSignalingServer = ExternalSignalingServer()
externalSignalingServer.externalSignalingServer = signalingSettingsOverall.ocs.settings.externalSignalingServer
externalSignalingServer.externalSignalingTicket = signalingSettingsOverall.ocs.settings.externalSignalingTicket
externalSignalingServer.externalSignalingServer = signalingSettingsOverall.ocs.signalingSettings.externalSignalingServer
externalSignalingServer.externalSignalingTicket = signalingSettingsOverall.ocs.signalingSettings.externalSignalingTicket
val user = usersRepository.getUserWithId(userEntity.id!!)
user.externalSignaling = externalSignalingServer
runBlocking {

View File

@ -17,28 +17,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.signaling.settings
package com.nextcloud.talk.models.json.signaling.settings;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import java.util.List;
import lombok.Data;
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
import lombok.Data
@Data
@JsonObject
public class IceServer {
@JsonField(name = "url")
public String url;
@JsonField(name = "urls")
public List<String> urls;
@JsonField(name = "username")
public String username;
@JsonField(name = "credential")
public String credential;
}
@Parcelize
@Serializable
data class IceServer @JvmOverloads constructor(
@JvmField
@JsonField(name = ["url"])
var url: String? = null,
@JvmField
@JsonField(name = ["urls"])
var urls: List<String>? = null,
@JvmField
@JsonField(name = ["username"])
var username: String? = null,
@JvmField
@JsonField(name = ["credential"])
var credential: String? = null
) : Parcelable

View File

@ -1,44 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.signaling.settings;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import java.util.List;
import lombok.Data;
@Data
@JsonObject
public class Settings {
@JsonField(name = "stunservers")
public List<IceServer> stunServers;
@JsonField(name = "turnservers")
public List<IceServer> turnServers;
@JsonField(name = "server")
public String externalSignalingServer;
@JsonField(name = "ticket")
public String externalSignalingTicket;
}

View File

@ -0,0 +1,46 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.signaling.settings
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
import lombok.Data
@Data
@JsonObject
@Serializable
@Parcelize
data class SignalingSettings @JvmOverloads constructor(
@JvmField
@JsonField(name = ["stunservers"])
var stunServers: List<IceServer>? = null,
@JvmField
@JsonField(name = ["turnservers"])
var turnServers: List<IceServer>? = null,
@JvmField
@JsonField(name = ["server"])
var externalSignalingServer: String? = null,
@JvmField
@JsonField(name = ["ticket"])
var externalSignalingTicket: String? = null
) : Parcelable

View File

@ -30,5 +30,5 @@ import lombok.Data;
@JsonObject
public class SignalingSettingsOcs extends GenericOCS {
@JsonField(name = "data")
public Settings settings;
public SignalingSettings signalingSettings;
}

View File

@ -18,25 +18,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.userprofile;
package com.nextcloud.talk.models.json.userprofile
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.parceler.Parcel;
import lombok.Data;
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import lombok.Data
import org.parceler.Parcel
@Parcel
@Data
@JsonObject
public class UserProfileData {
@JsonField(name = "display-name")
public String displayName;
@JsonField(name = "displayname")
public String displayNameAlt;
@JsonField(name = "id")
public String userId;
}
class UserProfileData {
@JvmField
@JsonField(name = ["display-name", "displayname"])
var displayName: String? = null
@JvmField
@JsonField(name = ["id"])
var userId: String? = null
}

View File

@ -24,6 +24,9 @@ import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.newarch.data.source.remote.ApiService
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
import com.nextcloud.talk.newarch.local.models.UserNgEntity
@ -86,6 +89,30 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
}
}
override suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map<String, String>): PushRegistrationOverall {
return apiService.registerForPushWithServer(user.getCredentials(), ApiUtils.getUrlNextcloudPush(user.baseUrl), options)
}
override suspend fun unregisterPushWithServerForUser(user: UserNgEntity): GenericOverall {
return apiService.unregisterForPushWithServer(user.getCredentials(), ApiUtils.getUrlNextcloudPush(user.baseUrl))
}
override suspend fun registerPushWithProxyForUser(user: UserNgEntity, options: Map<String, String>): Any {
return apiService.unregisterForPushWithProxy(ApiUtils.getUrlPushProxy(), options)
}
override suspend fun unregisterPushWithProxyForUser(user: UserNgEntity, options: Map<String, String>): Any {
return apiService.unregisterForPushWithProxy(ApiUtils.getUrlPushProxy(), options)
}
override suspend fun getSignalingSettingsForUser(user: UserNgEntity): SignalingSettingsOverall {
return apiService.getSignalingSettings(user.getCredentials(), ApiUtils.getUrlForSignalingSettings(user.baseUrl))
}
override suspend fun getProfileForUser(user: UserNgEntity): UserProfileOverall {
return apiService.getUserProfile(user.getCredentials(), ApiUtils.getUrlForUserProfile(user.baseUrl))
}
override suspend fun getConversationsForUser(user: UserNgEntity): List<Conversation> {
return apiService.getConversations(
user.getCredentials(),

View File

@ -24,16 +24,59 @@ import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.conversations.RoomsOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import io.reactivex.Observable
import retrofit2.http.*
interface ApiService {
/*
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room
*/
@GET
suspend fun getCapabilities(@Url url: String): CapabilitiesOverall
@GET
suspend fun getSignalingSettings(@Header("Authorization") authorization: String,
@Url url: String): SignalingSettingsOverall
@GET
suspend fun getUserProfile(@Header("Authorization") authorization: String,
@Url url: String): UserProfileOverall
/*
QueryMap items are as follows:
- "format" : "json"
- "pushTokenHash" : ""
- "devicePublicKey" : ""
- "proxyServer" : ""
Server URL is: baseUrl + ocsApiVersion + "/apps/notifications/api/v2/push
*/
@POST
fun registerForPushWithServer(
@Header("Authorization") authorization: String,
@Url url: String,
@QueryMap options: Map<String, String>): PushRegistrationOverall
@DELETE
fun unregisterForPushWithServer(@Header("Authorization") authorization: String,
@Url url: String): GenericOverall
@FormUrlEncoded
@POST
fun registerForPushWithProxy(@Url url: String,
@FieldMap fields: Map<String, String>): Any
/*
QueryMap items are as follows:
- "deviceIdentifier": "{{deviceIdentifier}}",
- "deviceIdentifierSignature": "{{signature}}",
- "userPublicKey": "{{userPublicKey}}"
*/
@DELETE
fun unregisterForPushWithProxy(@Url url: String?,
@QueryMap fields: Map<String, String>): Any
@GET
suspend fun getConversations(
@Header(

View File

@ -1,70 +0,0 @@
package com.nextcloud.talk.newarch.di.module
import android.app.Application
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
import com.nextcloud.talk.newarch.domain.repository.offline.MessagesRepository
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
import com.nextcloud.talk.newarch.domain.usecases.*
import com.nextcloud.talk.newarch.features.chat.ChatViewModelFactory
import com.nextcloud.talk.newarch.services.GlobalService
import org.koin.dsl.module
val ConversationsModule = module {
single { createGetConversationUseCase(get(), get()) }
single { createGetConversationsUseCase(get(), get()) }
single { createSetConversationFavoriteValueUseCase(get(), get()) }
single { createLeaveConversationUseCase(get(), get()) }
single { createDeleteConversationUseCase(get(), get()) }
single { createJoinConversationUseCase(get(), get()) }
single { createExitConversationUseCase(get(), get()) }
factory { createChatViewModelFactory(get(), get(), get(), get(), get(), get()) }
}
fun createSetConversationFavoriteValueUseCase(
nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): SetConversationFavoriteValueUseCase {
return SetConversationFavoriteValueUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createGetConversationUseCase(
nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): GetConversationUseCase {
return GetConversationUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createGetConversationsUseCase(
nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): GetConversationsUseCase {
return GetConversationsUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createLeaveConversationUseCase(
nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): LeaveConversationUseCase {
return LeaveConversationUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createDeleteConversationUseCase(
nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): DeleteConversationUseCase {
return DeleteConversationUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createJoinConversationUseCase(nextcloudTalkRepository: NextcloudTalkRepository, apiErrorHandler: ApiErrorHandler): JoinConversationUseCase {
return JoinConversationUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createExitConversationUseCase(nextcloudTalkRepository: NextcloudTalkRepository, apiErrorHandler: ApiErrorHandler): ExitConversationUseCase {
return ExitConversationUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createChatViewModelFactory(application: Application, joinConversationUseCase: JoinConversationUseCase, exitConversationUseCase: ExitConversationUseCase, conversationsRepository: ConversationsRepository, messagesRepository: MessagesRepository, globalService: GlobalService): ChatViewModelFactory {
return ChatViewModelFactory(application, joinConversationUseCase, exitConversationUseCase, conversationsRepository, messagesRepository, globalService)
}

View File

@ -3,14 +3,22 @@ package com.nextcloud.talk.newarch.di.module
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
import com.nextcloud.talk.newarch.domain.usecases.GetCapabilitiesUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetSignalingSettingsUseCase
import org.koin.dsl.module
val ServerModule = module {
single { createGetCapabilitiesUseCase(get(), get()) }
single { createGetSignalingSettingsUseCase(get(), get()) }
}
fun createGetCapabilitiesUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): GetCapabilitiesUseCase {
return GetCapabilitiesUseCase(nextcloudTalkRepository, apiErrorHandler)
}
fun createGetSignalingSettingsUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): GetSignalingSettingsUseCase {
return GetSignalingSettingsUseCase(nextcloudTalkRepository, apiErrorHandler)
}

View File

@ -24,9 +24,19 @@ import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.newarch.local.models.UserNgEntity
interface NextcloudTalkRepository {
suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map<String, String>): PushRegistrationOverall
suspend fun unregisterPushWithServerForUser(user: UserNgEntity): GenericOverall
suspend fun registerPushWithProxyForUser(user: UserNgEntity, options: Map<String, String>): Any
suspend fun unregisterPushWithProxyForUser(user: UserNgEntity, options: Map<String, String>): Any
suspend fun getSignalingSettingsForUser(user: UserNgEntity): SignalingSettingsOverall
suspend fun getProfileForUser(user: UserNgEntity): UserProfileOverall
suspend fun getConversationsForUser(user: UserNgEntity): List<Conversation>
suspend fun setFavoriteValueForConversation(
user: UserNgEntity,

View File

@ -0,0 +1,39 @@
/*
*
* * Nextcloud Talk application
* *
* * @author Mario Danic
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.nextcloud.talk.newarch.domain.usecases
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
import org.koin.core.parameter.DefinitionParameters
class GetProfileUseCase constructor(
private val nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler?
) : UseCase<UserProfileOverall, Any?>(apiErrorHandler) {
override suspend fun run(params: Any?): UserProfileOverall {
val definitionParameters = params as DefinitionParameters
return nextcloudTalkRepository.getProfileForUser(definitionParameters[0])
}
}

View File

@ -0,0 +1,39 @@
/*
*
* * Nextcloud Talk application
* *
* * @author Mario Danic
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.nextcloud.talk.newarch.domain.usecases
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
import org.koin.core.parameter.DefinitionParameters
class GetSignalingSettingsUseCase constructor(
private val nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler?
) : UseCase<SignalingSettingsOverall, Any?>(apiErrorHandler) {
override suspend fun run(params: Any?): SignalingSettingsOverall {
val definitionParameters = params as DefinitionParameters
return nextcloudTalkRepository.getSignalingSettingsForUser(definitionParameters[0])
}
}

View File

@ -1,7 +1,10 @@
package com.nextcloud.talk.newarch.features.account.di.module
import android.app.Application
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.domain.usecases.GetCapabilitiesUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetProfileUseCase
import com.nextcloud.talk.newarch.features.account.loginentry.LoginEntryViewModelFactory
import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryViewModelFactory
import org.koin.android.ext.koin.androidApplication
import org.koin.dsl.module
@ -12,6 +15,9 @@ val AccountModule = module {
androidApplication(), get()
)
}
factory {
createLoginEntryViewModelFactory(androidApplication(), get(), get(), get())
}
}
fun createServerEntryViewModelFactory(
@ -21,4 +27,15 @@ fun createServerEntryViewModelFactory(
return ServerEntryViewModelFactory(
application, getCapabilitiesUseCase
)
}
fun createLoginEntryViewModelFactory(
application: Application,
getProfileUseCase: GetProfileUseCase,
getCapabilitiesUseCase: GetCapabilitiesUseCase,
usersRepository: UsersRepository
): LoginEntryViewModelFactory {
return LoginEntryViewModelFactory(
application, getProfileUseCase, getCapabilitiesUseCase, usersRepository
)
}

View File

@ -0,0 +1,47 @@
/*
*
* * Nextcloud Talk application
* *
* * @author Mario Danic
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.nextcloud.talk.newarch.features.account.loginentry
import kotlinx.serialization.Serializable
enum class LoginEntryState {
PENDING_CHECK,
CHECKING,
FAILED,
OK
}
enum class LoginEntryStateClarification {
INVALID_PARSED_DATA,
PROFILE_FETCH_FAILED,
CAPABILITIES_FETCH_FAILED,
SIGNALING_SETTINGS_FETCH_FAILED,
PUSH_REGISTRATION_MISSING_TOKEN,
PUSH_REGISTRATION_WITH_SERVER_FAILED,
PUSH_REGISTRATION_WITH_PUSH_PROXY_FAILED,
ACCOUNT_UPDATED,
ACCOUNT_CREATED
}
@Serializable
data class LoginEntryStateWrapper(val state: LoginEntryState, val clarification: LoginEntryStateClarification?)

View File

@ -0,0 +1,143 @@
/*
*
* * Nextcloud Talk application
* *
* * @author Mario Danic
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.nextcloud.talk.newarch.features.account.loginentry
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import com.nextcloud.talk.R
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
import com.nextcloud.talk.utils.bundle.BundleKeys
import kotlinx.android.synthetic.main.login_entry_view.view.*
import kotlinx.android.synthetic.main.login_web_view.view.*
import org.koin.android.ext.android.inject
import org.mozilla.geckoview.*
import org.mozilla.geckoview.GeckoSessionSettings.USER_AGENT_MODE_MOBILE
import java.util.*
class LoginEntryView(val bundle: Bundle) : BaseView() {
private val protocolSuffix = "://"
private val dataSeparator = ":"
private lateinit var viewModel: LoginEntryViewModel
val factory: LoginEntryViewModelFactory by inject()
private lateinit var geckoView: GeckoView
private lateinit var geckoSession: GeckoSession
private val assembledPrefix = resources?.getString(R.string.nc_talk_login_scheme) + protocolSuffix + "login/"
private val webLoginUserAgent: String
get() = (Build.MANUFACTURER.substring(0, 1).toUpperCase(
Locale.getDefault()) +
Build.MANUFACTURER.substring(1).toLowerCase(
Locale.getDefault()) + " " + Build.MODEL + " ("
+ resources!!.getString(R.string.nc_app_name) + ")")
override fun getLayoutId(): Int {
return R.layout.login_entry_view
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
actionBar?.hide()
viewModel = viewModelProvider(factory).get(LoginEntryViewModel::class.java)
val view = super.onCreateView(inflater, container)
geckoView = view.stubImport.inflate() as GeckoView
activity?.let {
val settings = GeckoSessionSettings.Builder()
//.usePrivateMode(true)
//.useTrackingProtection(true)
.userAgentMode(USER_AGENT_MODE_MOBILE)
.userAgentOverride(webLoginUserAgent)
.suspendMediaWhenInactive(true)
.allowJavascript(true)
geckoView.autofillEnabled = true
geckoSession = GeckoSession(settings.build())
val runtime = GeckoRuntime.create(it)
geckoSession.open(runtime)
geckoSession.progressDelegate = createProgressDelegate()
geckoSession.navigationDelegate = createNavigationDelegate()
geckoView.setSession(geckoSession)
bundle.getString(BundleKeys.KEY_BASE_URL)?.let { baseUrl ->
geckoSession.loadUri("$baseUrl/index.php/login/flow", mapOf<String, String>("OCS-APIRequest" to "true"))
}
}
viewModel.state.observe(this@LoginEntryView, Observer {
if (it.state == LoginEntryState.FAILED) {
router.popController(this)
} else if (it.state == LoginEntryState.PENDING_CHECK) {
view.progressBar.isVisible = false
view.geckoView.isVisible = true
} else if (it.state == LoginEntryState.CHECKING) {
view.progressBar.isVisible = true
view.geckoView.isVisible = false
} else {
// all good, proceed
}
})
return view
}
private fun createNavigationDelegate(): GeckoSession.NavigationDelegate {
return object : GeckoSession.NavigationDelegate {
override fun onLoadRequest(p0: GeckoSession, p1: GeckoSession.NavigationDelegate.LoadRequest): GeckoResult<AllowOrDeny>? {
if (p1.uri.startsWith(assembledPrefix)) {
return GeckoResult.DENY
}
return super.onLoadRequest(p0, p1)
}
override fun onLocationChange(p0: GeckoSession, p1: String?) {
super.onLocationChange(p0, p1)
viewModel.parseData(assembledPrefix, dataSeparator, p1)
}
}
}
private fun createProgressDelegate(): GeckoSession.ProgressDelegate {
return object : GeckoSession.ProgressDelegate {
override fun onPageStop(session: GeckoSession, success: Boolean) = Unit
override fun onSecurityChange(
session: GeckoSession,
securityInfo: GeckoSession.ProgressDelegate.SecurityInformation
) = Unit
override fun onPageStart(session: GeckoSession, url: String) = Unit
override fun onProgressChange(session: GeckoSession, progress: Int) {
view?.pageProgressBar?.progress = progress
view?.pageProgressBar?.isVisible = progress in 1..99
}
}
}
}

View File

@ -0,0 +1,164 @@
package com.nextcloud.talk.newarch.features.account.loginentry
import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.nextcloud.talk.models.LoginData
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseViewModel
import com.nextcloud.talk.newarch.data.model.ErrorModel
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.domain.usecases.GetCapabilitiesUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetProfileUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetSignalingSettingsUseCase
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.utils.preferences.AppPreferences
import kotlinx.coroutines.launch
import org.koin.core.parameter.parametersOf
import java.net.URLDecoder
class LoginEntryViewModel constructor(
application: Application,
private val getProfileUseCase: GetProfileUseCase,
private val getCapabilitiesUseCase: GetCapabilitiesUseCase,
private val getSignalingSettingsUseCase: GetSignalingSettingsUseCase,
private val appPreferences: AppPreferences,
private val usersRepository: UsersRepository) :
BaseViewModel<LoginEntryView>(application) {
val state: MutableLiveData<LoginEntryStateWrapper> = MutableLiveData(LoginEntryStateWrapper(LoginEntryState.PENDING_CHECK, null))
private val user = UserNgEntity(-1, "-1", "", "")
fun parseData(prefix: String, separator: String, data: String?) {
viewModelScope.launch {
if (data?.startsWith(prefix) == false) {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.INVALID_PARSED_DATA))
return@launch
}
data as String
val loginData = LoginData()
// format is xxx://login/server:xxx&user:xxx&password:xxx
val dataWithoutPrefix = data.substring(prefix.length)
val values = dataWithoutPrefix.split("&").toTypedArray()
if (values.size != 3) {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.INVALID_PARSED_DATA))
return@launch
}
for (value in values) {
when {
value.startsWith("user$separator") -> {
loginData.username = URLDecoder.decode(
value.substring("user$separator".length)
)
}
value.startsWith("password$separator") -> {
loginData.token = URLDecoder.decode(
value.substring("password$separator".length)
)
}
value.startsWith("server$separator") -> {
loginData.serverUrl = URLDecoder.decode(
value.substring("server$separator".length)
)
}
else -> {
// fail
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.INVALID_PARSED_DATA))
return@launch
}
}
}
if (!loginData.serverUrl.isNullOrEmpty() && !loginData.username.isNullOrEmpty() && !loginData.token.isNullOrEmpty()) {
storeCredentialsOrVerify(loginData)
} else {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.INVALID_PARSED_DATA))
return@launch
}
}
}
private suspend fun storeCredentialsOrVerify(loginData: LoginData) {
// username and server url will be null here for sure because we do a check earlier in the process
val user = usersRepository.getUserWithUsernameAndServer(loginData.username!!, loginData.serverUrl!!)
if (user != null) {
user.token = loginData.token
usersRepository.updateUser(user)
state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.ACCOUNT_UPDATED))
} else {
getProfile(loginData)
}
}
private fun getProfile(loginData: LoginData) {
user.username = loginData.username!!
user.baseUrl = loginData.serverUrl!!
getProfileUseCase.invoke(viewModelScope, parametersOf(user), object : UseCaseResponse<UserProfileOverall> {
override suspend fun onSuccess(result: UserProfileOverall) {
result.ocs.data.userId?.let { userId ->
user.displayName = result.ocs.data.displayName
user.userId = userId
getCapabilities()
} ?: run {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.PROFILE_FETCH_FAILED))
}
}
override suspend fun onError(errorModel: ErrorModel?) {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.PROFILE_FETCH_FAILED))
}
})
}
private fun getCapabilities() {
getCapabilitiesUseCase.invoke(viewModelScope, parametersOf(user.baseUrl), object : UseCaseResponse<CapabilitiesOverall> {
override suspend fun onSuccess(result: CapabilitiesOverall) {
user.capabilities = result.ocs.data.capabilities
getSignalingSettings()
}
override suspend fun onError(errorModel: ErrorModel?) {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.CAPABILITIES_FETCH_FAILED))
}
})
}
private fun getSignalingSettings() {
getSignalingSettingsUseCase.invoke(viewModelScope, parametersOf(user), object : UseCaseResponse<SignalingSettingsOverall> {
override suspend fun onSuccess(result: SignalingSettingsOverall) {
user.signalingSettings = result.ocs.signalingSettings
registerForPush()
}
override suspend fun onError(errorModel: ErrorModel?) {
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.SIGNALING_SETTINGS_FETCH_FAILED))
}
})
}
private fun registerForPush() {
val token = appPreferences.pushToken
if (!token.isNullOrBlank()) {
} else {
state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.PUSH_REGISTRATION_MISSING_TOKEN))
}
}
private fun registerForPushWithServer() {
}
private fun registerForPushWithProxy() {
}
}

View File

@ -0,0 +1,15 @@
package com.nextcloud.talk.newarch.features.account.loginentry
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.domain.usecases.GetCapabilitiesUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetProfileUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetSignalingSettingsUseCase
class LoginEntryViewModelFactory constructor(private val application: Application, private val getProfileUseCase: GetProfileUseCase, private val getCapabilitiesUseCase: GetCapabilitiesUseCase, private val getSignalingSettingsUseCase: GetSignalingSettingsUseCase, private val usersRepository: UsersRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return LoginEntryViewModel(application, getProfileUseCase, getCapabilitiesUseCase, getSignalingSettingsUseCase, usersRepository) as T
}
}

View File

@ -30,8 +30,11 @@ import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.nextcloud.talk.R
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
import com.nextcloud.talk.newarch.features.account.loginentry.LoginEntryView
import com.nextcloud.talk.utils.bundle.BundleKeys
import kotlinx.android.synthetic.main.server_entry_view.view.*
import org.koin.android.ext.android.inject
@ -54,7 +57,7 @@ class ServerEntryView : BaseView() {
viewModel.apply {
checkState.observe(this@ServerEntryView, Observer {
when(it.checkState) {
when (it.checkState) {
ServerEntryCapabilitiesCheckState.WAITING_FOR_INPUT -> {
view.serverEntryTextInputLayout.isEnabled = true
view.serverEntryProgressBar.isVisible = false
@ -67,7 +70,8 @@ class ServerEntryView : BaseView() {
ServerEntryCapabilitiesCheckState.SERVER_SUPPORTED -> {
val bundle = Bundle()
bundle.putString(BundleKeys.KEY_BASE_URL, it.url)
//router.pushController(RouterTransaction.with(LoginEntryView(bundle)).popChangeHandler(HorizontalChangeHandler()).pushChangeHandler(HorizontalChangeHandler()))
router.pushController(RouterTransaction.with(LoginEntryView(bundle))
.popChangeHandler(HorizontalChangeHandler()).pushChangeHandler(HorizontalChangeHandler()))
}
// Unsupported
else -> {
@ -96,8 +100,8 @@ class ServerEntryView : BaseView() {
val drawableRight = 2
val drawableBottom = 3
if(event.action == MotionEvent.ACTION_UP) {
if(event.rawX >= (view.serverEntryTextInputEditText.right - view.serverEntryTextInputEditText.compoundDrawables[drawableRight].bounds.width())) {
if (event.action == MotionEvent.ACTION_UP) {
if (event.rawX >= (view.serverEntryTextInputEditText.right - view.serverEntryTextInputEditText.compoundDrawables[drawableRight].bounds.width())) {
if (view.serverEntryTextInputEditText.compoundDrawables[drawableRight].alpha == 255) {
view.serverEntryTextInputEditText?.text?.let { serverUrl ->
var baseUrl = serverUrl.toString()

View File

@ -28,7 +28,7 @@ import androidx.lifecycle.viewModelScope
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseViewModel
import com.nextcloud.talk.newarch.data.model.ErrorModel
import com.nextcloud.talk.newarch.domain.usecases.*
import com.nextcloud.talk.newarch.domain.usecases.GetCapabilitiesUseCase
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import org.koin.core.parameter.parametersOf
@ -53,11 +53,10 @@ class ServerEntryViewModel constructor(
override suspend fun onError(errorModel: ErrorModel?) {
if (url.startsWith("https://")) {
fetchCapabilities(url.replace("https://", "http://"))
} else {
} else {
checkState.postValue(ServerEntryCapabilitiesCheckStateWrapper(ServerEntryCapabilitiesCheckState.SERVER_UNSUPPORTED, url))
}
}
})
}
}

View File

@ -27,7 +27,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.nextcloud.talk.newarch.domain.usecases.GetCapabilitiesUseCase
class ServerEntryViewModelFactory constructor(private val application: Application, private val getCapabilitiesUseCase: GetCapabilitiesUseCase): ViewModelProvider.Factory {
class ServerEntryViewModelFactory constructor(private val application: Application, private val getCapabilitiesUseCase: GetCapabilitiesUseCase) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return ServerEntryViewModel(application, getCapabilitiesUseCase) as T
}

View File

@ -121,8 +121,8 @@ open class ConversationsPresenter(context: Context, onElementClick: ((Page, Hold
)
} else {
authorDisplayName = if (!TextUtils.isEmpty(conversation.lastMessage?.actorDisplayName)) {
conversation.lastMessage?.actorDisplayName!!.substringBefore(" ") }
else if ("guests" == conversation.lastMessage!!.actorType)
conversation.lastMessage?.actorDisplayName!!.substringBefore(" ")
} else if ("guests" == conversation.lastMessage!!.actorType)
context.getString(R.string.nc_guest)
else
""

View File

@ -21,21 +21,23 @@
package com.nextcloud.talk.newarch.local.converters
import androidx.room.TypeConverter
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettings
import com.nextcloud.talk.newarch.utils.MagicJson
import kotlinx.serialization.json.Json
class ExternalSignalingConverter {
class SignalingSettingsConverter {
val json = Json(MagicJson.customJsonConfiguration)
@TypeConverter
fun fromExternalSignalingToString(externalSignalingServer: ExternalSignalingServer?): String {
if (externalSignalingServer == null) {
return ""
fun fromSignalingSettingsToString(signalingSettings: SignalingSettings?): String {
return if (signalingSettings == null) {
""
} else {
return LoganSquare.serialize(externalSignalingServer)
json.stringify(SignalingSettings.serializer(), signalingSettings)
}
}
@TypeConverter
fun fromStringToExternalSignaling(value: String): ExternalSignalingServer? {
return LoganSquare.parse(value, ExternalSignalingServer::class.java)
fun fromStringToSignalingSettings(value: String): SignalingSettings? {
return json.parse(SignalingSettings.serializer(), value)
}
}

View File

@ -43,7 +43,7 @@ import com.nextcloud.talk.newarch.local.models.UserNgEntity
ConversationReadOnlyStateConverter::class, NotificationLevelConverter::class,
ConversationTypeConverter::class, ParticipantTypeConverter::class,
PushConfigurationConverter::class, CapabilitiesConverter::class,
ExternalSignalingConverter::class,
SignalingSettingsConverter::class,
UserStatusConverter::class, SystemMessageTypeConverter::class, ParticipantMapConverter::class
)

View File

@ -24,9 +24,9 @@ import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.json.capabilities.Capabilities
import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettings
import com.nextcloud.talk.newarch.local.models.other.UserStatus
import com.nextcloud.talk.utils.ApiUtils
import kotlinx.android.parcel.Parcelize
@ -47,8 +47,8 @@ data class UserNgEntity(
@ColumnInfo(name = "capabilities") var capabilities: Capabilities? = null,
@ColumnInfo(name = "client_auth_cert") var clientCertificate: String? = null,
@ColumnInfo(
name = "external_signaling"
) var externalSignaling: ExternalSignalingServer? = null,
name = "signaling_settings"
) var signalingSettings: SignalingSettings? = null,
@ColumnInfo(name = "status") var status: UserStatus? = null
) : Parcelable {
@ -71,7 +71,7 @@ data class UserNgEntity(
if (pushConfiguration != other.pushConfiguration) return false
if (capabilities != other.capabilities) return false
if (clientCertificate != other.clientCertificate) return false
if (externalSignaling != other.externalSignaling) return false
if (signalingSettings != other.signalingSettings) return false
if (status != other.status) return false
return true

View File

@ -0,0 +1,43 @@
/*
*
* * Nextcloud Talk application
* *
* * @author Mario Danic
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.nextcloud.talk.newarch.utils
import kotlinx.serialization.json.JsonConfiguration
sealed class MagicJson {
companion object {
private val defaultIndent: String = " "
private val defaultDiscriminator = "type"
val customJsonConfiguration = JsonConfiguration(
encodeDefaults = true,
strictMode = true,
unquoted = false,
allowStructuredMapKeys = true,
prettyPrint = true,
indent = defaultIndent,
useArrayPolymorphism = true,
classDiscriminator = defaultDiscriminator
)
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<ViewStub
android:id="@+id/stubImport"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:inflatedId="@+id/webViewImportLayout"
android:layout="@layout/login_web_view" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:layout_centerInParent="true"
android:indeterminate="true"
android:indeterminateTint="@color/white"
android:indeterminateTintMode="src_in" />
</RelativeLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ * Nextcloud Talk application
~ *
~ * @author Mario Danic
~ * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.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. If not, see <http://www.gnu.org/licenses/>.
~ */
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/pageProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="@color/white"
android:visibility="gone"/>
<org.mozilla.geckoview.GeckoView
android:id="@+id/geckoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
</RelativeLayout>

View File

@ -31,6 +31,9 @@ buildscript {
maven {
url 'https://jitpack.io'
}
maven {
url 'https://maven.mozilla.org/maven2'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
@ -54,10 +57,15 @@ allprojects {
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
maven {
url 'https://maven.mozilla.org/maven2'
}
maven {
url 'https://jitpack.io'
}
maven { url 'https://maven.google.com' }
maven {
url 'https://maven.google.com'
}
}
}