diff --git a/changelog.d/4645.misc b/changelog.d/4645.misc new file mode 100644 index 00000000000..7f96fc551eb --- /dev/null +++ b/changelog.d/4645.misc @@ -0,0 +1 @@ +Debounce some clicks \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index 66c5a53cc22..181bd8c6be9 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -62,7 +62,6 @@ import im.vector.app.core.extensions.restart import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.toMvRxBundle -import im.vector.app.core.flow.throttleFirst import im.vector.app.core.utils.toast import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs @@ -121,7 +120,6 @@ abstract class VectorBaseActivity : AppCompatActivity(), Maver protected fun View.debouncedClicks(onClicked: () -> Unit) { clicks() - .throttleFirst(300) .onEach { onClicked() } .launchIn(lifecycleScope) } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index 95feb45ad6c..e441efe684b 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -35,7 +35,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.EntryPointAccessors import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.extensions.toMvRxBundle -import im.vector.app.core.flow.throttleFirst import im.vector.app.core.utils.DimensionConverter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -168,7 +167,6 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe protected fun View.debouncedClicks(onClicked: () -> Unit) { clicks() - .throttleFirst(300) .onEach { onClicked() } .launchIn(viewLifecycleOwner.lifecycleScope) } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index f4e1fe84e1b..9f156f937c6 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -42,7 +42,6 @@ import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.toMvRxBundle -import im.vector.app.core.flow.throttleFirst import im.vector.app.features.navigation.Navigator import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog import kotlinx.coroutines.flow.launchIn @@ -239,7 +238,6 @@ abstract class VectorBaseFragment : Fragment(), MavericksView protected fun View.debouncedClicks(onClicked: () -> Unit) { clicks() - .throttleFirst(300) .onEach { onClicked() } .launchIn(viewLifecycleOwner.lifecycleScope) } diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 0e46cb2c78e..5b9c3f7fb46 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -79,7 +79,7 @@ class AttachmentsPreviewFragment @Inject constructor( applyInsets() setupRecyclerViews() setupToolbar(views.attachmentPreviewerToolbar) - views.attachmentPreviewerSendButton.setOnClickListener { + views.attachmentPreviewerSendButton.debouncedClicks { setResultAndFinish() } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 2406cbc625b..995dc3d5e8c 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -35,7 +35,6 @@ import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.view.isInvisible import androidx.core.view.isVisible -import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel @@ -62,8 +61,6 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs import io.github.hyuwah.draggableviewlib.DraggableView import io.github.hyuwah.draggableviewlib.setupDraggable -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.logger.LoggerTag @@ -142,12 +139,9 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } } - callViewModel.viewEvents - .stream() - .onEach { - handleViewEvents(it) - } - .launchIn(lifecycleScope) + callViewModel.observeViewEvents { + handleViewEvents(it) + } callViewModel.onEach(VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> if (isVideoCall) { diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index c1bccf855dd..5310fccb3a6 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -77,7 +77,7 @@ class ContactsBookFragment @Inject constructor( } private fun setupConsentView() { - views.phoneBookSearchForMatrixContacts.setOnClickListener { + views.phoneBookSearchForMatrixContacts.debouncedClicks { contactsBookViewModel.handle(ContactsBookAction.UserConsentRequest) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt index 435ca6e608f..40ad1372fb7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -58,8 +58,8 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() : views.keyInputLayout.error = newValue } - views.keysRestoreButton.setOnClickListener { onRestoreFromKey() } - views.keysBackupImport.setOnClickListener { onImport() } + views.keysRestoreButton.debouncedClicks { onRestoreFromKey() } + views.keysBackupImport.debouncedClicks { onImport() } views.keyTextEdit.doOnTextChanged { text, _, _, _ -> onRestoreKeyTextEditChange(text) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index c023e66e44f..631bc9ff4f0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -58,8 +58,8 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase return@setOnEditorActionListener false } - views.helperTextWithLink.setOnClickListener { onUseRecoveryKey() } - views.keysBackupRestoreWithPassphraseSubmit.setOnClickListener { onRestoreBackup() } + views.helperTextWithLink.debouncedClicks { onUseRecoveryKey() } + views.keysBackupRestoreWithPassphraseSubmit.debouncedClicks { onRestoreBackup() } views.keysBackupPassphraseEnterEdittext.doOnTextChanged { text, _, _, _ -> onPassphraseTextEditChange(text) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt index 66a7df14c3f..c4663fd3bcd 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt @@ -52,7 +52,7 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen views.successText.text = context?.getString(R.string.keys_backup_restore_success_title_already_up_to_date) views.successDetailsText.isVisible = false } - views.keysBackupSetupDoneButton.setOnClickListener { onDone() } + views.keysBackupSetupDoneButton.debouncedClicks { onDone() } } private fun onDone() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt index 08496c490f3..7d8feba942d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -45,8 +45,8 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment onPassphraseChanged() } views.keysBackupSetupStep2PassphraseConfirmEditText.doOnTextChanged { _, _, _, _ -> onConfirmPassphraseChanged() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index 2befc4e79d0..c1cd87b4c8d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -85,9 +85,9 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment(R.id.keys_backup_setup_save)?.setOnClickListener { + dialog.findViewById(R.id.keys_backup_setup_save)?.debouncedClicks { val userId = viewModel.userId val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( @@ -139,7 +139,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment(R.id.keys_backup_setup_share)?.setOnClickListener { + dialog.findViewById(R.id.keys_backup_setup_share)?.debouncedClicks { startSharePlainTextIntent( fragment = this, activityResultLauncher = null, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 8335c635e45..de8e0dfa9a3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -898,7 +898,7 @@ class RoomDetailFragment @Inject constructor( } private fun setupJumpToReadMarkerView() { - views.jumpToReadMarkerView.setOnClickListener { + views.jumpToReadMarkerView.debouncedClicks { onJumpToReadMarkerClicked() } views.jumpToReadMarkerView.setOnCloseIconClickListener { @@ -954,7 +954,7 @@ class RoomDetailFragment @Inject constructor( super.onCreateOptionsMenu(menu, inflater) // We use a custom layout for this menu item, so we need to set a ClickListener menu.findItem(R.id.open_matrix_apps)?.let { menuItem -> - menuItem.actionView.setOnClickListener { + menuItem.actionView.debouncedClicks { onOptionsItemSelected(menuItem) } } @@ -1463,7 +1463,7 @@ class RoomDetailFragment @Inject constructor( callback = this@RoomDetailFragment isVisible = true render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState) - setOnClickListener { } + setOnClickListener(null) } Unit } else if (mainState.asyncInviter.complete) { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt index 0978621f28a..9ca8a1dbecb 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt @@ -81,7 +81,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment - view.setOnClickListener { onAvatarClicked(view) } + view.debouncedClicks { onAvatarClicked(view) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt index 0a12a86ff0d..cca4b728181 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt @@ -20,6 +20,7 @@ import android.content.Context import android.os.Bundle import android.view.View import androidx.annotation.CallSuper +import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceFragmentCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R @@ -27,7 +28,10 @@ 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 kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.session.Session +import reactivecircus.flowbinding.android.view.clicks import timber.log.Timber abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat() { @@ -42,6 +46,16 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat() { protected lateinit var session: Session protected lateinit var errorFormatter: ErrorFormatter + /* ========================================================================================== + * Views + * ========================================================================================== */ + + protected fun View.debouncedClicks(onClicked: () -> Unit) { + clicks() + .onEach { onClicked() } + .launchIn(viewLifecycleOwner.lifecycleScope) + } + abstract val preferenceXmlRes: Int @CallSuper diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index 8d950b4e32b..27548dc756f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -418,7 +418,7 @@ class VectorSettingsGeneralFragment @Inject constructor( } } - updateButton.setOnClickListener { + updateButton.debouncedClicks { // Hide passwords during processing views.changePasswordOldPwdText.hidePassword() views.changePasswordNewPwdText.hidePassword() diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt index 2a3ea799a58..dbea2536498 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt @@ -197,7 +197,7 @@ class VectorSettingsPreferencesFragment @Inject constructor( .forEachIndexed { i, v -> v.isChecked = i == index - v.setOnClickListener { + v.debouncedClicks { dialog.dismiss() FontScale.updateFontScale(activity, i) vectorConfiguration.applyToApplicationContext() 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 438382ab3cc..3f25907be08 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 @@ -446,7 +446,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( val importDialog = builder.show() - views.dialogE2eKeysImportButton.setOnClickListener { + views.dialogE2eKeysImportButton.debouncedClicks { val password = views.dialogE2eKeysPassphraseEditText.text.toString() displayLoadingView() diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index d5fd3050e95..e1efef4d5a2 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -118,7 +118,7 @@ class IncomingShareFragment @Inject constructor( return true } }) - views.sendShareButton.setOnClickListener { + views.sendShareButton.debouncedClicks { handleSendShare() } } diff --git a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt index ee7557b402f..87b8c33aa3a 100644 --- a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt @@ -42,7 +42,7 @@ class SignedOutActivity : VectorBaseActivity() { } private fun setupViews() { - views.signedOutSubmit.setOnClickListener { submit() } + views.signedOutSubmit.debouncedClicks { submit() } } private fun submit() { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index a31edfcb029..f8d817acd51 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -40,7 +40,6 @@ class WidgetActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { - private const val WIDGET_FRAGMENT_TAG = "WIDGET_FRAGMENT_TAG" private const val WIDGET_PERMISSION_FRAGMENT_TAG = "WIDGET_PERMISSION_FRAGMENT_TAG" private const val EXTRA_RESULT = "EXTRA_RESULT" @@ -56,7 +55,7 @@ class WidgetActivity : VectorBaseActivity(), return intent.extras?.getSerializable(EXTRA_RESULT) as? Content } - fun createResultIntent(content: Content): Intent { + private fun createResultIntent(content: Content): Intent { return Intent().apply { putExtra(EXTRA_RESULT, content as Serializable) }