diff --git a/app/build.gradle b/app/build.gradle
index 76e7b18d2..dacc042d4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -48,7 +48,7 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 130
- versionName "8.0.0alpha1"
+ versionName "9.0.0alpha1"
flavorDimensions "default"
renderscriptTargetApi 19
@@ -159,8 +159,6 @@ ext {
lifecycle_version = '2.2.0-rc03'
coil_version = "0.9.1"
room_version = "2.2.3"
- geckoviewChannel = "nightly"
- geckoviewVersion = "71.0.20200108003105"
}
configurations.all {
@@ -187,7 +185,6 @@ 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
diff --git a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt
index 9983c13f3..2b7498d5d 100644
--- a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt
+++ b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt
@@ -23,7 +23,6 @@ import android.annotation.SuppressLint
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.nextcloud.talk.jobs.NotificationWorker
-import com.nextcloud.talk.jobs.PushRegistrationWorker
import com.nextcloud.talk.utils.bundle.BundleKeys
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
@@ -38,8 +37,6 @@ class MagicFirebaseMessagingService : FirebaseMessagingService(), KoinComponent
override fun onNewToken(token: String) {
super.onNewToken(token)
appPreferences.pushToken = token
- val pushRegistrationWork: OneTimeWorkRequest = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
- WorkManager.getInstance().enqueue(pushRegistrationWork)
}
@SuppressLint("LongLogTag")
diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
index a0438b055..5628eefe2 100644
--- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
+++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
@@ -37,12 +37,11 @@ import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.components.filebrowser.webdav.DavUtils
import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.jobs.CapabilitiesWorker
-import com.nextcloud.talk.jobs.PushRegistrationWorker
import com.nextcloud.talk.jobs.SignalingSettingsWorker
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.models.json.capabilities.Capabilities
-import com.nextcloud.talk.models.json.push.PushConfigurationState
+import com.nextcloud.talk.models.json.push.PushConfiguration
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettings
import com.nextcloud.talk.newarch.di.module.*
import com.nextcloud.talk.newarch.domain.di.module.UseCasesModule
@@ -71,7 +70,6 @@ import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
-import org.mozilla.geckoview.GeckoRuntime
import org.webrtc.PeerConnectionFactory
import org.webrtc.voiceengine.WebRtcAudioManager
import org.webrtc.voiceengine.WebRtcAudioUtils
@@ -140,8 +138,6 @@ class NextcloudTalkApplication : Application(), LifecycleObserver, Configuration
Security.insertProviderAt(Conscrypt.newProvider(), 1)
ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync()
- val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java)
- .build()
val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java)
.build()
val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(
@@ -152,8 +148,6 @@ class NextcloudTalkApplication : Application(), LifecycleObserver, Configuration
val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java)
.build()
- WorkManager.getInstance(this)
- .enqueue(pushRegistrationWork)
WorkManager.getInstance(this)
.enqueue(accountRemovalWork)
WorkManager.getInstance(this)
@@ -202,7 +196,7 @@ class NextcloudTalkApplication : Application(), LifecycleObserver, Configuration
userNg.displayName = user.displayName
try {
userNg.pushConfiguration =
- LoganSquare.parse(user.pushConfigurationState, PushConfigurationState::class.java)
+ LoganSquare.parse(user.pushConfigurationState, PushConfiguration::class.java)
} catch (e: Exception) {
// no push
}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt
deleted file mode 100644
index f3d0830b1..000000000
--- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt
+++ /dev/null
@@ -1,440 +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 .
- */
-
-package com.nextcloud.talk.controllers
-
-import android.annotation.SuppressLint
-import android.content.pm.ActivityInfo
-import android.os.Bundle
-import android.os.Handler
-import android.text.TextUtils
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.work.Data
-import androidx.work.OneTimeWorkRequest
-import androidx.work.WorkManager
-import butterknife.BindView
-import com.bluelinelabs.conductor.RouterTransaction
-import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
-import com.nextcloud.talk.R
-import com.nextcloud.talk.api.NcApi
-import com.nextcloud.talk.controllers.base.BaseController
-import com.nextcloud.talk.events.EventStatus
-import com.nextcloud.talk.jobs.CapabilitiesWorker
-import com.nextcloud.talk.jobs.PushRegistrationWorker
-import com.nextcloud.talk.jobs.SignalingSettingsWorker
-import com.nextcloud.talk.models.json.conversations.RoomsOverall
-import com.nextcloud.talk.models.json.generic.Status
-import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
-import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
-import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView
-import com.nextcloud.talk.newarch.local.dao.UsersDao
-import com.nextcloud.talk.newarch.local.models.UserNgEntity
-import com.nextcloud.talk.utils.ApiUtils
-import com.nextcloud.talk.utils.ClosedInterfaceImpl
-import com.nextcloud.talk.utils.bundle.BundleKeys
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
-import com.uber.autodispose.AutoDispose
-import com.uber.autodispose.ObservableSubscribeProxy
-import io.reactivex.Observer
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.disposables.Disposable
-import io.reactivex.schedulers.Schedulers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import org.greenrobot.eventbus.Subscribe
-import org.greenrobot.eventbus.ThreadMode
-import org.koin.core.KoinComponent
-import org.koin.core.inject
-import java.net.CookieManager
-
-class AccountVerificationController(args: Bundle?) : BaseController(), KoinComponent {
-
- val ncApi: NcApi by inject()
- val cookieManager: CookieManager by inject()
- val usersRepository: UsersRepository by inject()
- val usersDao: UsersDao by inject()
-
- @JvmField
- @BindView(R.id.progress_text)
- internal var progressText: TextView? = null
-
- private var internalAccountId: Long = -1
-
- private var baseUrl: String? = null
- private var username: String? = null
- private var token: String? = null
- private var isAccountImport: Boolean = false
- private var originalProtocol: String? = null
-
- init {
- if (args != null) {
- baseUrl = args.getString(BundleKeys.KEY_BASE_URL)
- username = args.getString(BundleKeys.KEY_USERNAME)
- token = args.getString(BundleKeys.KEY_TOKEN)
- if (args.containsKey(BundleKeys.KEY_IS_ACCOUNT_IMPORT)) {
- isAccountImport = true
- }
- if (args.containsKey(BundleKeys.KEY_ORIGINAL_PROTOCOL)) {
- originalProtocol = args.getString(BundleKeys.KEY_ORIGINAL_PROTOCOL)
- }
- }
- }
-
- override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
- return inflater.inflate(R.layout.controller_account_verification, container, false)
- }
-
- override fun onDetach(view: View) {
- eventBus.unregister(this)
- super.onDetach(view)
- }
-
- override fun onAttach(view: View) {
- super.onAttach(view)
- eventBus.register(this)
- }
-
- @SuppressLint("SourceLockedOrientationActivity")
- override fun onViewBound(view: View) {
- super.onViewBound(view)
- if (activity != null) {
- activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- }
-
- if (actionBar != null) {
- actionBar!!.hide()
- }
-
- if (isAccountImport && !baseUrl!!.startsWith("http://") && !baseUrl!!.startsWith("https://") || !TextUtils
- .isEmpty(originalProtocol) && !baseUrl!!.startsWith(originalProtocol!!)) {
- determineBaseUrlProtocol(true)
- } else {
- checkEverything()
- }
- }
-
- private fun checkEverything() {
- val credentials = ApiUtils.getCredentials(username, token)
- cookieManager.cookieStore.removeAll()
-
- findServerTalkApp(credentials)
- }
-
- private fun determineBaseUrlProtocol(checkForcedHttps: Boolean) {
- cookieManager.cookieStore.removeAll()
-
- val queryUrl: String
-
- baseUrl = baseUrl!!.replace("http://", "").replace("https://", "")
-
- if (checkForcedHttps) {
- queryUrl = "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus()
- } else {
- queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus()
- }
-
- ncApi.getServerStatus(queryUrl)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .`as`>(AutoDispose.autoDisposable(scopeProvider))
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {}
-
- override fun onNext(status: Status) {
- if (checkForcedHttps) {
- baseUrl = "https://" + baseUrl!!
- } else {
- baseUrl = "http://" + baseUrl!!
- }
-
- if (isAccountImport) {
- router.replaceTopController(
- RouterTransaction.with(WebViewLoginController(baseUrl,
- false, username, ""))
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- } else {
- checkEverything()
- }
- }
-
- override fun onError(e: Throwable) {
- if (checkForcedHttps) {
- determineBaseUrlProtocol(false)
- } else {
- GlobalScope.launch {
- abortVerification()
-
- }
- }
- }
-
- override fun onComplete() {
-
- }
- })
- }
-
- private fun findServerTalkApp(credentials: String?) {
- ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl))
- .subscribeOn(Schedulers.io())
- .`as`>(AutoDispose.autoDisposable(scopeProvider))
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {}
-
- override fun onNext(roomsOverall: RoomsOverall) {
- fetchProfile(credentials)
- }
-
- override fun onError(e: Throwable) {
- if (activity != null && resources != null) {
- activity!!.runOnUiThread {
- progressText!!.text = String.format(resources!!.getString(
- R.string.nc_nextcloud_talk_app_not_installed),
- resources!!.getString(R.string.nc_app_name))
- }
- }
-
- ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK
-
- GlobalScope.launch {
- abortVerification()
- }
- }
-
- override fun onComplete() {
-
- }
- })
- }
-
- private suspend fun storeProfile(displayName: String?, userId: String) {
- var user = usersRepository.getUserWithUsernameAndServer(username!!, baseUrl!!)
- if (user == null) {
- user = UserNgEntity(null, userId, username!!, baseUrl!!, token, displayName)
- internalAccountId = usersDao.saveUser(user)
- } else {
- user.displayName = displayName
- usersRepository.updateUser(user)
- internalAccountId = user.id!!
- }
-
- if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) {
- registerForPush()
- } else {
- activity!!.runOnUiThread {
- progressText!!.text = progressText!!.text.toString() + "\n" +
- resources!!.getString(R.string.nc_push_disabled)
- }
- fetchAndStoreCapabilities()
- }
- }
-
- private fun fetchProfile(credentials: String?) {
- ncApi.getUserProfile(credentials,
- ApiUtils.getUrlForUserProfile(baseUrl))
- .subscribeOn(Schedulers.io())
- .`as`>(AutoDispose.autoDisposable(scopeProvider))
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {}
-
- override fun onNext(userProfileOverall: UserProfileOverall) {
- var displayName: String? = userProfileOverall.ocs.data.displayName
-
- if (!TextUtils.isEmpty(displayName)) {
- GlobalScope.launch {
- storeProfile(displayName, userProfileOverall.ocs.data.userId!!)
- }
- } else {
- if (activity != null) {
- activity!!.runOnUiThread {
- progressText!!.text = progressText!!.text.toString() + "\n" +
- resources!!.getString(R.string.nc_display_name_not_fetched)
- }
- }
- GlobalScope.launch {
- abortVerification()
- }
- }
- }
-
- override fun onError(e: Throwable) {
- if (activity != null) {
- activity!!.runOnUiThread {
- progressText!!.text = progressText!!.text.toString() + "\n" +
- resources!!.getString(R.string.nc_display_name_not_fetched)
- }
- }
- GlobalScope.launch {
- abortVerification()
- }
- }
-
- override fun onComplete() {
-
- }
- })
- }
-
- private fun registerForPush() {
- val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
- WorkManager.getInstance().enqueue(pushRegistrationWork)
- }
-
- @Subscribe(threadMode = ThreadMode.BACKGROUND)
- fun onMessageEvent(eventStatus: EventStatus) {
- if (EventStatus.EventType.PUSH_REGISTRATION == eventStatus.eventType) {
- if (internalAccountId == eventStatus.userId
- && !eventStatus.allGood
- && activity != null) {
- activity!!.runOnUiThread {
- progressText!!.text = progressText!!.text.toString() + "\n" +
- resources!!.getString(R.string.nc_push_disabled)
- }
- }
- fetchAndStoreCapabilities()
- } else if (EventStatus.EventType.CAPABILITIES_FETCH == eventStatus.eventType) {
- if (internalAccountId == eventStatus.userId && !eventStatus.allGood) {
- if (activity != null) {
- activity!!.runOnUiThread {
- progressText!!.text = progressText!!.text.toString() + "\n" +
- resources!!.getString(R.string.nc_capabilities_failed)
- }
- }
- GlobalScope.launch {
- abortVerification()
- }
- } else if (internalAccountId == eventStatus.userId && eventStatus.allGood) {
- fetchAndStoreExternalSignalingSettings()
- }
- } else if (EventStatus.EventType.SIGNALING_SETTINGS == eventStatus.eventType) {
- if (internalAccountId == eventStatus.userId && !eventStatus.allGood) {
- if (activity != null) {
- activity!!.runOnUiThread {
- progressText!!.text = progressText!!.text.toString() + "\n" +
- resources!!.getString(R.string.nc_external_server_failed)
- }
- }
- }
-
- GlobalScope.launch {
- proceedWithLogin()
- }
- }
- }
-
- private fun fetchAndStoreCapabilities() {
- val userData = Data.Builder()
- .putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalAccountId)
- .build()
-
- val pushNotificationWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java)
- .setInputData(userData)
- .build()
- WorkManager.getInstance().enqueue(pushNotificationWork)
- }
-
- private fun fetchAndStoreExternalSignalingSettings() {
- val userData = Data.Builder()
- .putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalAccountId)
- .build()
-
- val signalingSettings = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java)
- .setInputData(userData)
- .build()
- WorkManager.getInstance().enqueue(signalingSettings)
- }
-
- private suspend fun proceedWithLogin() {
- cookieManager.cookieStore.removeAll()
- usersRepository.setUserAsActiveWithId(internalAccountId)
-
- if (activity != null) {
- if (usersRepository.getUsers().count() == 1) {
- activity!!.runOnUiThread {
- router.setRoot(RouterTransaction.with(ConversationsListView())
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- }
- } else {
- if (isAccountImport) {
- ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED
- }
- activity!!.runOnUiThread {
- router.popToRoot()
- }
- }
- }
- }
-
- override fun onDestroyView(view: View) {
- super.onDestroyView(view)
- if (activity != null) {
- activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- }
- }
-
- private suspend fun abortVerification() {
-
- if (!isAccountImport) {
- if (internalAccountId != -1L) {
- usersRepository.deleteUserWithId(internalAccountId)
- activity!!.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, 7500) }
-
- } else {
- if (activity != null) {
- activity!!.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, 7500) }
- }
- }
- } else {
- ApplicationWideMessageHolder.getInstance().messageType =
- ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT
- if (activity != null) {
- activity!!.runOnUiThread {
- Handler().postDelayed({
- if (router.hasRootController()) {
- if (activity != null) {
- router.popToRoot()
- }
- } else {
- if (usersRepository.getUsers().count() > 0) {
- router.setRoot(RouterTransaction.with(ConversationsListView())
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- } else {
- router.setRoot(RouterTransaction.with(ServerSelectionController())
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- }
- }
- }, 7500)
- }
- }
- }
- }
-
- companion object {
-
- const val TAG = "AccountVerificationController"
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt
deleted file mode 100644
index d271566bf..000000000
--- a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt
+++ /dev/null
@@ -1,331 +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 .
- */
-package com.nextcloud.talk.controllers
-
-import android.annotation.SuppressLint
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.net.Uri
-import android.os.Bundle
-import android.security.KeyChain
-import android.text.Editable
-import android.text.TextUtils
-import android.text.TextWatcher
-import android.view.KeyEvent
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
-import android.widget.ProgressBar
-import android.widget.TextView
-import butterknife.BindView
-import butterknife.OnClick
-import com.bluelinelabs.conductor.RouterTransaction
-import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
-import com.nextcloud.talk.R
-import com.nextcloud.talk.api.NcApi
-import com.nextcloud.talk.controllers.base.BaseController
-import com.nextcloud.talk.models.json.generic.Status
-import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
-import com.nextcloud.talk.utils.AccountUtils.findAccounts
-import com.nextcloud.talk.utils.AccountUtils.getAppNameBasedOnPackage
-import com.nextcloud.talk.utils.ApiUtils
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_ACCOUNT_IMPORT
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
-import com.uber.autodispose.AutoDispose
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.schedulers.Schedulers
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.koin.android.ext.android.inject
-import studio.carbonylgroup.textfieldboxes.ExtendedEditText
-import studio.carbonylgroup.textfieldboxes.TextFieldBoxes
-import java.security.cert.CertificateException
-
-class ServerSelectionController : BaseController() {
- @JvmField
- @BindView(R.id.extended_edit_text)
- var serverEntry: ExtendedEditText? = null
- @JvmField
- @BindView(R.id.text_field_boxes)
- var textFieldBoxes: TextFieldBoxes? = null
- @JvmField
- @BindView(R.id.progress_bar)
- var progressBar: ProgressBar? = null
- @JvmField
- @BindView(R.id.helper_text_view)
- var providersTextView: TextView? = null
- @JvmField
- @BindView(R.id.cert_text_view)
- var certTextView: TextView? = null
-
- val usersRepository: UsersRepository by inject()
- val ncApi: NcApi by inject()
-
- override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
- return inflater.inflate(R.layout.controller_server_selection, container, false)
- }
-
- @SuppressLint("LongLogTag")
- @OnClick(R.id.cert_text_view)
- fun onCertClick() {
- if (activity != null) {
- KeyChain.choosePrivateKeyAlias(activity!!, { alias: String? ->
- if (alias != null) {
- appPreferences.temporaryClientCertAlias = alias
- } else {
- appPreferences.removeTemporaryClientCertAlias()
- }
- setCertTextView()
- }, arrayOf("RSA", "EC"), null, null, -1, null)
- }
- }
-
- override fun onViewBound(view: View) {
- super.onViewBound(view)
- if (activity != null) {
- activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- }
- if (actionBar != null) {
- actionBar!!.hide()
- }
- textFieldBoxes!!.endIconImageButton
- .setBackgroundDrawable(resources!!.getDrawable(R.drawable.ic_arrow_forward_white_24px))
- textFieldBoxes!!.endIconImageButton.alpha = 0.5f
- textFieldBoxes!!.endIconImageButton.isEnabled = false
- textFieldBoxes!!.endIconImageButton.visibility = View.VISIBLE
- textFieldBoxes!!.endIconImageButton.setOnClickListener { view1: View? -> checkServerAndProceed() }
- if (TextUtils.isEmpty(resources!!.getString(R.string.nc_providers_url))
- && TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type))) {
- providersTextView!!.visibility = View.INVISIBLE
- } else {
- GlobalScope.launch {
- val users = usersRepository.getUsers()
- val usersSize = users.count()
-
- withContext(Dispatchers.Main) {
- if ((TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type)) ||
- findAccounts(users).isEmpty()) &&
- usersSize == 0) {
- providersTextView!!.setText(R.string.nc_get_from_provider)
- providersTextView!!.setOnClickListener { view12: View? ->
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(resources!!
- .getString(R.string.nc_providers_url)))
- startActivity(browserIntent)
- }
- } else if (findAccounts(users).isNotEmpty()) {
- if (!TextUtils.isEmpty(getAppNameBasedOnPackage(resources!!
- .getString(R.string.nc_import_accounts_from)))) {
- if (findAccounts(users).size > 1) {
- providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_accounts),
- getAppNameBasedOnPackage(resources!!
- .getString(R.string.nc_import_accounts_from)))
- } else {
- providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_account),
- getAppNameBasedOnPackage(resources!!
- .getString(R.string.nc_import_accounts_from)))
- }
- } else {
- if (findAccounts(users).size > 1) {
- providersTextView!!.text = resources!!.getString(R.string.nc_server_import_accounts_plain)
- } else {
- providersTextView!!.text = resources!!.getString(R.string.nc_server_import_account_plain)
- }
- }
- providersTextView!!.setOnClickListener { view13: View? ->
- val bundle = Bundle()
- bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true)
- router.pushController(RouterTransaction.with(
- SwitchAccountController(bundle))
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- }
- } else {
- providersTextView!!.visibility = View.INVISIBLE
- }
- }
- serverEntry!!.requestFocus()
- serverEntry!!.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
- override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
- override fun afterTextChanged(editable: Editable) {
- if (!textFieldBoxes!!.isOnError && !TextUtils.isEmpty(serverEntry!!.text)) {
- toggleProceedButton(true)
- } else {
- toggleProceedButton(false)
- }
- }
- })
- serverEntry!!.setOnEditorActionListener { textView: TextView?, i: Int, keyEvent: KeyEvent? ->
- if (i == EditorInfo.IME_ACTION_DONE) {
- checkServerAndProceed()
- }
- false
- }
- }
- }
- }
-
- private fun toggleProceedButton(show: Boolean) {
- textFieldBoxes!!.endIconImageButton.isEnabled = show
- if (show) {
- textFieldBoxes!!.endIconImageButton.alpha = 1f
- } else {
- textFieldBoxes!!.endIconImageButton.alpha = 0.5f
- }
- }
-
- private fun checkServerAndProceed() {
- var url = serverEntry!!.text.toString().trim { it <= ' ' }
- serverEntry!!.isEnabled = false
- progressBar!!.visibility = View.VISIBLE
- if (providersTextView!!.visibility != View.INVISIBLE) {
- providersTextView!!.visibility = View.INVISIBLE
- certTextView!!.visibility = View.INVISIBLE
- }
- if (url.endsWith("/")) {
- url = url.substring(0, url.length - 1)
- }
- val queryUrl = url + ApiUtils.getUrlPostfixForStatus()
- if (url.startsWith("http://") || url.startsWith("https://")) {
- checkServer(queryUrl, false)
- } else {
- checkServer("https://$queryUrl", true)
- }
- }
-
- private fun checkServer(queryUrl: String, checkForcedHttps: Boolean) {
- ncApi.getServerStatus(queryUrl)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .`as`(AutoDispose.autoDisposable(scopeProvider))
- .subscribe({ status: Status ->
- val productName = resources!!.getString(R.string.nc_server_product_name)
- val versionString: String = status.version.substring(0, status.version.indexOf("."))
- val version = versionString.toInt()
- if (status.installed && !status.maintenance &&
- !status.needsUpgrade && version >= 13) {
- router.pushController(RouterTransaction.with(
- WebViewLoginController(queryUrl.replace("/status.php", ""),
- false))
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- } else if (!status.installed) {
- textFieldBoxes!!.setError(String.format(
- resources!!.getString(R.string.nc_server_not_installed), productName),
- true)
- toggleProceedButton(false)
- } else if (status.needsUpgrade) {
- textFieldBoxes!!.setError(String.format(resources!!.getString(R.string.nc_server_db_upgrade_needed),
- productName), true)
- toggleProceedButton(false)
- } else if (status.maintenance) {
- textFieldBoxes!!.setError(String.format(resources!!.getString(R.string.nc_server_maintenance),
- productName),
- true)
- toggleProceedButton(false)
- } else if (!status.version.startsWith("13.")) {
- textFieldBoxes!!.setError(String.format(resources!!.getString(R.string.nc_server_version),
- resources!!.getString(R.string.nc_app_name)
- , productName), true)
- toggleProceedButton(false)
- }
- }, { throwable: Throwable ->
- if (checkForcedHttps) {
- checkServer(queryUrl.replace("https://", "http://"), false)
- } else {
- if (throwable.localizedMessage != null) {
- textFieldBoxes!!.setError(throwable.localizedMessage, true)
- } else if (throwable.cause is CertificateException) {
- textFieldBoxes!!.setError(resources!!.getString(R.string.nc_certificate_error),
- false)
- }
- if (serverEntry != null) {
- serverEntry!!.isEnabled = true
- }
- progressBar!!.visibility = View.INVISIBLE
- if (providersTextView!!.visibility != View.INVISIBLE) {
- providersTextView!!.visibility = View.VISIBLE
- certTextView!!.visibility = View.VISIBLE
- }
- toggleProceedButton(false)
- }
- }) {
- progressBar!!.visibility = View.INVISIBLE
- if (providersTextView!!.visibility != View.INVISIBLE) {
- providersTextView!!.visibility = View.VISIBLE
- certTextView!!.visibility = View.VISIBLE
- }
- }
- }
-
- override fun onAttach(view: View) {
- super.onAttach(view)
- if (ApplicationWideMessageHolder.getInstance().messageType != null) {
- when (ApplicationWideMessageHolder.getInstance().messageType
- ) {
- ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION -> {
- textFieldBoxes!!.setError(
- resources!!.getString(R.string.nc_account_scheduled_for_deletion),
- false)
- ApplicationWideMessageHolder.getInstance().messageType = null
- }
- ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK -> {
- textFieldBoxes!!.setError(resources!!.getString(R.string.nc_settings_no_talk_installed),
- false)
- }
- ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT -> {
- textFieldBoxes!!.setError(
- resources!!.getString(R.string.nc_server_failed_to_import_account),
- false)
- }
- }
- ApplicationWideMessageHolder.getInstance().messageType = null
- }
- setCertTextView()
- }
-
- private fun setCertTextView() {
- if (activity != null) {
- activity!!.runOnUiThread {
- if (!TextUtils.isEmpty(appPreferences.temporaryClientCertAlias)) {
- certTextView!!.setText(R.string.nc_change_cert_auth)
- } else {
- certTextView!!.setText(R.string.nc_configure_cert_auth)
- }
- textFieldBoxes!!.setError("", true)
- toggleProceedButton(true)
- }
- }
- }
-
- override fun onDestroyView(view: View) {
- super.onDestroyView(view)
- if (activity != null) {
- activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- }
- }
-
- companion object {
- const val TAG = "ServerSelectionController"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt
index e06d3b15e..c66a9e272 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt
@@ -60,6 +60,7 @@ import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.models.RingtoneSettings
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
+import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryView
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.local.models.getCredentials
import com.nextcloud.talk.newarch.local.models.other.UserStatus
@@ -276,7 +277,7 @@ class SettingsController : BaseController() {
} else {
withContext(Dispatchers.Main) {
router.setRoot(RouterTransaction.with(
- ServerSelectionController()
+ ServerEntryView()
)
.pushChangeHandler(VerticalChangeHandler())
.popChangeHandler(VerticalChangeHandler())
@@ -395,7 +396,7 @@ class SettingsController : BaseController() {
addAccountButton!!.addPreferenceClickListener { view15 ->
router.pushController(
- RouterTransaction.with(ServerSelectionController()).pushChangeHandler(
+ RouterTransaction.with(ServerEntryView()).pushChangeHandler(
VerticalChangeHandler()
)
.popChangeHandler(VerticalChangeHandler())
@@ -567,13 +568,6 @@ class SettingsController : BaseController() {
.host
reauthorizeButton!!.addPreferenceClickListener { view14 ->
- router.pushController(
- RouterTransaction.with(
- WebViewLoginController(currentUser!!.baseUrl, true)
- )
- .pushChangeHandler(VerticalChangeHandler())
- .popChangeHandler(VerticalChangeHandler())
- )
}
if (currentUser!!.displayName != null) {
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt
index 34f294a08..0af08c064 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt
@@ -260,9 +260,6 @@ class SwitchAccountController : BaseController {
bundle.putString(BundleKeys.KEY_USERNAME, importAccount.username)
bundle.putString(BundleKeys.KEY_TOKEN, importAccount.token)
bundle.putBoolean(BundleKeys.KEY_IS_ACCOUNT_IMPORT, true)
- router.pushController(RouterTransaction.with(AccountVerificationController(bundle))
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
}
override fun getTitle(): String? {
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt
deleted file mode 100644
index b03472860..000000000
--- a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt
+++ /dev/null
@@ -1,455 +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 .
- */
-package com.nextcloud.talk.controllers
-
-import android.annotation.SuppressLint
-import android.content.pm.ActivityInfo
-import android.net.http.SslError
-import android.os.Build
-import android.os.Bundle
-import android.security.KeyChain
-import android.security.KeyChainException
-import android.text.TextUtils
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.webkit.*
-import android.webkit.WebSettings.RenderPriority.HIGH
-import androidx.appcompat.app.AppCompatActivity
-import androidx.work.OneTimeWorkRequest.Builder
-import androidx.work.WorkManager
-import com.bluelinelabs.conductor.RouterTransaction
-import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
-import com.nextcloud.talk.R.layout
-import com.nextcloud.talk.R.string
-import com.nextcloud.talk.controllers.base.BaseController
-import com.nextcloud.talk.events.CertificateEvent
-import com.nextcloud.talk.jobs.PushRegistrationWorker
-import com.nextcloud.talk.models.LoginData
-import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
-import com.nextcloud.talk.newarch.local.models.other.UserStatus
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_BASE_URL
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ORIGINAL_PROTOCOL
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_TOKEN
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USERNAME
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder.MessageType
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED
-import com.nextcloud.talk.utils.ssl.MagicTrustManager
-import de.cotech.hw.fido.WebViewFidoBridge
-import kotlinx.android.synthetic.main.controller_web_view_login.view.*
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.koin.android.ext.android.inject
-import java.net.CookieManager
-import java.net.URLDecoder
-import java.security.PrivateKey
-import java.security.cert.CertificateException
-import java.security.cert.X509Certificate
-import java.util.*
-
-class WebViewLoginController : BaseController {
- private val PROTOCOL_SUFFIX = "://"
- private val LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":"
-
- val magicTrustManager: MagicTrustManager by inject()
- val cookieManager: CookieManager by inject()
- val usersRepository: UsersRepository by inject()
-
- private var assembledPrefix: String? = null
- private var baseUrl: String? = null
- private var isPasswordUpdate = false
- private var username: String? = null
- private var password: String? = null
- private var loginStep = 0
- private var automatedLoginAttempted = false
- private var webViewFidoBridge: WebViewFidoBridge? = null
-
- constructor(bundle: Bundle)
- constructor(
- baseUrl: String?,
- isPasswordUpdate: Boolean
- ) {
- this.baseUrl = baseUrl
- this.isPasswordUpdate = isPasswordUpdate
- }
-
- constructor(
- baseUrl: String?,
- isPasswordUpdate: Boolean,
- username: String?,
- password: String?
- ) {
- this.baseUrl = baseUrl
- this.isPasswordUpdate = isPasswordUpdate
- this.username = username
- this.password = password
- }
-
- private val webLoginUserAgent: String
- private get() = (Build.MANUFACTURER.substring(0, 1).toUpperCase(
- Locale.getDefault()
- ) +
- Build.MANUFACTURER.substring(1).toLowerCase(
- Locale.getDefault()
- ) + " " + Build.MODEL + " ("
- + resources!!.getString(string.nc_app_name) + ")")
-
- override fun inflateView(
- inflater: LayoutInflater,
- container: ViewGroup
- ): View {
- return inflater.inflate(layout.controller_web_view_login, container, false)
- }
-
- @SuppressLint("SetJavaScriptEnabled")
- override fun onViewBound(view: View) {
- super.onViewBound(view)
- if (activity != null) {
- activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- }
- if (actionBar != null) {
- actionBar!!.hide()
- }
- assembledPrefix =
- resources!!.getString(string.nc_talk_login_scheme) + PROTOCOL_SUFFIX + "login/"
-
- view.webview.apply {
- settings.allowFileAccess = false
- settings.allowFileAccessFromFileURLs = false
- settings.javaScriptEnabled = true
- settings.javaScriptCanOpenWindowsAutomatically = false
- settings.domStorageEnabled = true
- settings.userAgentString = webLoginUserAgent
- settings.saveFormData = false
- settings.savePassword = false
- settings.setRenderPriority(HIGH)
- clearCache(true)
- clearFormData()
- clearHistory()
- clearSslPreferences()
- }
-
- WebView.clearClientCertPreferences(null)
- webViewFidoBridge =
- WebViewFidoBridge.createInstanceForWebView(activity as AppCompatActivity?, view.webview)
- CookieSyncManager.createInstance(activity)
- android.webkit.CookieManager.getInstance()
- .removeAllCookies(null)
- val headers: MutableMap = hashMapOf()
- headers["OCS-APIRequest"] = "true"
-
- view.webview.webViewClient = object : WebViewClient() {
- private var basePageLoaded = false
- override fun shouldInterceptRequest(
- view: WebView,
- request: WebResourceRequest
- ): WebResourceResponse? {
- webViewFidoBridge?.delegateShouldInterceptRequest(view, request)
- return super.shouldInterceptRequest(view, request)
- }
-
- override fun shouldOverrideUrlLoading(
- view: WebView,
- url: String
- ): Boolean {
- if (url.startsWith(assembledPrefix!!)) {
- parseAndLoginFromWebView(url)
- return true
- }
- return false
- }
-
- override fun onPageFinished(
- view: WebView,
- url: String
- ) {
- loginStep++
- if (!basePageLoaded) {
- if (view.progress_bar != null) {
- view.progress_bar!!.visibility = View.GONE
- }
- if (view.webview != null) {
- view.webview.visibility = View.VISIBLE
- }
- basePageLoaded = true
- }
- if (!TextUtils.isEmpty(username)) {
- if (loginStep == 1) {
- view.webview.loadUrl(
- "javascript: {document.getElementsByClassName('login')[0].click(); };"
- )
- } else if (!automatedLoginAttempted) {
- automatedLoginAttempted = true
- if (TextUtils.isEmpty(password)) {
- view.webview.loadUrl(
- "javascript:var justStore = document.getElementById('user').value = '"
- + username
- + "';"
- )
- } else {
- view.webview.loadUrl(
- "javascript: {" +
- "document.getElementById('user').value = '" + username + "';" +
- "document.getElementById('password').value = '" + password + "';" +
- "document.getElementById('submit').click(); };"
- )
- }
- }
- }
- super.onPageFinished(view, url)
- }
-
- override fun onReceivedClientCertRequest(
- view: WebView,
- request: ClientCertRequest
- ) {
- val userEntity = usersRepository.getActiveUser()
- var alias: String? = null
- if (!isPasswordUpdate) {
- alias = appPreferences.temporaryClientCertAlias
- }
- if (TextUtils.isEmpty(alias)) {
- alias = userEntity!!.clientCertificate
- }
- if (!TextUtils.isEmpty(alias)) {
- val finalAlias = alias
- Thread(Runnable {
- try {
- val privateKey =
- KeyChain.getPrivateKey(activity!!, finalAlias!!)
- val certificates =
- KeyChain.getCertificateChain(activity!!, finalAlias)
- if (privateKey != null && certificates != null) {
- request.proceed(privateKey, certificates)
- } else {
- request.cancel()
- }
- } catch (e: KeyChainException) {
- request.cancel()
- } catch (e: InterruptedException) {
- request.cancel()
- }
- })
- .start()
- } else {
- KeyChain.choosePrivateKeyAlias(
- activity!!, { chosenAlias: String? ->
- if (chosenAlias != null) {
- appPreferences.temporaryClientCertAlias = chosenAlias
- Thread(Runnable {
- var privateKey: PrivateKey? = null
- try {
- privateKey = KeyChain.getPrivateKey(activity!!, chosenAlias)
- val certificates =
- KeyChain.getCertificateChain(activity!!, chosenAlias)
- if (privateKey != null && certificates != null) {
- request.proceed(privateKey, certificates)
- } else {
- request.cancel()
- }
- } catch (e: KeyChainException) {
- request.cancel()
- } catch (e: InterruptedException) {
- request.cancel()
- }
- })
- .start()
- } else {
- request.cancel()
- }
- }, arrayOf("RSA", "EC"), null, request.host, request.port, null
- )
- }
- }
-
- override fun onReceivedSslError(
- view: WebView,
- handler: SslErrorHandler,
- error: SslError
- ) {
- try {
- val sslCertificate = error.certificate
- val f =
- sslCertificate.javaClass.getDeclaredField("mX509Certificate")
- f.isAccessible = true
- val cert =
- f[sslCertificate] as X509Certificate
- if (cert == null) {
- handler.cancel()
- } else {
- try {
- magicTrustManager.checkServerTrusted(
- arrayOf(cert), "generic"
- )
- handler.proceed()
- } catch (exception: CertificateException) {
- eventBus.post(CertificateEvent(cert, magicTrustManager, handler))
- }
- }
- } catch (exception: Exception) {
- handler.cancel()
- }
- }
- }
- view.webview.loadUrl("$baseUrl/index.php/login/flow", headers)
- }
-
- private fun parseAndLoginFromWebView(dataString: String) {
- val loginData = parseLoginData(assembledPrefix, dataString)
- if (loginData != null) {
- GlobalScope.launch {
- val targetUser =
- usersRepository.getUserWithUsernameAndServer(loginData.username!!, baseUrl!!)
- var messageType: MessageType? = null
-
- if (!isPasswordUpdate && targetUser != null) {
- messageType = ACCOUNT_UPDATED_NOT_ADDED
- }
-
- if (targetUser != null && UserStatus.PENDING_DELETE == targetUser.status) {
- ApplicationWideMessageHolder.getInstance().messageType = ACCOUNT_SCHEDULED_FOR_DELETION
- if (!isPasswordUpdate) {
- withContext(Dispatchers.Main) {
- router.popToRoot()
- }
- } else {
- withContext(Dispatchers.Main) {
- router.popCurrentController()
- }
- }
- }
-
- val finalMessageType = messageType
- cookieManager.cookieStore.removeAll()
-
- if (!isPasswordUpdate && finalMessageType == null) {
- val bundle = Bundle()
- bundle.putString(KEY_USERNAME, loginData.username)
- bundle.putString(KEY_TOKEN, loginData.token)
- bundle.putString(KEY_BASE_URL, loginData.serverUrl)
- var protocol = ""
- if (baseUrl!!.startsWith("http://")) {
- protocol = "http://"
- } else if (baseUrl!!.startsWith("https://")) {
- protocol = "https://"
- }
- if (!TextUtils.isEmpty(protocol)) {
- bundle.putString(KEY_ORIGINAL_PROTOCOL, protocol)
- }
-
- withContext(Dispatchers.Main) {
- router.pushController(
- RouterTransaction.with(AccountVerificationController(bundle)).pushChangeHandler(
- HorizontalChangeHandler()
- )
- .popChangeHandler(HorizontalChangeHandler())
- )
- }
- } else {
- if (isPasswordUpdate && targetUser != null) {
- targetUser.token = loginData.token
- val updatedRows = usersRepository.updateUser(targetUser)
- if (updatedRows > 0) {
- if (finalMessageType != null) {
- ApplicationWideMessageHolder.getInstance().messageType = finalMessageType
- }
-
- val pushRegistrationWork = Builder(PushRegistrationWorker::class.java).build()
- WorkManager.getInstance()
- .enqueue(pushRegistrationWork)
- withContext(Dispatchers.Main) {
- router.popCurrentController()
- }
- } else {
- // do nothing
- }
- } else {
- if (finalMessageType != null) {
- ApplicationWideMessageHolder.getInstance()
- .messageType = finalMessageType
- }
- withContext(Dispatchers.Main) {
- router.popToRoot()
- }
- }
- }
- }
- }
- }
-
- private fun parseLoginData(
- prefix: String?,
- dataString: String
- ): LoginData? {
- if (dataString.length < prefix!!.length) {
- return null
- }
- val loginData = LoginData()
- // format is xxx://login/server:xxx&user:xxx&password:xxx
- val data = dataString.substring(prefix.length)
- val values = data.split("&")
- .toTypedArray()
- if (values.size != 3) {
- return null
- }
- for (value in values) {
- if (value.startsWith("user$LOGIN_URL_DATA_KEY_VALUE_SEPARATOR")) {
- loginData.username = URLDecoder.decode(
- value.substring("user$LOGIN_URL_DATA_KEY_VALUE_SEPARATOR".length)
- )
- } else if (value.startsWith("password$LOGIN_URL_DATA_KEY_VALUE_SEPARATOR")) {
- loginData.token = URLDecoder.decode(
- value.substring("password$LOGIN_URL_DATA_KEY_VALUE_SEPARATOR".length)
- )
- } else if (value.startsWith("server$LOGIN_URL_DATA_KEY_VALUE_SEPARATOR")) {
- loginData.serverUrl = URLDecoder.decode(
- value.substring("server$LOGIN_URL_DATA_KEY_VALUE_SEPARATOR".length)
- )
- } else {
- return null
- }
- }
- return if (!TextUtils.isEmpty(loginData.serverUrl)
- && !TextUtils.isEmpty(loginData.username)
- &&
- !TextUtils.isEmpty(loginData.token)
- ) {
- loginData
- } else {
- null
- }
- }
-
- override fun onDestroyView(view: View) {
- super.onDestroyView(view)
- if (activity != null) {
- activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- }
- }
-
- companion object {
- const val TAG = "WebViewLoginController"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt
index 3bd75f302..eeb241ec4 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt
@@ -42,10 +42,7 @@ import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MainActivity
-import com.nextcloud.talk.controllers.AccountVerificationController
-import com.nextcloud.talk.controllers.ServerSelectionController
import com.nextcloud.talk.controllers.SwitchAccountController
-import com.nextcloud.talk.controllers.WebViewLoginController
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
import com.nextcloud.talk.utils.FABAwareScrollingViewBehavior
import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -137,9 +134,6 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
private fun cleanTempCertPreference() {
val temporaryClassNames = ArrayList()
- temporaryClassNames.add(ServerSelectionController::class.java.name)
- temporaryClassNames.add(AccountVerificationController::class.java.name)
- temporaryClassNames.add(WebViewLoginController::class.java.name)
temporaryClassNames.add(SwitchAccountController::class.java.name)
if (!temporaryClassNames.contains(javaClass.name)) {
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt
deleted file mode 100644
index 864a388fe..000000000
--- a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Nextcloud Talk application
- *
- * @author Mario Danic
- * Copyright (C) 2017 Mario Danic
- *
- * 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 .
- */
-
-package com.nextcloud.talk.jobs
-
-import android.annotation.SuppressLint
-import android.app.Application
-import android.content.Context
-import android.util.Base64
-import androidx.work.CoroutineWorker
-import androidx.work.ListenableWorker.Result
-import androidx.work.WorkerParameters
-import com.nextcloud.talk.R
-import com.nextcloud.talk.api.NcApi
-import com.nextcloud.talk.events.EventStatus
-import com.nextcloud.talk.models.json.push.PushConfigurationState
-import com.nextcloud.talk.models.json.push.PushRegistrationOverall
-import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
-import com.nextcloud.talk.newarch.local.models.getCredentials
-import com.nextcloud.talk.newarch.utils.hashWithAlgorithm
-import com.nextcloud.talk.utils.ApiUtils
-import com.nextcloud.talk.utils.PushUtils
-import com.nextcloud.talk.utils.preferences.AppPreferences
-import io.reactivex.Observer
-import io.reactivex.disposables.Disposable
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import org.greenrobot.eventbus.EventBus
-import org.koin.core.KoinComponent
-import org.koin.core.inject
-import java.security.PublicKey
-import java.util.*
-
-class PushRegistrationWorker(
- context: Context,
- workerParams: WorkerParameters
-) : CoroutineWorker(context, workerParams), KoinComponent {
-
- val usersRepository: UsersRepository by inject()
- val eventBus: EventBus by inject()
- val appPreferences: AppPreferences by inject()
- val application: Application by inject()
- val ncApi: NcApi by inject()
-
- override suspend fun doWork(): Result {
- val pushUtils = PushUtils(usersRepository)
- pushUtils.generateRsa2048KeyPair()
- pushRegistrationToServer()
- return Result.success()
- }
-
- private fun pushRegistrationToServer() {
- val token: String? = appPreferences.pushToken
- if (!token.isNullOrEmpty()) {
- var credentials: String
- val pushUtils = PushUtils(usersRepository)
- val pushTokenHash = token.hashWithAlgorithm("SHA-512")
- val devicePublicKey = pushUtils.readKeyFromFile(true) as PublicKey?
- if (devicePublicKey != null) {
- val publicKeyBytes: ByteArray? =
- Base64.encode(devicePublicKey.encoded, Base64.NO_WRAP)
- var publicKey = String(publicKeyBytes!!)
- publicKey = publicKey.replace("(.{64})".toRegex(), "$1\n")
- publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey\n-----END PUBLIC KEY-----\n"
- val users = usersRepository.getUsers()
- if (users.count() > 0) {
- var accountPushData: PushConfigurationState?
- for (userEntityObject in users) {
- accountPushData = userEntityObject.pushConfiguration
- if (accountPushData == null || accountPushData.pushToken != token) {
- val queryMap: MutableMap =
- HashMap()
- queryMap["format"] = "json"
- queryMap["pushTokenHash"] = pushTokenHash
- queryMap["devicePublicKey"] = publicKey
- queryMap["proxyServer"] = application.getString(R.string.nc_push_server_url)
- credentials = userEntityObject.getCredentials()
- ncApi.registerDeviceForNotificationsWithNextcloud(
- credentials,
- ApiUtils.getUrlNextcloudPush(userEntityObject.baseUrl),
- queryMap
- )
- .blockingSubscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {}
- @SuppressLint("CheckResult")
- override fun onNext(pushRegistrationOverall: PushRegistrationOverall) {
- val proxyMap: MutableMap =
- HashMap()
- proxyMap["pushToken"] = token
- proxyMap["deviceIdentifier"] =
- pushRegistrationOverall.ocs.data.deviceIdentifier
- proxyMap["deviceIdentifierSignature"] = pushRegistrationOverall.ocs
- .data.signature
- proxyMap["userPublicKey"] = pushRegistrationOverall.ocs
- .data.publicKey
- ncApi.registerDeviceForNotificationsWithProxy(
- ApiUtils.getUrlPushProxy(), proxyMap
- ).subscribe({
- val pushConfigurationState = PushConfigurationState()
- pushConfigurationState.pushToken = token
- pushConfigurationState.deviceIdentifier = proxyMap["deviceIdentifier"]
- pushConfigurationState.deviceIdentifierSignature = proxyMap["deviceIdentifierSignature"]
- pushConfigurationState.userPublicKey = proxyMap["userPublicKey"]
- pushConfigurationState.usesRegularPass = false
- GlobalScope.launch {
- val user = usersRepository.getUserWithId(userEntityObject.id!!)
- user.pushConfiguration = pushConfigurationState
- usersRepository.updateUser(user)
- }
-
- eventBus.post(
- EventStatus(
- userEntityObject.id!!,
- EventStatus.EventType.PUSH_REGISTRATION,
- true
- )
- )
- }, {
- eventBus.post(
- EventStatus(
- userEntityObject.id!!,
- EventStatus.EventType.PUSH_REGISTRATION,
- false))
-
- })
- }
-
- override fun onError(e: Throwable) {
- eventBus.post(
- EventStatus(
- userEntityObject.id!!,
- EventStatus.EventType.PUSH_REGISTRATION,
- false
- )
- )
- }
-
- override fun onComplete() {}
- })
- }
- }
- }
- }
- }
- }
-
- companion object {
- const val TAG = "PushRegistrationWorker"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt b/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfiguration.kt
similarity index 64%
rename from app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt
rename to app/src/main/java/com/nextcloud/talk/models/json/push/PushConfiguration.kt
index 98f42bdf1..62e1e5043 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfiguration.kt
@@ -21,9 +21,10 @@
package com.nextcloud.talk.models.json.push
import android.os.Parcelable
-import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
import lombok.Data
import org.parceler.Parcel
@@ -31,30 +32,31 @@ import org.parceler.Parcel
@Data
@JsonObject
@Parcelize
-data class PushConfigurationState(
- @JsonField(name = ["pushToken"])
+@Serializable
+data class PushConfiguration(
+ @SerialName("pushToken")
var pushToken: String? = null,
- @JsonField(name = ["deviceIdentifier"])
+ @SerialName("deviceIdentifier")
var deviceIdentifier: String? = null,
- @JsonField(name = ["deviceIdentifierSignature"])
+ @SerialName("deviceIdentifierSignature")
var deviceIdentifierSignature: String? = null,
- @JsonField(name = ["userPublicKey"])
+ @SerialName("userPublicKey")
var userPublicKey: String? = null,
- @JsonField(name = ["usesRegularPass"])
- var usesRegularPass: Boolean = false
+ @SerialName("state")
+ var pushConfigurationStateWrapper: PushConfigurationStateWrapper? = null
) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
- other as PushConfigurationState
+ other as PushConfiguration
if (pushToken != other.pushToken) return false
if (deviceIdentifier != other.deviceIdentifier) return false
if (deviceIdentifierSignature != other.deviceIdentifierSignature) return false
if (userPublicKey != other.userPublicKey) return false
- if (usesRegularPass != other.usesRegularPass) return false
+ if (pushConfigurationStateWrapper != other.pushConfigurationStateWrapper) return false
return true
}
@@ -64,7 +66,27 @@ data class PushConfigurationState(
result = 31 * result + (deviceIdentifier?.hashCode() ?: 0)
result = 31 * result + (deviceIdentifierSignature?.hashCode() ?: 0)
result = 31 * result + (userPublicKey?.hashCode() ?: 0)
- result = 31 * result + usesRegularPass.hashCode()
+ result = 31 * result + pushConfigurationStateWrapper.hashCode()
return result
}
-}
\ No newline at end of file
+}
+
+enum class PushConfigurationState {
+ PENDING,
+ SERVER_REGISTRATION_DONE,
+ PROXY_REGISTRATION_DONE,
+ FAILED_WITH_SERVER_REGISTRATION,
+ FAILED_WITH_PROXY_REGISTRATION,
+ PENDING_UNREGISTRATION,
+ SERVER_UNREGISTRATION_DONE,
+ PROXY_UNREGISTRATION_DONE
+}
+
+@Serializable
+@Parcelize
+data class PushConfigurationStateWrapper(
+ @SerialName("pushConfigurationState")
+ var pushConfigurationState: PushConfigurationState,
+ @SerialName("reason")
+ var reason: Int?
+): Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.kt b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.kt
index 4ba215fff..f455ed7fb 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.kt
@@ -23,6 +23,7 @@ import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import lombok.Data
@@ -33,14 +34,18 @@ import lombok.Data
data class IceServer @JvmOverloads constructor(
@JvmField
@JsonField(name = ["url"])
+ @SerialName("url")
var url: String? = null,
@JvmField
@JsonField(name = ["urls"])
+ @SerialName("urls")
var urls: List? = null,
@JvmField
@JsonField(name = ["username"])
+ @SerialName("username")
var username: String? = null,
@JvmField
@JsonField(name = ["credential"])
+ @SerialName("credential")
var credential: String? = null
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettings.kt b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettings.kt
index 142024da1..acc33259e 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettings.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettings.kt
@@ -23,6 +23,7 @@ import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import lombok.Data
@@ -33,14 +34,18 @@ import lombok.Data
data class SignalingSettings @JvmOverloads constructor(
@JvmField
@JsonField(name = ["stunservers"])
+ @SerialName("stunservers")
var stunServers: List? = null,
@JvmField
@JsonField(name = ["turnservers"])
+ @SerialName("turnservers")
var turnServers: List? = null,
@JvmField
@JsonField(name = ["server"])
+ @SerialName("server")
var externalSignalingServer: String? = null,
@JvmField
@JsonField(name = ["ticket"])
+ @SerialName("ticket")
var externalSignalingTicket: String? = null
) : Parcelable
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt
index 74f22e188..ad6df77dc 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt
@@ -54,6 +54,10 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
return usersDao.updateUser(user)
}
+ override suspend fun insertUser(user: UserNgEntity): Long {
+ return usersDao.saveUser(user)
+ }
+
override suspend fun setUserAsActiveWithId(id: Long) {
usersDao.setUserAsActiveWithId(id)
}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt
index 88fd0d4e4..f7568c321 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt
@@ -57,7 +57,6 @@ import okhttp3.logging.HttpLoggingInterceptor.Logger
import org.koin.android.ext.koin.androidApplication
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
-import org.mozilla.geckoview.GeckoRuntime
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import java.io.IOException
@@ -73,7 +72,6 @@ import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509KeyManager
val NetworkModule = module {
- single { createGeckoRuntime(androidContext()) }
single { createService(get()) }
single { createLegacyNcApi(get()) }
single { createRetrofit(get()) }
@@ -91,10 +89,6 @@ val NetworkModule = module {
}
-fun createGeckoRuntime(context: Context): GeckoRuntime {
- return GeckoRuntime.create(context)
-}
-
fun createCookieManager(): CookieManager {
val cookieManager = CookieManager()
cookieManager.setCookiePolicy(ACCEPT_ALL)
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/di/module/UseCasesModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/di/module/UseCasesModule.kt
index c60d7a98b..615da8361 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/domain/di/module/UseCasesModule.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/di/module/UseCasesModule.kt
@@ -43,9 +43,38 @@ val UseCasesModule = module {
single { createGetProfileUseCase(get(), get()) }
single { createGetSignalingUseCase(get(), get()) }
single { createGetCapabilitiesUseCase(get(), get()) }
+ single { createRegisterPushWithProxyUseCase(get(), get()) }
+ single { createRegisterPushWithServerUseCase(get(), get()) }
+ single { createUnregisterPushWithProxyUseCase(get(), get()) }
+ single { createUnregisterPushWithServerUseCase(get(), get()) }
factory { createChatViewModelFactory(get(), get(), get(), get(), get(), get()) }
}
+fun createUnregisterPushWithServerUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler
+): UnregisterPushWithServerUseCase {
+ return UnregisterPushWithServerUseCase(nextcloudTalkRepository, apiErrorHandler)
+}
+
+fun createUnregisterPushWithProxyUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler
+): UnregisterPushWithProxyUseCase {
+ return UnregisterPushWithProxyUseCase(nextcloudTalkRepository, apiErrorHandler)
+}
+
+
+fun createRegisterPushWithServerUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler
+): RegisterPushWithServerUseCase {
+ return RegisterPushWithServerUseCase(nextcloudTalkRepository, apiErrorHandler)
+}
+
+fun createRegisterPushWithProxyUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler
+): RegisterPushWithProxyUseCase {
+ return RegisterPushWithProxyUseCase(nextcloudTalkRepository, apiErrorHandler)
+}
+
fun createGetCapabilitiesUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): GetCapabilitiesUseCase {
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt
index 4b4fad643..a0b431126 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt
@@ -30,6 +30,7 @@ interface UsersRepository {
fun getUserWithId(id: Long): UserNgEntity
suspend fun getUserWithUsernameAndServer(username: String, server: String): UserNgEntity?
suspend fun updateUser(user: UserNgEntity): Int
+ suspend fun insertUser(user: UserNgEntity): Long
suspend fun setUserAsActiveWithId(id: Long)
suspend fun deleteUserWithId(id: Long)
suspend fun setAnyUserAsActive(): Boolean
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt
index d053f6ae3..ac0051820 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt
@@ -34,7 +34,6 @@ interface NextcloudTalkRepository {
suspend fun unregisterPushWithServerForUser(user: UserNgEntity): GenericOverall
suspend fun registerPushWithProxyForUser(user: UserNgEntity, options: Map): Any
suspend fun unregisterPushWithProxyForUser(user: UserNgEntity, options: Map): Any
-
suspend fun getSignalingSettingsForUser(user: UserNgEntity): SignalingSettingsOverall
suspend fun getProfileForUser(user: UserNgEntity): UserProfileOverall
suspend fun getConversationsForUser(user: UserNgEntity): List
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/RegisterPushWithProxyUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/RegisterPushWithProxyUseCase.kt
new file mode 100644
index 000000000..2ebe2cf26
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/RegisterPushWithProxyUseCase.kt
@@ -0,0 +1,39 @@
+/*
+ *
+ * * Nextcloud Talk application
+ * *
+ * * @author Mario Danic
+ * * Copyright (C) 2017-2020 Mario Danic
+ * *
+ * * 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 .
+ *
+ */
+
+package com.nextcloud.talk.newarch.domain.usecases
+
+import com.nextcloud.talk.models.json.push.PushRegistrationOverall
+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 RegisterPushWithProxyUseCase constructor(
+ private val nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler?
+) : UseCase(apiErrorHandler) {
+ override suspend fun run(params: Any?): Any {
+ val definitionParameters = params as DefinitionParameters
+ return nextcloudTalkRepository.registerPushWithProxyForUser(definitionParameters[0], definitionParameters[1])
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/RegisterPushWithServerUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/RegisterPushWithServerUseCase.kt
new file mode 100644
index 000000000..c056354ce
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/RegisterPushWithServerUseCase.kt
@@ -0,0 +1,39 @@
+/*
+ *
+ * * Nextcloud Talk application
+ * *
+ * * @author Mario Danic
+ * * Copyright (C) 2017-2020 Mario Danic
+ * *
+ * * 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 .
+ *
+ */
+
+package com.nextcloud.talk.newarch.domain.usecases
+
+import com.nextcloud.talk.models.json.push.PushRegistrationOverall
+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 RegisterPushWithServerUseCase constructor(
+ private val nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler?
+) : UseCase(apiErrorHandler) {
+ override suspend fun run(params: Any?): PushRegistrationOverall {
+ val definitionParameters = params as DefinitionParameters
+ return nextcloudTalkRepository.registerPushWithServerForUser(definitionParameters[0], definitionParameters[1])
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt
index a706f3b63..0dd1fe2d4 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt
@@ -34,9 +34,9 @@ class SetConversationFavoriteValueUseCase constructor(
override suspend fun run(params: Any?): GenericOverall {
val definitionParameters = params as DefinitionParameters
return nextcloudTalkRepository.setFavoriteValueForConversation(
- definitionParameters.get(0),
- definitionParameters.get(1),
- definitionParameters.get(2)
+ definitionParameters[0],
+ definitionParameters[1],
+ definitionParameters[2]
)
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/UnregisterPushWithProxyUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/UnregisterPushWithProxyUseCase.kt
new file mode 100644
index 000000000..908796f13
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/UnregisterPushWithProxyUseCase.kt
@@ -0,0 +1,38 @@
+/*
+ *
+ * * Nextcloud Talk application
+ * *
+ * * @author Mario Danic
+ * * Copyright (C) 2017-2020 Mario Danic
+ * *
+ * * 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 .
+ *
+ */
+
+package com.nextcloud.talk.newarch.domain.usecases
+
+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 UnregisterPushWithProxyUseCase constructor(
+ private val nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler?
+) : UseCase(apiErrorHandler) {
+ override suspend fun run(params: Any?): Any {
+ val definitionParameters = params as DefinitionParameters
+ return nextcloudTalkRepository.unregisterPushWithProxyForUser(definitionParameters[0], definitionParameters[1])
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/UnregisterPushWithServerUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/UnregisterPushWithServerUseCase.kt
new file mode 100644
index 000000000..db9717cb9
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/UnregisterPushWithServerUseCase.kt
@@ -0,0 +1,39 @@
+/*
+ *
+ * * Nextcloud Talk application
+ * *
+ * * @author Mario Danic
+ * * Copyright (C) 2017-2020 Mario Danic
+ * *
+ * * 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 .
+ *
+ */
+
+package com.nextcloud.talk.newarch.domain.usecases
+
+import com.nextcloud.talk.models.json.generic.GenericOverall
+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 UnregisterPushWithServerUseCase constructor(
+ private val nextcloudTalkRepository: NextcloudTalkRepository,
+ apiErrorHandler: ApiErrorHandler?
+) : UseCase(apiErrorHandler) {
+ override suspend fun run(params: Any?): GenericOverall {
+ val definitionParameters = params as DefinitionParameters
+ return nextcloudTalkRepository.unregisterPushWithServerForUser(definitionParameters[0])
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/account/di/module/AccountModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/account/di/module/AccountModule.kt
index fa668746e..f4dade958 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/features/account/di/module/AccountModule.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/features/account/di/module/AccountModule.kt
@@ -2,9 +2,7 @@ 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.domain.usecases.GetSignalingSettingsUseCase
+import com.nextcloud.talk.newarch.domain.usecases.*
import com.nextcloud.talk.newarch.features.account.loginentry.LoginEntryViewModelFactory
import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryViewModelFactory
import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -18,7 +16,7 @@ val AccountModule = module {
)
}
factory {
- createLoginEntryViewModelFactory(androidApplication(), get(), get(), get(), get(), get() )
+ createLoginEntryViewModelFactory(androidApplication(), get(), get(), get(), get(), get(), get(), get())
}
}
@@ -36,10 +34,12 @@ fun createLoginEntryViewModelFactory(
getProfileUseCase: GetProfileUseCase,
getCapabilitiesUseCase: GetCapabilitiesUseCase,
getSignalingSettingsUseCase: GetSignalingSettingsUseCase,
+ registerPushWithServerUseCase: RegisterPushWithServerUseCase,
+ registerPushWithProxyUseCase: RegisterPushWithProxyUseCase,
appPreferences: AppPreferences,
usersRepository: UsersRepository
): LoginEntryViewModelFactory {
return LoginEntryViewModelFactory(
- application, getProfileUseCase, getCapabilitiesUseCase, getSignalingSettingsUseCase, appPreferences, usersRepository
+ application, getProfileUseCase, getCapabilitiesUseCase, getSignalingSettingsUseCase, registerPushWithServerUseCase, registerPushWithProxyUseCase, appPreferences, usersRepository
)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryView.kt
index 6c14043fa..acb88aa03 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryView.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryView.kt
@@ -22,11 +22,14 @@
package com.nextcloud.talk.newarch.features.account.loginentry
+import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.webkit.*
+import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import com.bluelinelabs.conductor.RouterTransaction
@@ -35,10 +38,9 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView
import com.nextcloud.talk.utils.bundle.BundleKeys
+import de.cotech.hw.fido.WebViewFidoBridge
import kotlinx.android.synthetic.main.login_entry_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() {
@@ -48,11 +50,7 @@ class LoginEntryView(val bundle: Bundle) : BaseView() {
private lateinit var viewModel: LoginEntryViewModel
val factory: LoginEntryViewModelFactory by inject()
- private lateinit var geckoView: GeckoView
- private lateinit var geckoSession: GeckoSession
- private val geckoRuntime: GeckoRuntime by inject()
-
- private val assembledPrefix = resources?.getString(R.string.nc_talk_login_scheme) + protocolSuffix + "login/"
+ private var assembledPrefix = ""
private val webLoginUserAgent: String
get() = (Build.MANUFACTURER.substring(0, 1).toUpperCase(
@@ -65,11 +63,14 @@ class LoginEntryView(val bundle: Bundle) : BaseView() {
return R.layout.login_entry_view
}
+ @SuppressLint("SetJavaScriptEnabled")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
actionBar?.hide()
viewModel = viewModelProvider(factory).get(LoginEntryViewModel::class.java)
val view = super.onCreateView(inflater, container)
+ assembledPrefix = resources?.getString(R.string.nc_talk_login_scheme) + protocolSuffix + "login/"
+
viewModel.state.observe(this@LoginEntryView, Observer {
when (it.state) {
LoginEntryState.FAILED -> {
@@ -80,82 +81,83 @@ class LoginEntryView(val bundle: Bundle) : BaseView() {
}
LoginEntryState.CHECKING -> {
view.progressBar.isVisible = true
- geckoView.isVisible = false
+ view.webView.isVisible = false
}
else -> {
- if (router?.hasRootController() == true) {
- router.popController(this)
- } else {
- router.setRoot(RouterTransaction.with(ConversationsListView())
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
- }
- // all good, proceed
+ router.setRoot(RouterTransaction.with(ConversationsListView())
+ .pushChangeHandler(HorizontalChangeHandler())
+ .popChangeHandler(HorizontalChangeHandler()))
}
}
})
- geckoView = view.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())
- geckoSession.open(geckoRuntime)
- 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("OCS-APIRequest" to "true"))
- }
- }
+
+ val baseUrl = bundle.get(BundleKeys.KEY_BASE_URL)
+ val headers: MutableMap = hashMapOf()
+ headers["OCS-APIRequest"] = "true"
+
+ setupWebView(view)
+ view.webView.loadUrl("$baseUrl/index.php/login/flow", headers)
return view
}
- private fun createNavigationDelegate(): GeckoSession.NavigationDelegate {
- return object : GeckoSession.NavigationDelegate {
- override fun onLoadRequest(p0: GeckoSession, p1: GeckoSession.NavigationDelegate.LoadRequest): GeckoResult? {
- if (p1.uri.startsWith(assembledPrefix)) {
- viewModel.parseData(assembledPrefix, dataSeparator, p1.uri)
- return GeckoResult.DENY
+ override fun onSaveViewState(view: View, outState: Bundle) {
+ view.webView.saveState(outState)
+ super.onSaveViewState(view, outState)
+ }
+
+ override fun onRestoreViewState(view: View, savedViewState: Bundle) {
+ super.onRestoreViewState(view, savedViewState)
+ view.webView.restoreState(savedViewState)
+ }
+
+ private fun setupWebView(loginEntryView: View) {
+ loginEntryView.webView.apply {
+ settings.allowFileAccess = false
+ settings.allowFileAccessFromFileURLs = false
+ settings.javaScriptEnabled = true
+ settings.javaScriptCanOpenWindowsAutomatically = false
+ settings.domStorageEnabled = true
+ settings.userAgentString = webLoginUserAgent
+ settings.saveFormData = false
+ settings.savePassword = false
+ settings.setRenderPriority(WebSettings.RenderPriority.HIGH)
+ clearCache(true)
+ clearFormData()
+ clearHistory()
+ clearSslPreferences()
+ }
+
+ val webViewFidoBridge = WebViewFidoBridge.createInstanceForWebView(activity as AppCompatActivity?, loginEntryView.webView)
+ CookieSyncManager.createInstance(activity)
+ CookieManager.getInstance().removeAllCookies(null)
+
+ loginEntryView.webView.webViewClient = object : WebViewClient() {
+ var initialPageLoad = true
+ override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
+ webViewFidoBridge?.delegateShouldInterceptRequest(view, request)
+ return super.shouldInterceptRequest(view, request)
+ }
+
+ override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
+ if (request?.url.toString().startsWith(assembledPrefix)) {
+ viewModel.parseData(assembledPrefix, dataSeparator, request?.url.toString())
+ return true
}
- return super.onLoadRequest(p0, p1)
+ return super.shouldOverrideUrlLoading(view, request)
+ }
+
+ override fun onPageFinished(view: WebView?, url: String?) {
+ if (initialPageLoad) {
+ initialPageLoad = false
+ loginEntryView.progressBar?.isVisible = false
+ loginEntryView.webView?.isVisible = true
+ }
+ super.onPageFinished(view, url)
}
}
}
- private fun createProgressDelegate(): GeckoSession.ProgressDelegate {
- return object : GeckoSession.ProgressDelegate {
- private var initialLoad = true
-
- 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) {
- if (initialLoad) {
- view?.pageProgressBar?.progress = progress
- view?.pageProgressBar?.isVisible = progress in 1..99
- }
-
- if (progress == 100) {
- initialLoad = false
- view?.pageProgressBar?.isVisible = false
- view?.geckoView?.isVisible = true
- }
- }
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModel.kt
index 456ddb73c..357a9803b 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModel.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModel.kt
@@ -5,16 +5,19 @@ 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.push.PushConfiguration
+import com.nextcloud.talk.models.json.push.PushConfigurationState
+import com.nextcloud.talk.models.json.push.PushConfigurationStateWrapper
+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.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.*
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.local.models.UserNgEntity
+import com.nextcloud.talk.utils.PushUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import kotlinx.coroutines.launch
import org.koin.core.parameter.parametersOf
@@ -25,12 +28,15 @@ class LoginEntryViewModel constructor(
private val getProfileUseCase: GetProfileUseCase,
private val getCapabilitiesUseCase: GetCapabilitiesUseCase,
private val getSignalingSettingsUseCase: GetSignalingSettingsUseCase,
+ private val registerPushWithServerUseCase: RegisterPushWithServerUseCase,
+ private val registerPushWithProxyUseCase: RegisterPushWithProxyUseCase,
private val appPreferences: AppPreferences,
private val usersRepository: UsersRepository) :
BaseViewModel(application) {
val state: MutableLiveData = MutableLiveData(LoginEntryStateWrapper(LoginEntryState.PENDING_CHECK, null))
- private val user = UserNgEntity(-1, "-1", "", "")
+ private var user = UserNgEntity(-1, "-1", "", "")
+ private var updatingUser = false
fun parseData(prefix: String, separator: String, data: String?) {
viewModelScope.launch {
@@ -88,10 +94,13 @@ class LoginEntryViewModel constructor(
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) {
+ val userIfExists = usersRepository.getUserWithUsernameAndServer(loginData.username!!, loginData.serverUrl!!)
+ if (userIfExists != null) {
+ updatingUser = true
+ user = userIfExists
user.token = loginData.token
usersRepository.updateUser(user)
+ // complicated - we need to unregister, etc, etc, but not yet
state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.ACCOUNT_UPDATED))
} else {
getProfile(loginData)
@@ -101,6 +110,7 @@ class LoginEntryViewModel constructor(
private fun getProfile(loginData: LoginData) {
user.username = loginData.username!!
user.baseUrl = loginData.serverUrl!!
+ user.token = loginData.token
getProfileUseCase.invoke(viewModelScope, parametersOf(user), object : UseCaseResponse {
override suspend fun onSuccess(result: UserProfileOverall) {
result.ocs.data.userId?.let { userId ->
@@ -135,6 +145,10 @@ class LoginEntryViewModel constructor(
getSignalingSettingsUseCase.invoke(viewModelScope, parametersOf(user), object : UseCaseResponse {
override suspend fun onSuccess(result: SignalingSettingsOverall) {
user.signalingSettings = result.ocs.signalingSettings
+ val pushConfiguration = PushConfiguration()
+ val pushConfigurationStateWrapper = PushConfigurationStateWrapper(PushConfigurationState.PENDING, 0)
+ pushConfiguration.pushConfigurationStateWrapper = pushConfigurationStateWrapper
+ usersRepository.insertUser(user)
registerForPush()
}
@@ -142,12 +156,13 @@ class LoginEntryViewModel constructor(
state.postValue(LoginEntryStateWrapper(LoginEntryState.FAILED, LoginEntryStateClarification.SIGNALING_SETTINGS_FETCH_FAILED))
}
})
-
}
- private fun registerForPush() {
+ private suspend fun registerForPush() {
val token = appPreferences.pushToken
if (!token.isNullOrBlank()) {
+ user.pushConfiguration?.pushToken = token
+ usersRepository.updateUser(user)
registerForPushWithServer(token)
} else {
state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.PUSH_REGISTRATION_MISSING_TOKEN))
@@ -155,10 +170,57 @@ class LoginEntryViewModel constructor(
}
private fun registerForPushWithServer(token: String) {
+ val options = PushUtils(usersRepository).getMapForPushRegistrationWithServer(context, token)
+ registerPushWithServerUseCase.invoke(viewModelScope, parametersOf(user, options), object : UseCaseResponse {
+ override suspend fun onSuccess(result: PushRegistrationOverall) {
+ user.pushConfiguration?.deviceIdentifier = result.ocs.data.deviceIdentifier
+ user.pushConfiguration?.deviceIdentifierSignature = result.ocs.data.signature
+ user.pushConfiguration?.userPublicKey = result.ocs.data.publicKey
+ user.pushConfiguration?.pushConfigurationStateWrapper = PushConfigurationStateWrapper(PushConfigurationState.SERVER_REGISTRATION_DONE, null)
+ usersRepository.updateUser(user)
+ registerForPushWithProxy()
+ }
+ override suspend fun onError(errorModel: ErrorModel?) {
+ user.pushConfiguration?.pushConfigurationStateWrapper?.pushConfigurationState = PushConfigurationState.FAILED_WITH_SERVER_REGISTRATION
+ user.pushConfiguration?.pushConfigurationStateWrapper?.reason = errorModel?.code
+ usersRepository.updateUser(user)
+ state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.PUSH_REGISTRATION_WITH_SERVER_FAILED))
+ }
+ })
}
- private fun registerForPushWithProxy() {
+ private suspend fun registerForPushWithProxy() {
+ val options = PushUtils(usersRepository).getMapForPushRegistrationWithServer(user)
+ if (options != null) {
+ registerPushWithProxyUseCase.invoke(viewModelScope, parametersOf(user, options), object : UseCaseResponse {
+ override suspend fun onSuccess(result: Any) {
+ user.pushConfiguration?.pushConfigurationStateWrapper = PushConfigurationStateWrapper(PushConfigurationState.PROXY_REGISTRATION_DONE, null)
+ usersRepository.updateUser(user)
+ state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, if (!updatingUser) LoginEntryStateClarification.ACCOUNT_CREATED else LoginEntryStateClarification.ACCOUNT_UPDATED))
+ }
+
+ override suspend fun onError(errorModel: ErrorModel?) {
+ user.pushConfiguration?.pushConfigurationStateWrapper?.pushConfigurationState = PushConfigurationState.FAILED_WITH_PROXY_REGISTRATION
+ user.pushConfiguration?.pushConfigurationStateWrapper?.reason = errorModel?.code
+ usersRepository.updateUser(user)
+ state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.PUSH_REGISTRATION_WITH_PUSH_PROXY_FAILED))
+ }
+ })
+ } else {
+ user.pushConfiguration?.pushConfigurationStateWrapper?.pushConfigurationState = PushConfigurationState.FAILED_WITH_PROXY_REGISTRATION
+ usersRepository.updateUser(user)
+ state.postValue(LoginEntryStateWrapper(LoginEntryState.OK, LoginEntryStateClarification.PUSH_REGISTRATION_WITH_PUSH_PROXY_FAILED))
+ }
+ }
+
+ private suspend fun setAdjustedUserAsActive() {
+ if (user.id == -1L) {
+ val adjustedUser = usersRepository.getUserWithUsernameAndServer(user.username, user.baseUrl)
+ adjustedUser?.id?.let {
+ usersRepository.setUserAsActiveWithId(it)
+ }
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModelFactory.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModelFactory.kt
index 7182cbb2e..74ac7c425 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModelFactory.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/features/account/loginentry/LoginEntryViewModelFactory.kt
@@ -4,13 +4,11 @@ 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
+import com.nextcloud.talk.newarch.domain.usecases.*
import com.nextcloud.talk.utils.preferences.AppPreferences
-class LoginEntryViewModelFactory constructor(private val application: Application, private val getProfileUseCase: GetProfileUseCase, private val getCapabilitiesUseCase: GetCapabilitiesUseCase, private val getSignalingSettingsUseCase: GetSignalingSettingsUseCase, private val appPreferences: AppPreferences, private val usersRepository: UsersRepository) : ViewModelProvider.Factory {
+class LoginEntryViewModelFactory constructor(private val application: Application, private val getProfileUseCase: GetProfileUseCase, private val getCapabilitiesUseCase: GetCapabilitiesUseCase, private val getSignalingSettingsUseCase: GetSignalingSettingsUseCase, private val registerPushWithServerUseCase: RegisterPushWithServerUseCase, private val registerPushWithProxyUseCase: RegisterPushWithProxyUseCase, private val appPreferences: AppPreferences, private val usersRepository: UsersRepository) : ViewModelProvider.Factory {
override fun create(modelClass: Class): T {
- return LoginEntryViewModel(application, getProfileUseCase, getCapabilitiesUseCase, getSignalingSettingsUseCase, appPreferences, usersRepository) as T
+ return LoginEntryViewModel(application, getProfileUseCase, getCapabilitiesUseCase, getSignalingSettingsUseCase, registerPushWithServerUseCase, registerPushWithProxyUseCase, appPreferences, usersRepository) as T
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/converters/PushConfigurationConverter.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/converters/PushConfigurationConverter.kt
index 4916a999e..e8d91b009 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/local/converters/PushConfigurationConverter.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/local/converters/PushConfigurationConverter.kt
@@ -22,20 +22,25 @@ package com.nextcloud.talk.newarch.local.converters
import androidx.room.TypeConverter
import com.bluelinelabs.logansquare.LoganSquare
-import com.nextcloud.talk.models.json.push.PushConfigurationState
+import com.nextcloud.talk.models.json.push.PushConfiguration
+import com.nextcloud.talk.newarch.utils.MagicJson
+import kotlinx.serialization.json.Json
class PushConfigurationConverter {
+ val json = Json(MagicJson.customJsonConfiguration)
+
@TypeConverter
- fun fromPushConfigurationToString(pushConfigurationState: PushConfigurationState?): String {
- if (pushConfigurationState == null) {
- return ""
+ fun fromPushConfigurationToString(pushConfiguration: PushConfiguration?): String {
+
+ return if (pushConfiguration == null) {
+ ""
} else {
- return LoganSquare.serialize(pushConfigurationState)
+ json.stringify(PushConfiguration.serializer(), pushConfiguration)
}
}
@TypeConverter
- fun fromStringToPushConfiguration(value: String): PushConfigurationState? {
- return LoganSquare.parse(value, PushConfigurationState::class.java)
+ fun fromStringToPushConfiguration(value: String): PushConfiguration? {
+ return json.parse(PushConfiguration.serializer(), value)
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt
index ccabd7f40..3bc4072cf 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt
@@ -44,7 +44,7 @@ abstract class UsersDao {
abstract fun saveUser(user: UserNgEntity): Long
@Insert(onConflict = OnConflictStrategy.REPLACE)
- abstract suspend fun saveUsers(vararg users: UserNgEntity)
+ abstract suspend fun saveUsers(vararg users: UserNgEntity): List
// get all users not scheduled for deletion
@Query("SELECT * FROM users where status != 2")
diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt
index 7b116d478..2dee280b0 100644
--- a/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt
+++ b/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt
@@ -25,7 +25,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.nextcloud.talk.models.json.capabilities.Capabilities
-import com.nextcloud.talk.models.json.push.PushConfigurationState
+import com.nextcloud.talk.models.json.push.PushConfiguration
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettings
import com.nextcloud.talk.newarch.local.models.other.UserStatus
import com.nextcloud.talk.utils.ApiUtils
@@ -43,7 +43,7 @@ data class UserNgEntity(
@ColumnInfo(name = "display_name") var displayName: String? = null,
@ColumnInfo(
name = "push_configuration"
- ) var pushConfiguration: PushConfigurationState? = null,
+ ) var pushConfiguration: PushConfiguration? = null,
@ColumnInfo(name = "capabilities") var capabilities: Capabilities? = null,
@ColumnInfo(name = "client_auth_cert") var clientCertificate: String? = null,
@ColumnInfo(
diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt
index 0b41a32e7..23899db98 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt
@@ -23,12 +23,14 @@ package com.nextcloud.talk.utils
import android.content.Context
import android.util.Base64
import android.util.Log
+import com.nextcloud.talk.R
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.models.SignatureVerification
-import com.nextcloud.talk.models.json.push.PushConfigurationState
+import com.nextcloud.talk.models.json.push.PushConfiguration
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.local.models.UserNgEntity
+import com.nextcloud.talk.newarch.utils.hashWithAlgorithm
import com.nextcloud.talk.utils.preferences.AppPreferences
import org.greenrobot.eventbus.EventBus
import org.koin.core.KoinComponent
@@ -38,6 +40,7 @@ import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
+import java.util.HashMap
class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
val appPreferences: AppPreferences by inject()
@@ -46,12 +49,52 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
private val keysFile: File
private val publicKeyFile: File
private val privateKeyFile: File
+
+ fun getMapForPushRegistrationWithServer(user: UserNgEntity): Map? {
+ val options = mutableMapOf()
+ val pushConfiguration = user.pushConfiguration
+ options["pushToken"] = pushConfiguration?.pushToken
+ options["deviceIdentifier"] = pushConfiguration?.deviceIdentifier
+ options["deviceIdentifierSignature"] = pushConfiguration?.deviceIdentifierSignature
+ options["userPublicKey"] = pushConfiguration?.userPublicKey
+
+ if (options.containsValue(null)) {
+ return null
+ }
+
+ return options
+ }
+
+ fun getMapForPushRegistrationWithServer(context: Context, token: String) : Map {
+ val options = mutableMapOf()
+
+ // Let's generate a keypair if we don't have it
+ generateRsa2048KeyPair()
+
+ val pushTokenHash = token.hashWithAlgorithm("SHA-512")
+ var publicKey = ""
+ val devicePublicKey = readKeyFromFile(true) as PublicKey?
+ devicePublicKey?.let {
+ val publicKeyBytes: ByteArray = Base64.encode(it.encoded, Base64.NO_WRAP)
+ publicKey = String(publicKeyBytes)
+ publicKey = publicKey.replace("(.{64})".toRegex(), "$1\n")
+ publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey\n-----END PUBLIC KEY-----\n"
+ }
+
+ options["format"] = "json"
+ options["pushTokenHash"] = pushTokenHash
+ options["devicePublicKey"] = publicKey
+ options["proxyServer"] = context.resources.getString(R.string.nc_push_server_url)
+
+ return options
+ }
+
fun verifySignature(
signatureBytes: ByteArray?,
subjectBytes: ByteArray?
): SignatureVerification {
val signature: Signature?
- var pushConfigurationState: PushConfigurationState?
+ var pushConfiguration: PushConfiguration?
var publicKey: PublicKey?
val signatureVerification =
SignatureVerification()
@@ -61,10 +104,10 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
signature = Signature.getInstance("SHA512withRSA")
if (userEntities.isNotEmpty()) {
for (userEntity in userEntities) {
- pushConfigurationState = userEntity.pushConfiguration
- if (pushConfigurationState?.userPublicKey != null) {
+ pushConfiguration = userEntity.pushConfiguration
+ if (pushConfiguration?.userPublicKey != null) {
publicKey = readKeyFromString(
- true, pushConfigurationState.userPublicKey!!
+ true, pushConfiguration.userPublicKey!!
) as PublicKey?
signature.initVerify(publicKey)
signature.update(subjectBytes)
@@ -141,7 +184,6 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
// we failed to generate the key
else {
// We already have the key
-
return -1
}
diff --git a/app/src/main/res/layout/login_entry_view.xml b/app/src/main/res/layout/login_entry_view.xml
index 9612389b3..0669a8a44 100644
--- a/app/src/main/res/layout/login_entry_view.xml
+++ b/app/src/main/res/layout/login_entry_view.xml
@@ -5,18 +5,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/colorPrimary">
-
-
-