Integrate Valere's remarks - step 1

This commit is contained in:
Benoit Marty 2020-05-15 18:54:12 +02:00
parent 85a4f83662
commit a6541481bf
17 changed files with 76 additions and 65 deletions
matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx
matrix-sdk-android/src/main/java/im/vector/matrix/android
vector/src/main/java/im/vector/riotx

View file

@ -95,8 +95,8 @@ class RxSession(private val session: Session) {
return session.getPagedUsersLive(filter, excludedUserIds).asObservable() return session.getPagedUsersLive(filter, excludedUserIds).asObservable()
} }
fun liveThreePIds(): Observable<List<ThreePid>> { fun liveThreePIds(refreshData: Boolean): Observable<List<ThreePid>> {
return session.getThreePidsLive().asObservable() return session.getThreePidsLive(refreshData).asObservable()
.startWithCallable { session.getThreePids() } .startWithCallable { session.getThreePids() }
} }

View file

@ -63,6 +63,7 @@ interface ProfileService {
/** /**
* Get the current user 3Pids Live * Get the current user 3Pids Live
* @param refreshData set to true to fetch data from the homeserver
*/ */
fun getThreePidsLive(): LiveData<List<ThreePid>> fun getThreePidsLive(refreshData: Boolean): LiveData<List<ThreePid>>
} }

View file

@ -22,6 +22,7 @@ import im.vector.matrix.android.internal.session.cleanup.CleanupSession
import im.vector.matrix.android.internal.session.identity.IdentityDisconnectTask import im.vector.matrix.android.internal.session.identity.IdentityDisconnectTask
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> { internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> {
@ -47,9 +48,8 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
} }
// Logout from identity server if any, ignoring errors // Logout from identity server if any, ignoring errors
runCatching { runCatching { identityDisconnectTask.execute(Unit) }
identityDisconnectTask.execute(Unit) .onFailure { Timber.w(it, "Unable to disconnect identity server") }
}
cleanupSession.handle() cleanupSession.handle()
} }

View file

@ -50,6 +50,7 @@ import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAcco
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
import im.vector.matrix.android.internal.task.launchToCallback import im.vector.matrix.android.internal.task.launchToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.ensureProtocol
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -201,12 +202,7 @@ internal class DefaultIdentityService @Inject constructor(
} }
override fun setNewIdentityServer(url: String, callback: MatrixCallback<String>): Cancelable { override fun setNewIdentityServer(url: String, callback: MatrixCallback<String>): Cancelable {
val urlCandidate = buildString { val urlCandidate = url.ensureProtocol()
if (!url.startsWith("http")) {
append("https://")
}
append(url)
}
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
val current = getCurrentIdentityServerUrl() val current = getCurrentIdentityServerUrl()
@ -217,9 +213,8 @@ internal class DefaultIdentityService @Inject constructor(
// Disconnect previous one if any, first, because the token will change. // Disconnect previous one if any, first, because the token will change.
// In case of error when configuring the new identity server, this is not a big deal, // In case of error when configuring the new identity server, this is not a big deal,
// we will ask for a new token on the previous Identity server // we will ask for a new token on the previous Identity server
runCatching { runCatching { identityDisconnectTask.execute(Unit) }
identityDisconnectTask.execute(Unit) .onFailure { Timber.w(it, "Unable to disconnect identity server") }
}
// Try to get a token // Try to get a token
val token = getNewIdentityServerToken(urlCandidate) val token = getNewIdentityServerToken(urlCandidate)

View file

@ -34,14 +34,12 @@ internal data class IdentityLookUpParams(
/** /**
* Required. The algorithm the client is using to encode the addresses. This should be one of the available options from /hash_details. * Required. The algorithm the client is using to encode the addresses. This should be one of the available options from /hash_details.
*/ */
@JvmField
@Json(name = "algorithm") @Json(name = "algorithm")
val algorithm: String, val algorithm: String,
/** /**
* Required. The pepper from /hash_details. This is required even when the algorithm does not make use of it. * Required. The pepper from /hash_details. This is required even when the algorithm does not make use of it.
*/ */
@JvmField
@Json(name = "pepper") @Json(name = "pepper")
val pepper: String val pepper: String
) )

View file

@ -88,11 +88,13 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
) )
} }
override fun getThreePidsLive(): LiveData<List<ThreePid>> { override fun getThreePidsLive(refreshData: Boolean): LiveData<List<ThreePid>> {
// Force a refresh of the values if (refreshData) {
refreshUserThreePidsTask // Force a refresh of the values
.configureWith() refreshUserThreePidsTask
.executeBy(taskExecutor) .configureWith()
.executeBy(taskExecutor)
}
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ it.where<UserThreePidEntity>() }, { it.where<UserThreePidEntity>() },

View file

@ -20,7 +20,6 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.UserThreePidEntity import im.vector.matrix.android.internal.database.model.UserThreePidEntity
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -38,7 +37,7 @@ internal class DefaultRefreshUserThreePidsTask @Inject constructor(private val p
Timber.d("Get ${accountThreePidsResponse.threePids?.size} threePids") Timber.d("Get ${accountThreePidsResponse.threePids?.size} threePids")
// Store the list in DB // Store the list in DB
monarchy.awaitTransaction { realm -> monarchy.writeAsync { realm ->
realm.where(UserThreePidEntity::class.java).findAll().deleteAllFromRealm() realm.where(UserThreePidEntity::class.java).findAll().deleteAllFromRealm()
accountThreePidsResponse.threePids?.forEach { accountThreePidsResponse.threePids?.forEach {
val entity = UserThreePidEntity( val entity = UserThreePidEntity(

View file

@ -63,9 +63,9 @@ internal class DefaultSignOutTask @Inject constructor(
} }
// Logout from identity server if any // Logout from identity server if any
runCatching { runCatching { identityDisconnectTask.execute(Unit) }
identityDisconnectTask.execute(Unit) .onFailure { Timber.w(it, "Unable to disconnect identity server") }
}
Timber.d("SignOut: cleanup session...") Timber.d("SignOut: cleanup session...")
cleanupSession.handle() cleanupSession.handle()

View file

@ -26,3 +26,14 @@ internal fun String.isValidUrl(): Boolean {
false false
} }
} }
/**
* Ensure string starts with "http". If it is not the case, "https://" is added, only if the String is not empty
*/
internal fun String.ensureProtocol(): String {
return when {
isEmpty() -> this
!startsWith("http") -> "https://$this"
else -> this
}
}

View file

@ -26,3 +26,14 @@ fun String.isValidUrl(): Boolean {
false false
} }
} }
/**
* Ensure string starts with "http". If it is not the case, "https://" is added, only if the String is not empty
*/
internal fun String.ensureProtocol(): String {
return when {
isEmpty() -> this
!startsWith("http") -> "https://$this"
else -> this
}
}

View file

@ -48,17 +48,16 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
bootstrapSaveText.text = getString(R.string.bootstrap_save_key_description, getString(R.string.message_key), getString(R.string.recovery_passphrase)) val messageKey = getString(R.string.message_key)
val recoveryPassphrase = getString(R.string.recovery_passphrase)
val color = colorProvider.getColorFromAttribute(R.attr.vctr_toolbar_link_text_color)
bootstrapSaveText.text = getString(R.string.bootstrap_save_key_description, messageKey, recoveryPassphrase)
.toSpannable() .toSpannable()
.colorizeMatchingText(getString(R.string.recovery_passphrase), colorProvider.getColorFromAttribute(android.R.attr.textColorLink)) .colorizeMatchingText(messageKey, color)
.colorizeMatchingText(getString(R.string.message_key), colorProvider.getColorFromAttribute(android.R.attr.textColorLink)) .colorizeMatchingText(recoveryPassphrase, color)
// TODO: previous debouncing window was 600ms, check with Valere why
recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() } recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() }
// TODO: previous debouncing window was 600ms, check with Valere why
recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() } recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() }
recoveryContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.GoToCompleted) } recoveryContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.GoToCompleted) }
} }

View file

@ -32,8 +32,8 @@ import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.ensureProtocol
import im.vector.riotx.features.discovery.change.SetIdentityServerFragment import im.vector.riotx.features.discovery.change.SetIdentityServerFragment
import im.vector.riotx.features.discovery.change.SetIdentityServerViewModel
import im.vector.riotx.features.terms.ReviewTermsActivity import im.vector.riotx.features.terms.ReviewTermsActivity
import kotlinx.android.synthetic.main.fragment_generic_recycler.* import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import javax.inject.Inject import javax.inject.Inject
@ -108,7 +108,7 @@ class DiscoverySettingsFragment @Inject constructor(
navigator.openTerms( navigator.openTerms(
this, this,
TermsService.ServiceType.IdentityService, TermsService.ServiceType.IdentityService,
SetIdentityServerViewModel.sanitatizeBaseURL(state.identityServer() ?: ""), state.identityServer()?.ensureProtocol() ?: "",
null) null)
} }
} }

View file

@ -77,7 +77,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
private fun observeThreePids() { private fun observeThreePids() {
session.rx() session.rx()
.liveThreePIds() .liveThreePIds(true)
.subscribe { .subscribe {
retrieveBinding(it) retrieveBinding(it)
} }

View file

@ -15,6 +15,7 @@
*/ */
package im.vector.riotx.features.discovery package im.vector.riotx.features.discovery
import android.view.KeyEvent
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
@ -37,6 +38,20 @@ abstract class SettingsEditTextItem : EpoxyModelWithHolder<SettingsEditTextItem.
@EpoxyAttribute @EpoxyAttribute
var interactionListener: Listener? = null var interactionListener: Listener? = null
private val textChangeListener: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit = { code, _, _, _ ->
code?.let { interactionListener?.onCodeChange(it.toString()) }
}
private val editorActionListener = object : TextView.OnEditorActionListener {
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
if (actionId == EditorInfo.IME_ACTION_DONE) {
interactionListener?.onValidate()
return true
}
return false
}
}
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.textView.setTextOrHide(descriptionText) holder.textView.setTextOrHide(descriptionText)
@ -49,16 +64,8 @@ abstract class SettingsEditTextItem : EpoxyModelWithHolder<SettingsEditTextItem.
holder.textInputLayout.error = errorText holder.textInputLayout.error = errorText
} }
holder.editText.doOnTextChanged { code, _, _, _ -> holder.editText.doOnTextChanged(textChangeListener)
code?.let { interactionListener?.onCodeChange(it.toString()) } holder.editText.setOnEditorActionListener(editorActionListener)
}
holder.editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
interactionListener?.onValidate()
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
}
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -122,7 +122,7 @@ class SetIdentityServerFragment @Inject constructor(
navigator.openTerms( navigator.openTerms(
this, this,
TermsService.ServiceType.IdentityService, TermsService.ServiceType.IdentityService,
SetIdentityServerViewModel.sanitatizeBaseURL(it.identityServerUrl), it.identityServerUrl,
null) null)
} }
}.exhaustive }.exhaustive

View file

@ -31,6 +31,7 @@ import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.ensureProtocol
class SetIdentityServerViewModel @AssistedInject constructor( class SetIdentityServerViewModel @AssistedInject constructor(
@Assisted initialState: SetIdentityServerState, @Assisted initialState: SetIdentityServerState,
@ -59,14 +60,6 @@ class SetIdentityServerViewModel @AssistedInject constructor(
val fragment: SetIdentityServerFragment = (viewModelContext as FragmentViewModelContext).fragment() val fragment: SetIdentityServerFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.viewModelFactory.create(state) return fragment.viewModelFactory.create(state)
} }
fun sanitatizeBaseURL(baseUrl: String): String {
var baseUrl1 = baseUrl
if (!baseUrl1.startsWith("http://") && !baseUrl1.startsWith("https://")) {
baseUrl1 = "https://$baseUrl1"
}
return baseUrl1
}
} }
var currentWantedUrl: String? = null var currentWantedUrl: String? = null
@ -90,14 +83,11 @@ class SetIdentityServerViewModel @AssistedInject constructor(
} }
private fun doChangeIdentityServerUrl(url: String) { private fun doChangeIdentityServerUrl(url: String) {
var baseUrl = url if (url.isEmpty()) {
if (baseUrl.isEmpty()) {
_viewEvents.post(SetIdentityServerViewEvents.Failure(R.string.settings_discovery_please_enter_server)) _viewEvents.post(SetIdentityServerViewEvents.Failure(R.string.settings_discovery_please_enter_server))
return return
} }
baseUrl = sanitatizeBaseURL(baseUrl) val baseUrl = url.ensureProtocol().also { currentWantedUrl = it }
currentWantedUrl = baseUrl
_viewEvents.post(SetIdentityServerViewEvents.Loading()) _viewEvents.post(SetIdentityServerViewEvents.Loading())

View file

@ -25,6 +25,7 @@ import butterknife.OnClick
import com.jakewharton.rxbinding3.widget.textChanges import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.utils.ensureProtocol
import im.vector.riotx.core.utils.openUrlInExternalBrowser import im.vector.riotx.core.utils.openUrlInExternalBrowser
import kotlinx.android.synthetic.main.fragment_login_server_url_form.* import kotlinx.android.synthetic.main.fragment_login_server_url_form.*
import javax.inject.Inject import javax.inject.Inject
@ -96,16 +97,13 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment()
cleanupUi() cleanupUi()
// Static check of homeserver url, empty, malformed, etc. // Static check of homeserver url, empty, malformed, etc.
var serverUrl = loginServerUrlFormHomeServerUrl.text.toString().trim() val serverUrl = loginServerUrlFormHomeServerUrl.text.toString().trim().ensureProtocol()
when { when {
serverUrl.isBlank() -> { serverUrl.isBlank() -> {
loginServerUrlFormHomeServerUrlTil.error = getString(R.string.login_error_invalid_home_server) loginServerUrlFormHomeServerUrlTil.error = getString(R.string.login_error_invalid_home_server)
} }
else -> { else -> {
if (serverUrl.startsWith("http").not()) {
serverUrl = "https://$serverUrl"
}
loginServerUrlFormHomeServerUrl.setText(serverUrl) loginServerUrlFormHomeServerUrl.setText(serverUrl)
loginViewModel.handle(LoginAction.UpdateHomeServer(serverUrl)) loginViewModel.handle(LoginAction.UpdateHomeServer(serverUrl))
} }