Flow migration: remove Rx completely (rxbinding)

This commit is contained in:
ganfra 2021-10-27 12:13:49 +02:00
parent a9d192fa39
commit 34cb99e8ae
57 changed files with 392 additions and 435 deletions

View file

@ -17,7 +17,7 @@ def arrow = "0.8.2"
def markwon = "4.6.2"
def moshi = "1.12.0"
def lifecycle = "2.2.0"
def rxBinding = "3.1.0"
def flowBinding = "1.2.0"
def epoxy = "4.6.2"
def mavericks = "2.4.0"
def glide = "4.12.0"
@ -115,13 +115,13 @@ ext.libs = [
'bigImageViewer' : "com.github.piasy:BigImageViewer:$bigImageViewer",
'glideImageLoader' : "com.github.piasy:GlideImageLoader:$bigImageViewer",
'progressPieIndicator' : "com.github.piasy:ProgressPieIndicator:$bigImageViewer",
'glideImageViewFactory' : "com.github.piasy:GlideImageViewFactory:$bigImageViewer"
'glideImageViewFactory' : "com.github.piasy:GlideImageViewFactory:$bigImageViewer",
'flowBinding' : "io.github.reactivecircus.flowbinding:flowbinding-android:$flowBinding",
'flowBindingAppcompat' : "io.github.reactivecircus.flowbinding:flowbinding-appcompat:$flowBinding",
'flowBindingMaterial' : "io.github.reactivecircus.flowbinding:flowbinding-material:$flowBinding"
],
jakewharton : [
'timber' : "com.jakewharton.timber:timber:5.0.1",
'rxbinding' : "com.jakewharton.rxbinding3:rxbinding:$rxBinding",
'rxbindingAppcompat' : "com.jakewharton.rxbinding3:rxbinding-appcompat:$rxBinding",
'rxbindingMaterial' : "com.jakewharton.rxbinding3:rxbinding-material:$rxBinding"
'timber' : "com.jakewharton.timber:timber:5.0.1"
],
jsonwebtoken: [
'jjwtApi' : "io.jsonwebtoken:jjwt-api:$jjwt",

View file

@ -373,14 +373,10 @@ dependencies {
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.35'
// rx
implementation libs.rx.rxKotlin
implementation libs.rx.rxAndroid
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.1'
// RXBinding
implementation libs.jakewharton.rxbinding
implementation libs.jakewharton.rxbindingAppcompat
implementation libs.jakewharton.rxbindingMaterial
// FlowBinding
implementation libs.github.flowBinding
implementation libs.github.flowBindingAppcompat
implementation libs.github.flowBindingMaterial
implementation libs.airbnb.epoxy
implementation libs.airbnb.epoxyGlide

View file

@ -24,7 +24,6 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.ui.UiStateRepository
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@ -37,7 +36,6 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.group.model.GroupSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.CancelableBag
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -111,7 +111,7 @@ import im.vector.app.features.roomprofile.members.RoomMemberListFragment
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsFragment
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleChooseRestrictedFragment
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedFragment
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleFragment
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment

View file

@ -26,13 +26,14 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
@ExperimentalCoroutinesApi
fun <T> Flow<T>.chunk(durationInMillis: Long): Flow<List<T>> {
require(durationInMillis> 0) { "Duration should be greater than 0" }
require(durationInMillis > 0) { "Duration should be greater than 0" }
return flow {
coroutineScope {
val events = ArrayList<T>()
@ -66,6 +67,10 @@ fun <T> Flow<T>.chunk(durationInMillis: Long): Flow<List<T>> {
}
}
fun tickerFlow(scope: CoroutineScope, delayMillis: Long, initialDelayMillis: Long = delayMillis): Flow<Unit> {
return scope.fixedPeriodTicker(delayMillis, initialDelayMillis).consumeAsFlow()
}
private fun CoroutineScope.fixedPeriodTicker(delayMillis: Long, initialDelayMillis: Long = delayMillis): ReceiveChannel<Unit> {
require(delayMillis >= 0) { "Expected non-negative delay, but has $delayMillis ms" }
require(initialDelayMillis >= 0) { "Expected non-negative initial delay, but has $initialDelayMillis ms" }

View file

@ -45,7 +45,6 @@ import com.airbnb.mvrx.MavericksView
import com.bumptech.glide.util.Util
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.snackbar.Snackbar
import com.jakewharton.rxbinding3.view.clicks
import dagger.hilt.android.EntryPointAccessors
import im.vector.app.BuildConfig
import im.vector.app.R
@ -78,17 +77,13 @@ import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils
import im.vector.app.receivers.DebugReceiver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.GlobalError
import reactivecircus.flowbinding.android.view.clicks
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), MavericksView {
@ -123,10 +118,9 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
protected fun View.debouncedClicks(onClicked: () -> Unit) {
clicks()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { onClicked() }
.disposeOnDestroy()
.sample(300)
.onEach { onClicked() }
.launchIn(lifecycleScope)
}
/* ==========================================================================================
@ -137,6 +131,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
private lateinit var sessionListener: SessionListener
protected lateinit var bugReporter: BugReporter
private lateinit var pinLocker: PinLocker
@Inject
lateinit var rageShake: RageShake
lateinit var navigator: Navigator
@ -154,7 +149,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
// For debug only
private var debugReceiver: DebugReceiver? = null
private val uiDisposables = CompositeDisposable()
private val restorables = ArrayList<Restorable>()
override fun attachBaseContext(base: Context) {
@ -179,10 +173,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
return this
}
protected fun Disposable.disposeOnDestroy() {
uiDisposables.add(this)
}
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
Timber.i("onCreate Activity ${javaClass.simpleName}")
@ -306,8 +296,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
override fun onDestroy() {
super.onDestroy()
Timber.i("onDestroy Activity ${javaClass.simpleName}")
uiDisposables.dispose()
}
private val pinStartForActivityResult = registerStartForActivityResult { activityResult ->

View file

@ -33,19 +33,14 @@ import com.airbnb.mvrx.MavericksView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.jakewharton.rxbinding3.view.clicks
import dagger.hilt.android.EntryPointAccessors
import im.vector.app.core.di.ActivityEntryPoint
import im.vector.app.core.utils.DimensionConverter
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import reactivecircus.flowbinding.android.view.clicks
import timber.log.Timber
import java.util.concurrent.TimeUnit
/**
* Add Mavericks capabilities, handle DI and bindings.
@ -113,14 +108,12 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
@CallSuper
override fun onDestroyView() {
uiDisposables.clear()
_binding = null
super.onDestroyView()
}
@CallSuper
override fun onDestroy() {
uiDisposables.dispose()
super.onDestroy()
}
@ -169,27 +162,15 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
arguments = args?.let { Bundle().apply { putParcelable(Mavericks.KEY_ARG, it) } }
}
/* ==========================================================================================
* Disposable
* ========================================================================================== */
private val uiDisposables = CompositeDisposable()
protected fun Disposable.disposeOnDestroyView(): Disposable {
uiDisposables.add(this)
return this
}
/* ==========================================================================================
* Views
* ========================================================================================== */
protected fun View.debouncedClicks(onClicked: () -> Unit) {
clicks()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { onClicked() }
.disposeOnDestroyView()
.sample(300)
.onEach { onClicked() }
.launchIn(viewLifecycleOwner.lifecycleScope)
}
/* ==========================================================================================

View file

@ -35,7 +35,6 @@ import com.airbnb.mvrx.MavericksView
import com.bumptech.glide.util.Util.assertMainThread
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.view.clicks
import dagger.hilt.android.EntryPointAccessors
import im.vector.app.R
import im.vector.app.core.di.ActivityEntryPoint
@ -46,14 +45,10 @@ import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.features.navigation.Navigator
import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import reactivecircus.flowbinding.android.view.clicks
import timber.log.Timber
import java.util.concurrent.TimeUnit
@ -155,7 +150,6 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
@CallSuper
override fun onDestroyView() {
Timber.i("onDestroyView Fragment ${javaClass.simpleName}")
uiDisposables.clear()
_binding = null
super.onDestroyView()
}
@ -163,7 +157,6 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
@CallSuper
override fun onDestroy() {
Timber.i("onDestroy Fragment ${javaClass.simpleName}")
uiDisposables.dispose()
super.onDestroy()
}
@ -228,16 +221,6 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
}
}
/* ==========================================================================================
* Disposable
* ========================================================================================== */
private val uiDisposables = CompositeDisposable()
protected fun Disposable.disposeOnDestroyView() {
uiDisposables.add(this)
}
/* ==========================================================================================
* ViewEvents
* ========================================================================================== */
@ -258,10 +241,9 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
protected fun View.debouncedClicks(onClicked: () -> Unit) {
clicks()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { onClicked() }
.disposeOnDestroyView()
.sample(300)
.onEach { onClicked() }
.launchIn(viewLifecycleOwner.lifecycleScope)
}
/* ==========================================================================================

View file

@ -16,28 +16,14 @@
package im.vector.app.core.rx
import im.vector.app.features.settings.VectorPreferences
import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber
import javax.inject.Inject
class RxConfig @Inject constructor(
private val vectorPreferences: VectorPreferences
) {
class RxConfig @Inject constructor() {
/**
* Make sure unhandled Rx error does not crash the app in production
*/
fun setupRxPlugin() {
RxJavaPlugins.setErrorHandler { throwable ->
Timber.e(throwable, "RxError")
// is InterruptedException -> fine, some blocking code was interrupted by a dispose call
if (throwable !is InterruptedException) {
// Avoid crash in production, except if user wants it
if (vectorPreferences.failFast()) {
throw throwable
}
}
}
}
}

View file

@ -16,23 +16,36 @@
package im.vector.app.core.utils
import io.reactivex.Observable
import java.util.concurrent.TimeUnit
import im.vector.app.core.flow.tickerFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong
class CountUpTimer(private val intervalInMs: Long = 1_000) {
private val coroutineScope = CoroutineScope(Dispatchers.Main)
private val elapsedTime: AtomicLong = AtomicLong()
private val resumed: AtomicBoolean = AtomicBoolean(false)
private val disposable = Observable.interval(intervalInMs / 10, TimeUnit.MILLISECONDS)
.filter { resumed.get() }
.map { elapsedTime.addAndGet(intervalInMs / 10) }
.filter { it % intervalInMs == 0L }
.subscribe {
tickListener?.onTick(it)
}
init {
startCounter()
}
private fun startCounter() {
tickerFlow(coroutineScope, intervalInMs / 10)
.filter { resumed.get() }
.map { elapsedTime.addAndGet(intervalInMs / 10) }
.filter { it % intervalInMs == 0L }
.onEach {
tickListener?.onTick(it)
}.launchIn(coroutineScope)
}
var tickListener: TickListener? = null
@ -49,7 +62,7 @@ class CountUpTimer(private val intervalInMs: Long = 1_000) {
}
fun stop() {
disposable.dispose()
coroutineScope.cancel()
}
interface TickListener {

View file

@ -16,7 +16,6 @@
package im.vector.app.core.utils
import com.jakewharton.rxrelay2.BehaviorRelay
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -46,14 +45,6 @@ open class BehaviorDataSource<T>(private val defaultValue: T? = null) : MutableD
override fun post(value: T) {
mutableFlow.tryEmit(value)
}
private fun createRelay(): BehaviorRelay<T> {
return if (defaultValue == null) {
BehaviorRelay.create()
} else {
BehaviorRelay.createDefault(defaultValue)
}
}
}
/**

View file

@ -1,31 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.utils
import io.reactivex.Completable
import io.reactivex.Single
import io.reactivex.disposables.Disposable
import io.reactivex.internal.functions.Functions
import timber.log.Timber
fun <T> Single<T>.subscribeLogError(): Disposable {
return subscribe(Functions.emptyConsumer(), { Timber.e(it) })
}
fun Completable.subscribeLogError(): Disposable {
return subscribe({}, { Timber.e(it) })
}

View file

@ -19,8 +19,10 @@ package im.vector.app.features.call.webrtc
import android.content.Context
import android.hardware.camera2.CameraManager
import androidx.core.content.getSystemService
import im.vector.app.core.flow.chunk
import im.vector.app.core.services.CallService
import im.vector.app.core.utils.CountUpTimer
import im.vector.app.core.utils.PublishDataSource
import im.vector.app.core.utils.TextUtils.formatDuration
import im.vector.app.features.call.CameraEventsHandlerAdapter
import im.vector.app.features.call.CameraProxy
@ -35,14 +37,16 @@ import im.vector.app.features.call.utils.awaitSetLocalDescription
import im.vector.app.features.call.utils.awaitSetRemoteDescription
import im.vector.app.features.call.utils.mapToCallCandidate
import im.vector.app.features.session.coroutineScope
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.ReplaySubject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.extensions.orFalse
@ -85,7 +89,6 @@ import org.webrtc.VideoTrack
import timber.log.Timber
import java.lang.ref.WeakReference
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.TimeUnit
import javax.inject.Provider
import kotlin.coroutines.CoroutineContext
@ -157,7 +160,7 @@ class WebRtcCall(
private var currentCaptureFormat: CaptureFormat = CaptureFormat.HD
private var cameraAvailabilityCallback: CameraManager.AvailabilityCallback? = null
private val timer = CountUpTimer(Duration.ofSeconds(1).toMillis()).apply {
private val timer = CountUpTimer(1000L).apply {
tickListener = object : CountUpTimer.TickListener {
override fun onTick(milliseconds: Long) {
val formattedDuration = formatDuration(Duration.ofMillis(milliseconds))
@ -197,26 +200,33 @@ class WebRtcCall(
private var localSurfaceRenderers: MutableList<WeakReference<SurfaceViewRenderer>> = ArrayList()
private var remoteSurfaceRenderers: MutableList<WeakReference<SurfaceViewRenderer>> = ArrayList()
private val iceCandidateSource: PublishSubject<IceCandidate> = PublishSubject.create()
private val iceCandidateDisposable = iceCandidateSource
.buffer(300, TimeUnit.MILLISECONDS)
.subscribe {
// omit empty :/
if (it.isNotEmpty()) {
Timber.tag(loggerTag.value).v("Sending local ice candidates to call")
// it.forEach { peerConnection?.addIceCandidate(it) }
mxCall.sendLocalCallCandidates(it.mapToCallCandidate())
}
}
private val localIceCandidateSource = PublishDataSource<IceCandidate>()
private var localIceCandidateJob: Job? = null
private val remoteCandidateSource: ReplaySubject<IceCandidate> = ReplaySubject.create()
private var remoteIceCandidateDisposable: Disposable? = null
private val remoteCandidateSource: MutableSharedFlow<IceCandidate> = MutableSharedFlow(replay = Int.MAX_VALUE)
private var remoteIceCandidateJob: Job? = null
init {
setupLocalIceCanditate()
mxCall.addListener(this)
}
fun onIceCandidate(iceCandidate: IceCandidate) = iceCandidateSource.onNext(iceCandidate)
private fun setupLocalIceCanditate() {
sessionScope?.let {
localIceCandidateJob = localIceCandidateSource.stream()
.chunk(300)
.onEach {
// omit empty :/
if (it.isNotEmpty()) {
Timber.tag(loggerTag.value).v("Sending local ice candidates to call")
// it.forEach { peerConnection?.addIceCandidate(it) }
mxCall.sendLocalCallCandidates(it.mapToCallCandidate())
}
}.launchIn(it)
}
}
fun onIceCandidate(iceCandidate: IceCandidate) = localIceCandidateSource.post(iceCandidate)
fun onRenegotiationNeeded(restartIce: Boolean) {
sessionScope?.launch(dispatcher) {
@ -438,12 +448,15 @@ class WebRtcCall(
createLocalStream()
attachViewRenderersInternal()
Timber.tag(loggerTag.value).v("remoteCandidateSource $remoteCandidateSource")
remoteIceCandidateDisposable = remoteCandidateSource.subscribe({
Timber.tag(loggerTag.value).v("adding remote ice candidate $it")
peerConnection?.addIceCandidate(it)
}, {
Timber.tag(loggerTag.value).v("failed to add remote ice candidate $it")
})
remoteIceCandidateJob = remoteCandidateSource
.onEach {
Timber.tag(loggerTag.value).v("adding remote ice candidate $it")
peerConnection?.addIceCandidate(it)
}
.catch {
Timber.tag(loggerTag.value).v("failed to add remote ice candidate $it")
}
.launchIn(this)
// Now we wait for negotiation callback
}
@ -488,12 +501,13 @@ class WebRtcCall(
mxCall.accept(it.description)
}
Timber.tag(loggerTag.value).v("remoteCandidateSource $remoteCandidateSource")
remoteIceCandidateDisposable = remoteCandidateSource.subscribe({
Timber.tag(loggerTag.value).v("adding remote ice candidate $it")
peerConnection?.addIceCandidate(it)
}, {
Timber.tag(loggerTag.value).v("failed to add remote ice candidate $it")
})
remoteIceCandidateJob = remoteCandidateSource
.onEach {
Timber.tag(loggerTag.value).v("adding remote ice candidate $it")
peerConnection?.addIceCandidate(it)
}.catch {
Timber.tag(loggerTag.value).v("failed to add remote ice candidate $it")
}.launchIn(this)
}
private suspend fun getTurnServer(): TurnServerResponse? {
@ -761,8 +775,8 @@ class WebRtcCall(
videoCapturer?.stopCapture()
videoCapturer?.dispose()
videoCapturer = null
remoteIceCandidateDisposable?.dispose()
iceCandidateDisposable?.dispose()
remoteIceCandidateJob?.cancel()
localIceCandidateJob?.cancel()
peerConnection?.close()
peerConnection?.dispose()
localAudioSource?.dispose()
@ -852,7 +866,7 @@ class WebRtcCall(
}
Timber.tag(loggerTag.value).v("onCallIceCandidateReceived for call ${mxCall.callId} sdp: ${it.candidate}")
val iceCandidate = IceCandidate(it.sdpMid, it.sdpMLineIndex, it.candidate)
remoteCandidateSource.onNext(iceCandidate)
remoteCandidateSource.emit(iceCandidate)
}
}
}

View file

@ -21,10 +21,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.checkedChanges
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.hideKeyboard
@ -37,8 +36,13 @@ import im.vector.app.features.userdirectory.UserListAction
import im.vector.app.features.userdirectory.UserListSharedAction
import im.vector.app.features.userdirectory.UserListSharedActionViewModel
import im.vector.app.features.userdirectory.UserListViewModel
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.user.model.User
import reactivecircus.flowbinding.android.widget.checkedChanges
import reactivecircus.flowbinding.android.widget.textChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -83,21 +87,21 @@ class ContactsBookFragment @Inject constructor(
private fun setupOnlyBoundContactsView() {
views.phoneBookOnlyBoundContacts.checkedChanges()
.subscribe {
.onEach {
contactsBookViewModel.handle(ContactsBookAction.OnlyBoundContacts(it))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun setupFilterView() {
views.phoneBookFilter
.textChanges()
.skipInitialValue()
.debounce(300, TimeUnit.MILLISECONDS)
.subscribe {
.debounce(300)
.onEach {
contactsBookViewModel.handle(ContactsBookAction.FilterWith(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
override fun onDestroyView() {

View file

@ -22,16 +22,20 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.startImportTextFromFileIntent
import im.vector.app.databinding.FragmentSsssAccessFromKeyBinding
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import org.matrix.android.sdk.api.extensions.tryOrNull
import reactivecircus.flowbinding.android.widget.editorActionEvents
import reactivecircus.flowbinding.android.widget.textChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -48,22 +52,21 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment
views.ssssRestoreWithKeyText.text = getString(R.string.enter_secret_storage_input_key)
views.ssssKeyEnterEdittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
.sample(300)
.onEach {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.ssssKeyEnterEdittext.textChanges()
.skipInitialValue()
.subscribe {
.onEach {
views.ssssKeyEnterTil.error = null
views.ssssKeySubmit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.ssssKeyUseFile.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) }

View file

@ -22,14 +22,18 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider
import im.vector.app.databinding.FragmentSsssAccessFromPassphraseBinding
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import reactivecircus.flowbinding.android.widget.editorActionEvents
import reactivecircus.flowbinding.android.widget.textChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -60,21 +64,20 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor(
// .colorizeMatchingText(key, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
views.ssssPassphraseEnterEdittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
.sample(300)
.onEach {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.ssssPassphraseEnterEdittext.textChanges()
.subscribe {
.onEach {
views.ssssPassphraseEnterTil.error = null
views.ssssPassphraseSubmit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.ssssPassphraseReset.views.bottomSheetActionClickableZone.debouncedClicks {
sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll)

View file

@ -22,15 +22,19 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.view.isGone
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentBootstrapEnterPassphraseBinding
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import reactivecircus.flowbinding.android.widget.editorActionEvents
import reactivecircus.flowbinding.android.widget.textChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -58,21 +62,20 @@ class BootstrapConfirmPassphraseFragment @Inject constructor() :
}
views.ssssPassphraseEnterEdittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
.sample(300)
.onEach {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.ssssPassphraseEnterEdittext.textChanges()
.subscribe {
.onEach {
views.ssssPassphraseEnterTil.error = null
sharedViewModel.handle(BootstrapActions.UpdateConfirmCandidatePassphrase(it?.toString() ?: ""))
sharedViewModel.handle(BootstrapActions.UpdateConfirmCandidatePassphrase(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
sharedViewModel.observeViewEvents {
// when (it) {

View file

@ -21,15 +21,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentBootstrapEnterPassphraseBinding
import im.vector.app.features.settings.VectorLocale
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import reactivecircus.flowbinding.android.widget.editorActionEvents
import reactivecircus.flowbinding.android.widget.textChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -53,22 +57,21 @@ class BootstrapEnterPassphraseFragment @Inject constructor() :
views.ssssPassphraseEnterEdittext.setText(it.passphrase ?: "")
}
views.ssssPassphraseEnterEdittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
.sample(300)
.onEach {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.ssssPassphraseEnterEdittext.textChanges()
.subscribe {
.onEach {
// ssss_passphrase_enter_til.error = null
sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: ""))
sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it.toString()))
// ssss_passphrase_submit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
sharedViewModel.observeViewEvents {
// when (it) {

View file

@ -27,10 +27,9 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult
@ -40,8 +39,13 @@ import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.core.utils.startImportTextFromFileIntent
import im.vector.app.databinding.FragmentBootstrapMigrateBackupBinding
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.keysbackup.util.isValidRecoveryKey
import reactivecircus.flowbinding.android.widget.editorActionEvents
import reactivecircus.flowbinding.android.widget.textChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -63,22 +67,21 @@ class BootstrapMigrateBackupFragment @Inject constructor(
views.bootstrapMigrateEditText.setText(it.passphrase ?: "")
}
views.bootstrapMigrateEditText.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
.sample(300)
.onEach {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.bootstrapMigrateEditText.textChanges()
.skipInitialValue()
.subscribe {
.onEach {
views.bootstrapRecoveryKeyEnterTil.error = null
// sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: ""))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
// sharedViewModel.observeViewEvents {}
views.bootstrapMigrateContinueButton.debouncedClicks { submit() }

View file

@ -20,12 +20,15 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentDevtoolsEditorBinding
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
class RoomDevToolEditFragment @Inject constructor() :
@ -44,10 +47,10 @@ class RoomDevToolEditFragment @Inject constructor() :
}
views.editText.textChanges()
.skipInitialValue()
.subscribe {
.onEach {
sharedViewModel.handle(RoomDevToolAction.UpdateContentText(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
override fun onResume() {

View file

@ -24,10 +24,10 @@ import android.view.inputmethod.EditorInfo
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.toSpannable
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.registerStartForActivityResult
@ -37,7 +37,10 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.databinding.FragmentSetIdentityServerBinding
import im.vector.app.features.discovery.DiscoverySharedViewModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.terms.TermsService
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
class SetIdentityServerFragment @Inject constructor(
@ -90,11 +93,11 @@ class SetIdentityServerFragment @Inject constructor(
views.identityServerSetDefaultAlternativeTextInput
.textChanges()
.subscribe {
.onEach {
views.identityServerSetDefaultAlternativeTil.error = null
views.identityServerSetDefaultAlternativeSubmit.isEnabled = it.isNotEmpty()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.identityServerSetDefaultSubmit.debouncedClicks {
viewModel.handle(SetIdentityServerAction.UseDefaultIdentityServer)

View file

@ -36,7 +36,6 @@ import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.showInvites
import im.vector.app.features.settings.VectorDataStore
import im.vector.app.features.ui.UiStateRepository
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged

View file

@ -66,8 +66,6 @@ import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.view.focusChanges
import com.jakewharton.rxbinding3.widget.textChanges
import com.vanniktech.emoji.EmojiPopup
import im.vector.app.R
import im.vector.app.core.dialogs.ConfirmationDialogBuilder
@ -185,7 +183,9 @@ import im.vector.app.features.widgets.WidgetArgs
import im.vector.app.features.widgets.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import nl.dionsegijn.konfetti.models.Shape
@ -217,6 +217,8 @@ import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
import reactivecircus.flowbinding.android.view.focusChanges
import reactivecircus.flowbinding.android.widget.textChanges
import timber.log.Timber
import java.net.URL
import java.util.UUID
@ -1349,19 +1351,19 @@ class RoomDetailFragment @Inject constructor(
private fun observerUserTyping() {
views.composerLayout.views.composerEditText.textChanges()
.skipInitialValue()
.debounce(300, TimeUnit.MILLISECONDS)
.sample(300)
.map { it.isNotEmpty() }
.subscribe {
.onEach {
Timber.d("Typing: User is typing: $it")
textComposerViewModel.handle(TextComposerAction.UserIsTyping(it))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.composerLayout.views.composerEditText.focusChanges()
.subscribe {
.onEach {
roomDetailViewModel.handle(RoomDetailAction.ComposerFocusChange(it))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun sendUri(uri: Uri): Boolean {

View file

@ -31,10 +31,8 @@ import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import io.reactivex.Observable
import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.flow.FlowRoom
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap

View file

@ -17,7 +17,7 @@
package im.vector.app.features.home.room.list
import im.vector.app.features.home.RoomListDisplayMode
import io.reactivex.functions.Predicate
import androidx.core.util.Predicate
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary

View file

@ -16,7 +16,7 @@
package im.vector.app.features.home.room.list
import io.reactivex.functions.Predicate
import androidx.core.util.Predicate
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import javax.inject.Inject

View file

@ -30,12 +30,8 @@ import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.showInvites
import im.vector.app.space
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest

View file

@ -18,7 +18,6 @@ package im.vector.app.features.invite
import im.vector.app.ActiveSessionDataSource
import im.vector.app.features.session.coroutineScope
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob

View file

@ -25,21 +25,24 @@ import android.view.inputmethod.EditorInfo
import androidx.autofill.HintConstants
import androidx.core.text.isDigitsOnly
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.hidePassword
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginBinding
import io.reactivex.Observable
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.failure.isInvalidPassword
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -224,20 +227,18 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
private fun setupSubmitButton() {
views.loginSubmit.setOnClickListener { submit() }
Observable
.combineLatest(
views.loginField.textChanges().map { it.trim().isNotEmpty() },
views.passwordField.textChanges().map { it.isNotEmpty() },
{ isLoginNotEmpty, isPasswordNotEmpty ->
isLoginNotEmpty && isPasswordNotEmpty
}
)
.subscribeBy {
combine(
views.loginField.textChanges().map { it.trim().isNotEmpty() },
views.passwordField.textChanges().map { it.isNotEmpty() }
) { isLoginNotEmpty, isPasswordNotEmpty ->
isLoginNotEmpty && isPasswordNotEmpty
}
.onEach {
views.loginFieldTil.error = null
views.passwordFieldTil.error = null
views.loginSubmit.isEnabled = it
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun forgetPasswordClicked() {

View file

@ -25,19 +25,22 @@ import android.view.View
import android.view.ViewGroup
import androidx.autofill.HintConstants
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.args
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.isEmail
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.databinding.FragmentLoginGenericTextInputFormBinding
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.is401
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
enum class TextInputFormFragmentMode {
@ -93,10 +96,10 @@ class LoginGenericTextInputFormFragment @Inject constructor() : AbstractLoginFra
private fun setupTil() {
views.loginGenericTextInputFormTextInput.textChanges()
.subscribe {
.onEach {
views.loginGenericTextInputFormTil.error = null
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun setupUi() {
@ -195,10 +198,10 @@ class LoginGenericTextInputFormFragment @Inject constructor() : AbstractLoginFra
private fun setupSubmitButton() {
views.loginGenericTextInputFormSubmit.isEnabled = false
views.loginGenericTextInputFormTextInput.textChanges()
.subscribe {
.onEach {
views.loginGenericTextInputFormSubmit.isEnabled = isInputValid(it)
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun isInputValid(input: CharSequence): Boolean {

View file

@ -20,19 +20,22 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.hidePassword
import im.vector.app.core.extensions.isEmail
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginResetPasswordBinding
import io.reactivex.Observable
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -59,21 +62,18 @@ class LoginResetPasswordFragment @Inject constructor() : AbstractLoginFragment<F
private fun setupSubmitButton() {
views.resetPasswordSubmit.setOnClickListener { submit() }
Observable
.combineLatest(
views.resetPasswordEmail.textChanges().map { it.isEmail() },
views.passwordField.textChanges().map { it.isNotEmpty() },
{ isEmail, isPasswordNotEmpty ->
isEmail && isPasswordNotEmpty
}
)
.subscribeBy {
combine(
views.resetPasswordEmail.textChanges().map { it.isEmail() },
views.passwordField.textChanges().map { it.isNotEmpty() }
) { isEmail, isPasswordNotEmpty ->
isEmail && isPasswordNotEmpty
}
.onEach {
views.resetPasswordEmailTil.error = null
views.passwordFieldTil.error = null
views.resetPasswordSubmit.isEnabled = it
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun submit() {

View file

@ -25,15 +25,18 @@ import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentLoginServerUrlFormBinding
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.failure.Failure
import reactivecircus.flowbinding.android.widget.textChanges
import java.net.UnknownHostException
import javax.inject.Inject
@ -61,11 +64,11 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment<F
private fun setupHomeServerField() {
views.loginServerUrlFormHomeServerUrl.textChanges()
.subscribe {
.onEach {
views.loginServerUrlFormHomeServerUrlTil.error = null
views.loginServerUrlFormSubmit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.loginServerUrlFormHomeServerUrl.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {

View file

@ -24,17 +24,20 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.autofill.HintConstants
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Fail
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.hidePassword
import im.vector.app.databinding.FragmentLoginSigninPassword2Binding
import im.vector.app.features.home.AvatarRenderer
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.auth.login.LoginProfileInfo
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.isInvalidPassword
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
import javax.net.ssl.HttpsURLConnection
@ -121,11 +124,11 @@ class LoginFragmentSigninPassword2 @Inject constructor(
views.passwordField
.textChanges()
.map { it.isNotEmpty() }
.subscribeBy {
.onEach {
views.passwordFieldTil.error = null
views.loginSubmit.isEnabled = it
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun forgetPasswordClicked() {

View file

@ -22,13 +22,16 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.autofill.HintConstants
import com.jakewharton.rxbinding3.widget.textChanges
import androidx.lifecycle.lifecycleScope
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.databinding.FragmentLoginSigninUsername2Binding
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -83,11 +86,11 @@ class LoginFragmentSigninUsername2 @Inject constructor() : AbstractLoginFragment
views.loginSubmit.setOnClickListener { submit() }
views.loginField.textChanges()
.map { it.trim().isNotEmpty() }
.subscribeBy {
.onEach {
views.loginFieldTil.error = null
views.loginSubmit.isEnabled = it
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
override fun resetViewModel() {

View file

@ -23,12 +23,14 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.autofill.HintConstants
import com.jakewharton.rxbinding3.widget.textChanges
import androidx.lifecycle.lifecycleScope
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.hidePassword
import im.vector.app.databinding.FragmentLoginSignupPassword2Binding
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -87,11 +89,11 @@ class LoginFragmentSignupPassword2 @Inject constructor() : AbstractLoginFragment
private fun setupSubmitButton() {
views.loginSubmit.setOnClickListener { submit() }
views.passwordField.textChanges()
.subscribeBy { password ->
.onEach { password ->
views.passwordFieldTil.error = null
views.loginSubmit.isEnabled = password.isNotEmpty()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
override fun resetViewModel() {

View file

@ -24,14 +24,17 @@ import android.view.View
import android.view.ViewGroup
import androidx.autofill.HintConstants
import androidx.core.view.isVisible
import com.jakewharton.rxbinding3.widget.textChanges
import androidx.lifecycle.lifecycleScope
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginSignupUsername2Binding
import im.vector.app.features.login.LoginMode
import im.vector.app.features.login.SocialLoginButtonsView
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -111,12 +114,12 @@ class LoginFragmentSignupUsername2 @Inject constructor() : AbstractSSOLoginFragm
views.loginSubmit.setOnClickListener { submit() }
views.loginField.textChanges()
.map { it.trim() }
.subscribeBy { text ->
.onEach { text ->
val isNotEmpty = text.isNotEmpty()
views.loginFieldTil.error = null
views.loginSubmit.isEnabled = isNotEmpty
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
override fun resetViewModel() {

View file

@ -24,7 +24,7 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.autofill.HintConstants
import androidx.core.view.isVisible
import com.jakewharton.rxbinding3.widget.textChanges
import androidx.lifecycle.lifecycleScope
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.hidePassword
@ -32,11 +32,14 @@ import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginSigninToAny2Binding
import im.vector.app.features.login.LoginMode
import im.vector.app.features.login.SocialLoginButtonsView
import io.reactivex.Observable
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.failure.isInvalidPassword
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -136,20 +139,18 @@ class LoginFragmentToAny2 @Inject constructor() : AbstractSSOLoginFragment2<Frag
private fun setupSubmitButton() {
views.loginSubmit.setOnClickListener { submit() }
Observable
.combineLatest(
views.loginField.textChanges().map { it.trim().isNotEmpty() },
views.passwordField.textChanges().map { it.isNotEmpty() },
{ isLoginNotEmpty, isPasswordNotEmpty ->
isLoginNotEmpty && isPasswordNotEmpty
}
)
.subscribeBy {
combine(
views.loginField.textChanges().map { it.trim().isNotEmpty() },
views.passwordField.textChanges().map { it.isNotEmpty() }
) { isLoginNotEmpty, isPasswordNotEmpty ->
isLoginNotEmpty && isPasswordNotEmpty
}
.onEach {
views.loginFieldTil.error = null
views.passwordFieldTil.error = null
views.loginSubmit.isEnabled = it
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun forgetPasswordClicked() {

View file

@ -24,10 +24,10 @@ import android.view.View
import android.view.ViewGroup
import androidx.autofill.HintConstants
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.args
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.isEmail
@ -36,9 +36,12 @@ import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginGenericTextInputForm2Binding
import im.vector.app.features.login.LoginGenericTextInputFormFragmentArgument
import im.vector.app.features.login.TextInputFormFragmentMode
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.is401
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -82,10 +85,10 @@ class LoginGenericTextInputFormFragment2 @Inject constructor() : AbstractLoginFr
private fun setupTil() {
views.loginGenericTextInputFormTextInput.textChanges()
.subscribe {
.onEach {
views.loginGenericTextInputFormTil.error = null
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun setupUi() {
@ -189,11 +192,11 @@ class LoginGenericTextInputFormFragment2 @Inject constructor() : AbstractLoginFr
private fun setupSubmitButton() {
views.loginGenericTextInputFormSubmit.isEnabled = false
views.loginGenericTextInputFormTextInput.textChanges()
.subscribe { text ->
.onEach { text ->
views.loginGenericTextInputFormSubmit.isEnabled = isInputValid(text)
text?.let { updateSubmitButtons(it) }
updateSubmitButtons(text)
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun updateSubmitButtons(text: CharSequence) {

View file

@ -23,8 +23,8 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.autofill.HintConstants
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.hidePassword
@ -32,8 +32,11 @@ import im.vector.app.core.extensions.isEmail
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.core.utils.autoResetTextInputLayoutErrors
import im.vector.app.databinding.FragmentLoginResetPassword2Binding
import io.reactivex.Observable
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
/**
@ -78,19 +81,16 @@ class LoginResetPasswordFragment2 @Inject constructor() : AbstractLoginFragment2
private fun setupSubmitButton() {
views.resetPasswordSubmit.setOnClickListener { submit() }
Observable
.combineLatest(
views.resetPasswordEmail.textChanges().map { it.isEmail() },
views.passwordField.textChanges().map { it.isNotEmpty() },
{ isEmail, isPasswordNotEmpty ->
isEmail && isPasswordNotEmpty
}
)
.subscribeBy {
combine(
views.resetPasswordEmail.textChanges().map { it.isEmail() },
views.passwordField.textChanges().map { it.isNotEmpty() }
) { isEmail, isPasswordNotEmpty ->
isEmail && isPasswordNotEmpty
}
.onEach {
views.resetPasswordSubmit.isEnabled = it
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun submit() {

View file

@ -24,15 +24,18 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter
import androidx.core.view.isInvisible
import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.utils.ensureProtocol
import im.vector.app.databinding.FragmentLoginServerUrlForm2Binding
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import reactivecircus.flowbinding.android.widget.textChanges
import java.net.UnknownHostException
import javax.inject.Inject
import javax.net.ssl.HttpsURLConnection
@ -60,11 +63,11 @@ class LoginServerUrlFormFragment2 @Inject constructor() : AbstractLoginFragment2
private fun setupHomeServerField() {
views.loginServerUrlFormHomeServerUrl.textChanges()
.subscribe {
.onEach {
views.loginServerUrlFormHomeServerUrlTil.error = null
views.loginServerUrlFormSubmit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.loginServerUrlFormHomeServerUrl.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {

View file

@ -28,7 +28,6 @@ import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.viewModel
import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxbinding3.widget.queryTextChanges
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.EmojiCompatFontProvider
import im.vector.app.R
@ -37,7 +36,11 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityEmojiReactionPickerBinding
import im.vector.app.features.reactions.data.EmojiDataSource
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
import reactivecircus.flowbinding.android.widget.queryTextChanges
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -167,13 +170,11 @@ class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPick
}
searchView.queryTextChanges()
.throttleWithTimeout(600, TimeUnit.MILLISECONDS)
.doOnError { err -> Timber.e(err) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { query ->
.sample(600)
.onEach { query ->
onQueryText(query.toString())
}
.disposeOnDestroy()
.launchIn(lifecycleScope)
}
searchItem.expandActionView()
return true

View file

@ -25,7 +25,6 @@ import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@ -37,10 +36,13 @@ import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentPublicRoomsBinding
import im.vector.app.features.permalink.NavigationInterceptor
import im.vector.app.features.permalink.PermalinkHandler
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
import reactivecircus.flowbinding.appcompat.queryTextChanges
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -79,11 +81,11 @@ class PublicRoomsFragment @Inject constructor(
setupRecyclerView()
views.publicRoomsFilter.queryTextChanges()
.debounce(500, TimeUnit.MILLISECONDS)
.subscribeBy {
.debounce(500)
.onEach {
viewModel.handle(RoomDirectoryAction.FilterWith(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.publicRoomsCreateNewRoom.debouncedClicks {
sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoom)

View file

@ -16,7 +16,7 @@
package im.vector.app.features.roomprofile.members
import io.reactivex.functions.Predicate
import androidx.core.util.Predicate
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import javax.inject.Inject

View file

@ -40,6 +40,7 @@ import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.roomprofile.RoomProfileArgs
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedActions
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedEvents
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedFragment
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
import javax.inject.Inject

View file

@ -14,26 +14,26 @@
* limitations under the License.
*/
package im.vector.app.features.roomprofile.settings.joinrule
package im.vector.app.features.roomprofile.settings.joinrule.advanced
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentSpaceRestrictedSelectBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.roomprofile.settings.joinrule.advanced.ChooseRestrictedController
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedActions
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.util.MatrixItem
import reactivecircus.flowbinding.appcompat.queryTextChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -54,11 +54,11 @@ class RoomJoinRuleChooseRestrictedFragment @Inject constructor(
controller.listener = this
views.recyclerView.configureWith(controller)
views.roomsFilter.queryTextChanges()
.debounce(500, TimeUnit.MILLISECONDS)
.subscribeBy {
.debounce(500)
.onEach {
viewModel.handle(RoomJoinRuleChooseRestrictedActions.FilterWith(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.okButton.debouncedClicks {
parentFragmentManager.popBackStack()

View file

@ -27,8 +27,6 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.toast
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import org.matrix.android.sdk.api.session.Session
import timber.log.Timber
@ -67,31 +65,10 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat() {
mLoadingView = vectorActivity.findViewById(R.id.vector_settings_spinner_views)
}
@CallSuper
override fun onDestroyView() {
uiDisposables.clear()
super.onDestroyView()
}
override fun onDestroy() {
uiDisposables.dispose()
super.onDestroy()
}
abstract fun bindPref()
abstract var titleRes: Int
/* ==========================================================================================
* Disposable
* ========================================================================================== */
private val uiDisposables = CompositeDisposable()
protected fun Disposable.disposeOnDestroyView() {
uiDisposables.add(this)
}
/* ==========================================================================================
* Protected
* ========================================================================================== */

View file

@ -58,7 +58,6 @@ import im.vector.app.features.pin.PinMode
import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault
import im.vector.app.features.themes.ThemeUtils
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
@ -85,7 +84,6 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
override var titleRes = R.string.settings_security_and_privacy
override val preferenceXmlRes = R.xml.vector_settings_security_privacy
private var disposables = mutableListOf<Disposable>()
// cryptography
private val mCryptographyCategory by lazy {
@ -172,14 +170,6 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
// findPreference<VectorPreference>(VectorPreferences.SETTINGS_SECURE_BACKUP_RESET_PREFERENCE_KEY)
// }
override fun onPause() {
super.onPause()
disposables.forEach {
it.dispose()
}
disposables.clear()
}
private fun refresh4SSection(info: SecretsSynchronisationInfo) {
// it's a lot of if / else if / else
// But it's not yet clear how to manage all cases

View file

@ -34,7 +34,6 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.PublishDataSource
import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.login.ReAuthHelper
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged

View file

@ -26,12 +26,12 @@ import android.view.ViewGroup
import androidx.core.text.toSpannable
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.args
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.checkedChanges
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
@ -44,9 +44,12 @@ import im.vector.app.databinding.BottomSheetLeaveSpaceBinding
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.parcelize.Parcelize
import me.gujun.android.span.span
import org.matrix.android.sdk.api.util.toMatrixItem
import reactivecircus.flowbinding.android.widget.checkedChanges
import javax.inject.Inject
@AndroidEntryPoint
@ -82,8 +85,7 @@ class LeaveSpaceBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetLea
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
views.autoLeaveRadioGroup.checkedChanges()
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
.onEach {
when (it) {
views.leaveAll.id -> {
settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll)
@ -100,7 +102,7 @@ class LeaveSpaceBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetLea
}
}
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.leaveButton.debouncedClicks {
settingsViewModel.handle(SpaceLeaveViewAction.LeaveSpace)

View file

@ -27,7 +27,7 @@ import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.spaces.manage.roomSelectionItem
import io.reactivex.functions.Predicate
import androidx.core.util.Predicate
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.toMatrixItem

View file

@ -20,15 +20,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import reactivecircus.flowbinding.appcompat.queryTextChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -54,11 +57,11 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
}
views.publicRoomsFilter.queryTextChanges()
.debounce(100, TimeUnit.MILLISECONDS)
.subscribeBy {
.debounce(100)
.onEach {
viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
override fun onDestroyView() {

View file

@ -22,6 +22,7 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.Loading
@ -29,14 +30,16 @@ import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentSpaceAddRoomsBinding
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import reactivecircus.flowbinding.appcompat.queryTextChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -72,11 +75,11 @@ class SpaceAddRoomFragment @Inject constructor(
setupRecyclerView()
views.publicRoomsFilter.queryTextChanges()
.debounce(100, TimeUnit.MILLISECONDS)
.subscribeBy {
.debounce(100)
.onEach {
viewModel.handle(SpaceAddRoomActions.UpdateFilter(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
spaceEpoxyController.subHeaderText = getString(R.string.spaces_feeling_experimental_subspace)
viewModel.selectionListLiveData.observe(viewLifecycleOwner) {

View file

@ -16,7 +16,7 @@
package im.vector.app.features.spaces.manage
import io.reactivex.functions.Predicate
import androidx.core.util.Predicate
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo

View file

@ -25,13 +25,13 @@ import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.appcompat.view.ActionMode.Callback
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.transition.TransitionManager
import com.airbnb.epoxy.EpoxyVisibilityTracker
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@ -39,8 +39,11 @@ import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentSpaceAddRoomsBinding
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import reactivecircus.flowbinding.appcompat.queryTextChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -72,11 +75,11 @@ class SpaceManageRoomsFragment @Inject constructor(
epoxyVisibilityTracker.attach(views.roomList)
views.publicRoomsFilter.queryTextChanges()
.debounce(200, TimeUnit.MILLISECONDS)
.subscribeBy {
.debounce(200)
.onEach {
viewModel.handle(SpaceManageRoomViewAction.UpdateFilter(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
viewModel.onEach(SpaceManageRoomViewState::actionState) { actionState ->
when (actionState) {

View file

@ -20,13 +20,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@ -37,8 +37,11 @@ import im.vector.app.core.resources.DrawableProvider
import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding
import im.vector.app.features.roomprofile.members.RoomMemberListAction
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import reactivecircus.flowbinding.appcompat.queryTextChanges
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -117,11 +120,11 @@ class SpacePeopleFragment @Inject constructor(
private fun setupSearchView() {
views.memberNameFilter.queryHint = getString(R.string.search_members_hint)
views.memberNameFilter.queryTextChanges()
.debounce(100, TimeUnit.MILLISECONDS)
.subscribeBy {
.debounce(100)
.onEach {
membersViewModel.handle(RoomMemberListAction.FilterMemberList(it.toString()))
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
}
private fun handleViewEvents(events: SpacePeopleViewEvents) {

View file

@ -22,13 +22,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.appcompat.navigationClicks
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@ -37,10 +37,12 @@ import im.vector.app.databinding.FragmentSpacePreviewBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.spaces.SpacePreviewSharedAction
import im.vector.app.features.spaces.SpacePreviewSharedActionViewModel
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.util.MatrixItem
import java.util.concurrent.TimeUnit
import reactivecircus.flowbinding.appcompat.navigationClicks
import javax.inject.Inject
@Parcelize
@ -72,11 +74,11 @@ class SpacePreviewFragment @Inject constructor(
handleViewEvents(it)
}
views.roomPreviewNoPreviewToolbar.navigationClicks()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { sharedActionViewModel.post(SpacePreviewSharedAction.DismissAction) }
.disposeOnDestroyView()
views.roomPreviewNoPreviewToolbar
.navigationClicks()
.sample(300)
.onEach { sharedActionViewModel.post(SpacePreviewSharedAction.DismissAction) }
.launchIn(viewLifecycleOwner.lifecycleScope)
views.spacePreviewRecyclerView.configureWith(epoxyController)

View file

@ -31,7 +31,6 @@ import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.chip.Chip
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@ -45,8 +44,13 @@ import im.vector.app.databinding.FragmentUserListBinding
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
import im.vector.app.features.navigation.SettingsActivityPayload
import im.vector.app.features.settings.VectorSettingsActivity
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.startWith
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.user.model.User
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
class UserListFragment @Inject constructor(
@ -133,8 +137,8 @@ class UserListFragment @Inject constructor(
private fun setupSearchView() {
views.userListSearch
.textChanges()
.startWith(views.userListSearch.text)
.subscribe { text ->
.onStart { emit(views.userListSearch.text)}
.onEach { text ->
val searchValue = text.trim()
val action = if (searchValue.isBlank()) {
UserListAction.ClearSearchUsers
@ -143,7 +147,7 @@ class UserListFragment @Inject constructor(
}
viewModel.handle(action)
}
.disposeOnDestroyView()
.launchIn(viewLifecycleOwner.lifecycleScope)
views.userListSearch.setupAsSearch()
views.userListSearch.requestFocus()