diff --git a/dependencies.gradle b/dependencies.gradle index 93d1d5d5a1..80e56f0868 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -29,7 +29,7 @@ def jjwt = "0.11.5" def vanniktechEmoji = "0.16.0-SNAPSHOT" def sentry = "6.15.0" // Use 1.6.0 alpha to fix issue with test -def fragment = "1.6.0-alpha04" +def fragment = "1.6.0-alpha06" // Testing def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819 def espresso = "3.5.1" diff --git a/vector-app/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector-app/src/androidTest/java/im/vector/app/EspressoExt.kt index 7139af49a2..2c18f86f68 100644 --- a/vector-app/src/androidTest/java/im/vector/app/EspressoExt.kt +++ b/vector-app/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -150,7 +150,7 @@ fun initialSyncIdlingResource(session: Session): IdlingResource { this.callback = callback } - override fun onChanged(t: SyncState?) { + override fun onChanged(value: SyncState) { val isIdle = session.syncService().hasAlreadySynced() if (isIdle) { callback?.onTransitionToIdle() @@ -241,10 +241,10 @@ fun allSecretsKnownIdling(session: Session): IdlingResource { this.callback = callback } - override fun onChanged(t: Optional?) { - println("*** [$name] allSecretsKnownIdling ${t?.getOrNull()}") - privateKeysInfo = t?.getOrNull() - if (t?.getOrNull()?.allKnown() == true) { + override fun onChanged(value: Optional) { + println("*** [$name] allSecretsKnownIdling ${value.getOrNull()}") + privateKeysInfo = value.getOrNull() + if (value.getOrNull()?.allKnown() == true) { session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().removeObserver(this) callback?.onTransitionToIdle() } diff --git a/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt b/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt index 97a2a14da3..d34df8fd3d 100644 --- a/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt +++ b/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt @@ -122,7 +122,7 @@ abstract class VerificationTestBase { session.syncService().getSyncStateLive() } val syncObserver = object : Observer { - override fun onChanged(t: SyncState?) { + override fun onChanged(value: SyncState) { if (session.syncService().hasAlreadySynced()) { lock.countDown() syncLiveData.removeObserver(this) diff --git a/vector/src/main/java/im/vector/app/core/extensions/TextInputLayout.kt b/vector/src/main/java/im/vector/app/core/extensions/TextInputLayout.kt index 909c343a45..bb6f6a2cbb 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TextInputLayout.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TextInputLayout.kt @@ -21,13 +21,16 @@ import android.text.Editable import android.view.View import android.view.inputmethod.EditorInfo import androidx.autofill.HintConstants +import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.textfield.TextInputLayout import im.vector.app.core.platform.SimpleTextWatcher import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import reactivecircus.flowbinding.android.widget.textChanges fun TextInputLayout.editText() = this.editText!! @@ -85,7 +88,7 @@ fun TextInputLayout.setOnImeDoneListener(action: () -> Unit) { fun TextInputLayout.setOnFocusLostListener(lifecycleOwner: LifecycleOwner, action: () -> Unit) { editText().setOnFocusChangeListener { _, hasFocus -> when (hasFocus) { - false -> lifecycleOwner.lifecycleScope.launchWhenResumed { action() } + false -> lifecycleOwner.lifecycleScope.launch { lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { action() } } else -> { // do nothing } diff --git a/vector/src/main/java/im/vector/app/core/utils/LiveEvent.kt b/vector/src/main/java/im/vector/app/core/utils/LiveEvent.kt index 061790cb7a..e462715edb 100644 --- a/vector/src/main/java/im/vector/app/core/utils/LiveEvent.kt +++ b/vector/src/main/java/im/vector/app/core/utils/LiveEvent.kt @@ -48,9 +48,7 @@ open class LiveEvent(private val content: T) { * [onEventUnhandledContent] is *only* called if the [LiveEvent]'s contents has not been handled. */ class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> { - override fun onChanged(event: LiveEvent?) { - event?.getContentIfNotHandled()?.let { value -> - onEventUnhandledContent(value) - } + override fun onChanged(value: LiveEvent) { + value.getContentIfNotHandled()?.let { onEventUnhandledContent(it) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index e08ed6db46..1d6ca7948c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -30,7 +30,9 @@ import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -402,8 +404,8 @@ class HomeActivity : private fun handleStartRecoverySetup() { // To avoid IllegalStateException in case the transaction was executed after onSaveInstanceState - lifecycleScope.launchWhenResumed { - navigator.open4SSetup(this@HomeActivity, SetupMode.NORMAL) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { navigator.open4SSetup(this@HomeActivity, SetupMode.NORMAL) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index f363128ad5..034ae52a46 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -47,7 +47,9 @@ import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.setFragmentResultListener +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -1109,29 +1111,31 @@ class TimelineFragment : private fun updateJumpToReadMarkerViewVisibility() { if (isThreadTimeLine()) return - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - val state = timelineViewModel.awaitState() - val showJumpToUnreadBanner = when (state.unreadState) { - UnreadState.Unknown, - UnreadState.HasNoUnread -> false - is UnreadState.ReadMarkerNotLoaded -> true - is UnreadState.HasUnread -> { - if (state.canShowJumpToReadMarker) { - val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() - val positionOfReadMarker = withContext(Dispatchers.Default) { - timelineEventController.getPositionOfReadMarker() - } - if (positionOfReadMarker == null) { - false + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { + val state = timelineViewModel.awaitState() + val showJumpToUnreadBanner = when (state.unreadState) { + UnreadState.Unknown, + UnreadState.HasNoUnread -> false + is UnreadState.ReadMarkerNotLoaded -> true + is UnreadState.HasUnread -> { + if (state.canShowJumpToReadMarker) { + val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() + val positionOfReadMarker = withContext(Dispatchers.Default) { + timelineEventController.getPositionOfReadMarker() + } + if (positionOfReadMarker == null) { + false + } else { + positionOfReadMarker > lastVisibleItem + } } else { - positionOfReadMarker > lastVisibleItem + false } - } else { - false } } + views.jumpToReadMarkerView.isVisible = showJumpToUnreadBanner } - views.jumpToReadMarkerView.isVisible = showJumpToUnreadBanner } } @@ -1621,14 +1625,16 @@ class TimelineFragment : } override fun onRoomCreateLinkClicked(url: String) { - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - permalinkHandler - .launch(requireActivity(), url, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean { - requireActivity().finish() - return false - } - }) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { + permalinkHandler + .launch(requireActivity(), url, object : NavigationInterceptor { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean { + requireActivity().finish() + return false + } + }) + } } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 0fdf9d04cd..dea2e08a1f 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -26,7 +26,9 @@ import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.setFragmentResultListener +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -47,6 +49,7 @@ import im.vector.app.features.location.live.duration.ChooseLiveDurationBottomShe import im.vector.app.features.location.live.tracking.LocationSharingAndroidService import im.vector.app.features.location.option.LocationSharingOption import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.util.MatrixItem import java.lang.ref.WeakReference import javax.inject.Inject @@ -97,11 +100,13 @@ class LocationSharingFragment : }.also { views.mapView.addOnDidFailLoadingMapListener(it) } views.mapView.onCreate(savedInstanceState) - lifecycleScope.launchWhenCreated { - views.mapView.initialize( - url = urlMapProvider.getMapUrl(), - locationTargetChangeListener = this@LocationSharingFragment - ) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { + views.mapView.initialize( + url = urlMapProvider.getMapUrl(), + locationTargetChangeListener = this@LocationSharingFragment + ) + } } initLocateButton() diff --git a/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewFragment.kt index 1d816ddc83..aa4be84985 100644 --- a/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewFragment.kt @@ -22,7 +22,9 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState @@ -42,6 +44,7 @@ import im.vector.app.features.location.LocationSharingArgs import im.vector.app.features.location.MapState import im.vector.app.features.location.UrlMapProvider import im.vector.app.features.location.showUserLocationNotAvailableErrorDialog +import kotlinx.coroutines.launch import java.lang.ref.WeakReference import javax.inject.Inject @@ -77,8 +80,10 @@ class LocationPreviewFragment : }.also { views.mapView.addOnDidFailLoadingMapListener(it) } views.mapView.onCreate(savedInstanceState) - lifecycleScope.launchWhenCreated { - views.mapView.initialize(urlMapProvider.getMapUrl()) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { + views.mapView.initialize(urlMapProvider.getMapUrl()) + } } observeViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt index 0cd93f4ab1..840ae23115 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt @@ -16,7 +16,9 @@ package im.vector.app.features.settings +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.preference.Preference import androidx.preference.SwitchPreference import dagger.hilt.android.AndroidEntryPoint @@ -129,34 +131,36 @@ class VectorSettingsPinFragment : } private fun refreshPinCodeStatus() { - lifecycleScope.launchWhenResumed { - val hasPinCode = pinCodeStore.hasEncodedPin() - usePinCodePref.isChecked = hasPinCode - usePinCodePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { - if (hasPinCode) { - lifecycleScope.launch { - pinCodeStore.deletePinCode() - refreshPinCodeStatus() + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { + val hasPinCode = pinCodeStore.hasEncodedPin() + usePinCodePref.isChecked = hasPinCode + usePinCodePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + if (hasPinCode) { + lifecycleScope.launch { + pinCodeStore.deletePinCode() + refreshPinCodeStatus() + } + } else { + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.CREATE + ) } - } else { - navigator.openPinCode( - requireContext(), - pinActivityResultLauncher, - PinMode.CREATE - ) + true } - true - } - changePinCodePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { - if (hasPinCode) { - navigator.openPinCode( - requireContext(), - pinActivityResultLauncher, - PinMode.MODIFY - ) + changePinCodePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + if (hasPinCode) { + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.MODIFY + ) + } + true } - true } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index d940132b5e..284ea84ed7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -27,7 +27,9 @@ import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreference @@ -185,11 +187,11 @@ class VectorSettingsSecurityPrivacyFragment : } .launchIn(viewLifecycleOwner.lifecycleScope) - lifecycleScope.launchWhenResumed { - findPreference(VectorPreferences.SETTINGS_CRYPTOGRAPHY_HS_ADMIN_DISABLED_E2E_DEFAULT)?.isVisible = - rawService - .getElementWellknown(session.sessionParams) - ?.isE2EByDefault() == false + viewLifecycleOwner.lifecycleScope.launch { + findPreference(VectorPreferences.SETTINGS_CRYPTOGRAPHY_HS_ADMIN_DISABLED_E2E_DEFAULT)?.isVisible = + rawService + .getElementWellknown(session.sessionParams) + ?.isE2EByDefault() == false } } @@ -416,16 +418,18 @@ class VectorSettingsSecurityPrivacyFragment : } private fun openPinCodePreferenceScreen() { - lifecycleScope.launchWhenResumed { - val hasPinCode = pinCodeStore.hasEncodedPin() - if (hasPinCode) { - navigator.openPinCode( - requireContext(), - pinActivityResultLauncher, - PinMode.AUTH - ) - } else { - doOpenPinCodePreferenceScreen() + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { + val hasPinCode = pinCodeStore.hasEncodedPin() + if (hasPinCode) { + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.AUTH + ) + } else { + doOpenPinCodePreferenceScreen() + } } } } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 6447452e5b..1fb4d25667 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -24,7 +24,9 @@ import android.view.View import android.view.ViewGroup import android.widget.ScrollView import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.args import com.airbnb.mvrx.withState @@ -45,6 +47,7 @@ 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.launch 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 @@ -174,8 +177,10 @@ class UserListFragment : // Scroll to the bottom when adding chips. When removing chips, do not scroll if (newNumberOfChips >= currentNumberOfChips) { - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - views.chipGroupScrollView.fullScroll(ScrollView.FOCUS_DOWN) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { + views.chipGroupScrollView.fullScroll(ScrollView.FOCUS_DOWN) + } } } }