Migrate to ViewBindings (#1072) - WIP

This commit is contained in:
Benoit Marty 2020-12-16 02:05:38 +01:00
parent 706736273c
commit dba65dcd22
27 changed files with 478 additions and 405 deletions

View File

@ -172,3 +172,6 @@ getSystemService\(Context
### Use DefaultSharedPreferences.getInstance() instead for better performance
PreferenceManager\.getDefaultSharedPreferences==2
### Use ViewBindings
findViewById

View File

@ -24,26 +24,27 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.snackbar.Snackbar
import im.vector.app.R
import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityTestMaterialThemeBinding
// Rendering is not the same with VectorBaseActivity
abstract class DebugMaterialThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_material_theme)
val views = ActivityTestMaterialThemeBinding.inflate(layoutInflater)
setContentView(views.root)
debugShowSnackbar.setOnClickListener {
Snackbar.make(debugMaterialCoordinator, "Snackbar!", Snackbar.LENGTH_SHORT)
views.debugShowSnackbar.setOnClickListener {
Snackbar.make(views.debugMaterialCoordinator, "Snackbar!", Snackbar.LENGTH_SHORT)
.setAction("Action") { }
.show()
}
debugShowToast.setOnClickListener {
views.debugShowToast.setOnClickListener {
toast("Toast")
}
debugShowDialog.setOnClickListener {
views.debugShowDialog.setOnClickListener {
AlertDialog.Builder(this)
.setMessage("Dialog content")
.setIcon(R.drawable.ic_settings_x)
@ -53,7 +54,7 @@ abstract class DebugMaterialThemeActivity : AppCompatActivity() {
.show()
}
debugShowBottomSheet.setOnClickListener {
views.debugShowBottomSheet.setOnClickListener {
BottomSheetDialogFragment().show(supportFragmentManager, "TAG")
}
}

View File

@ -22,15 +22,16 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R
import im.vector.app.databinding.ActivityTestLinkifyBinding
import im.vector.app.databinding.ActivityTestMaterialThemeBinding
class TestLinkifyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_linkify)
test_linkify_content_view.removeAllViews()
val views = ActivityTestLinkifyBinding.inflate(layoutInflater)
setContentView(views.root)
views.testLinkifyContentView.removeAllViews()
listOf(
"https://www.html5rocks.com/en/tutorials/webrtc/basics/ |",
@ -79,7 +80,7 @@ class TestLinkifyActivity : AppCompatActivity() {
)
.forEach { textContent ->
val item = LayoutInflater.from(this)
.inflate(R.layout.item_test_linkify, test_linkify_content_view, false)
.inflate(R.layout.item_test_linkify, views.testLinkifyContentView, false)
item.findViewById<TextView>(R.id.test_linkify_auto_text)
?.apply {
@ -115,7 +116,7 @@ class TestLinkifyActivity : AppCompatActivity() {
// TODO Call VectorLinkify.addLinks(text)
}
test_linkify_content_view.addView(item, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
views.testLinkifyContentView.addView(item, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
}
}
}

View File

@ -21,14 +21,18 @@ import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import org.matrix.android.sdk.api.crypto.getAllVerificationEmojis
class DebugSasEmojiActivity : AppCompatActivity() {
private lateinit var views: FragmentGenericRecyclerBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_generic_recycler)
views = FragmentGenericRecyclerBinding.inflate(layoutInflater)
setContentView(views.root)
val controller = SasEmojiController()
views.genericRecyclerView.configureWith(controller)
controller.setData(SasState(getAllVerificationEmojis()))

View File

@ -21,7 +21,7 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.databinding.DialogConfirmationWithReasonBinding
object ConfirmationDialogBuilder {
@ -33,25 +33,26 @@ object ConfirmationDialogBuilder {
@StringRes reasonHintRes: Int,
confirmation: (String?) -> Unit) {
val layout = activity.layoutInflater.inflate(R.layout.dialog_confirmation_with_reason, null)
layout.dialogConfirmationText.setText(confirmationRes)
val views = DialogConfirmationWithReasonBinding.bind(layout)
views.dialogConfirmationText.setText(confirmationRes)
layout.dialogReasonCheck.isVisible = askForReason
layout.dialogReasonTextInputLayout.isVisible = askForReason
views.dialogReasonCheck.isVisible = askForReason
views.dialogReasonTextInputLayout.isVisible = askForReason
layout.dialogReasonCheck.setOnCheckedChangeListener { _, isChecked ->
layout.dialogReasonTextInputLayout.isEnabled = isChecked
views.dialogReasonCheck.setOnCheckedChangeListener { _, isChecked ->
views.dialogReasonTextInputLayout.isEnabled = isChecked
}
if (askForReason && reasonHintRes != 0) {
layout.dialogReasonInput.setHint(reasonHintRes)
views.dialogReasonInput.setHint(reasonHintRes)
}
AlertDialog.Builder(activity)
.setTitle(titleRes)
.setView(layout)
.setPositiveButton(positiveRes) { _, _ ->
val reason = layout.dialogReasonInput.text.toString()
val reason = views.dialogReasonInput.text.toString()
.takeIf { askForReason }
?.takeIf { layout.dialogReasonCheck.isChecked }
?.takeIf { views.dialogReasonCheck.isChecked }
?.takeIf { it.isNotBlank() }
confirmation(reason)
}

View File

@ -26,6 +26,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.SimpleTextWatcher
import im.vector.app.databinding.DialogExportE2eKeysBinding
class ExportKeysDialog {
@ -33,48 +34,45 @@ class ExportKeysDialog {
fun show(activity: Activity, exportKeyDialogListener: ExportKeyDialogListener) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_export_e2e_keys, null)
val views = DialogExportE2eKeysBinding.bind(dialogLayout)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.encryption_export_room_keys)
.setView(dialogLayout)
val passPhrase1EditText = dialogLayout.findViewById<TextInputEditText>(R.id.exportDialogEt)
val passPhrase2EditText = dialogLayout.findViewById<TextInputEditText>(R.id.exportDialogEtConfirm)
val passPhrase2Til = dialogLayout.findViewById<TextInputLayout>(R.id.exportDialogTilConfirm)
val exportButton = dialogLayout.findViewById<Button>(R.id.exportDialogSubmit)
val textWatcher = object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
when {
passPhrase1EditText.text.isNullOrEmpty() -> {
exportButton.isEnabled = false
passPhrase2Til.error = null
views.exportDialogEt.text.isNullOrEmpty() -> {
views.exportDialogSubmit.isEnabled = false
views.exportDialogTilConfirm.error = null
}
passPhrase1EditText.text.toString() == passPhrase2EditText.text.toString() -> {
exportButton.isEnabled = true
passPhrase2Til.error = null
views.exportDialogEt.text.toString() == views.exportDialogEtConfirm.text.toString() -> {
views.exportDialogSubmit.isEnabled = true
views.exportDialogTilConfirm.error = null
}
else -> {
exportButton.isEnabled = false
passPhrase2Til.error = activity.getString(R.string.passphrase_passphrase_does_not_match)
views.exportDialogSubmit.isEnabled = false
views.exportDialogTilConfirm.error = activity.getString(R.string.passphrase_passphrase_does_not_match)
}
}
}
}
passPhrase1EditText.addTextChangedListener(textWatcher)
passPhrase2EditText.addTextChangedListener(textWatcher)
views.exportDialogEt.addTextChangedListener(textWatcher)
views.exportDialogEtConfirm.addTextChangedListener(textWatcher)
val showPassword = dialogLayout.findViewById<ImageView>(R.id.exportDialogShowPassword)
showPassword.setOnClickListener {
passwordVisible = !passwordVisible
passPhrase1EditText.showPassword(passwordVisible)
passPhrase2EditText.showPassword(passwordVisible)
views.exportDialogEt.showPassword(passwordVisible)
views.exportDialogEtConfirm.showPassword(passwordVisible)
showPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
val exportDialog = builder.show()
exportButton.setOnClickListener {
exportKeyDialogListener.onPassphrase(passPhrase1EditText.text.toString())
views.exportDialogSubmit.setOnClickListener {
exportKeyDialogListener.onPassphrase(views.exportDialogEt.text.toString())
exportDialog.dismiss()
}

View File

@ -17,9 +17,9 @@
package im.vector.app.core.dialogs
import android.app.Activity
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import im.vector.app.R
import im.vector.app.databinding.DialogDeviceVerifyBinding
import org.matrix.android.sdk.api.extensions.getFingerprintHumanReadable
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
@ -27,6 +27,7 @@ object ManuallyVerifyDialog {
fun show(activity: Activity, cryptoDeviceInfo: CryptoDeviceInfo, onVerified: (() -> Unit)) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_device_verify, null)
val views = DialogDeviceVerifyBinding.bind(dialogLayout)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.cross_signing_verify_by_text)
.setView(dialogLayout)
@ -35,17 +36,9 @@ object ManuallyVerifyDialog {
}
.setNegativeButton(R.string.cancel, null)
dialogLayout.findViewById<TextView>(R.id.encrypted_device_info_device_name)?.let {
it.text = cryptoDeviceInfo.displayName()
}
dialogLayout.findViewById<TextView>(R.id.encrypted_device_info_device_id)?.let {
it.text = cryptoDeviceInfo.deviceId
}
dialogLayout.findViewById<TextView>(R.id.encrypted_device_info_device_key)?.let {
it.text = cryptoDeviceInfo.getFingerprintHumanReadable()
}
views.encryptedDeviceInfoDeviceName.text = cryptoDeviceInfo.displayName()
views.encryptedDeviceInfoDeviceId.text = cryptoDeviceInfo.deviceId
views.encryptedDeviceInfoDeviceKey.text = cryptoDeviceInfo.getFingerprintHumanReadable()
builder.show()
}

View File

@ -28,6 +28,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.SimpleTextWatcher
import im.vector.app.databinding.DialogPromptPasswordBinding
class PromptPasswordDialog {
@ -35,20 +36,18 @@ class PromptPasswordDialog {
fun show(activity: Activity, listener: (String) -> Unit) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_prompt_password, null)
val passwordTil = dialogLayout.findViewById<TextInputLayout>(R.id.promptPasswordTil)
val passwordEditText = dialogLayout.findViewById<TextInputEditText>(R.id.promptPassword)
val views = DialogPromptPasswordBinding.bind(dialogLayout)
val textWatcher = object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
passwordTil.error = null
views.promptPasswordTil.error = null
}
}
passwordEditText.addTextChangedListener(textWatcher)
views.promptPassword.addTextChangedListener(textWatcher)
val showPassword = dialogLayout.findViewById<ImageView>(R.id.promptPasswordPasswordReveal)
showPassword.setOnClickListener {
passwordVisible = !passwordVisible
passwordEditText.showPassword(passwordVisible)
views.promptPassword.showPassword(passwordVisible)
showPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
@ -73,10 +72,10 @@ class PromptPasswordDialog {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE)
.setOnClickListener {
if (passwordEditText.text.toString().isEmpty()) {
passwordTil.error = activity.getString(R.string.error_empty_field_your_password)
if (views.promptPassword.text.toString().isEmpty()) {
views.promptPasswordTil.error = activity.getString(R.string.error_empty_field_your_password)
} else {
listener.invoke(passwordEditText.text.toString())
listener.invoke(views.promptPassword.text.toString())
dismiss()
}
}

View File

@ -23,6 +23,7 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import im.vector.app.R
import im.vector.app.databinding.DialogRecoveryKeySavedInfoBinding
import me.gujun.android.span.image
import me.gujun.android.span.span
@ -30,10 +31,9 @@ class KeepItSafeDialog {
fun show(activity: Activity) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_recovery_key_saved_info, null)
val views = DialogRecoveryKeySavedInfoBinding.bind(dialogLayout)
val descriptionText = dialogLayout.findViewById<TextView>(R.id.keepItSafeText)
descriptionText.text = span {
views.keepItSafeText.text = span {
span {
image(ContextCompat.getDrawable(activity, R.drawable.ic_check_on)!!)
+" "

View File

@ -35,6 +35,7 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.ImageView
import android.widget.TextView
@ -113,6 +114,7 @@ import im.vector.app.core.utils.saveMedia
import im.vector.app.core.utils.shareMedia
import im.vector.app.core.utils.shareText
import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentRoomDetailBinding
import im.vector.app.features.attachments.AttachmentTypeSelectorView
import im.vector.app.features.attachments.AttachmentsHelper
import im.vector.app.features.attachments.ContactAttachment
@ -232,7 +234,7 @@ class RoomDetailFragment @Inject constructor(
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
private val pillsPostProcessorFactory: PillsPostProcessor.Factory
) :
VectorBaseFragment(),
VectorBaseFragment<FragmentRoomDetailBinding>(),
TimelineEventController.Callback,
VectorInviteView.Callback,
JumpToReadMarkerView.Callback,
@ -278,7 +280,9 @@ class RoomDetailFragment @Inject constructor(
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
private lateinit var scrollOnHighlightedEventCallback: ScrollOnHighlightedEventCallback
override fun getLayoutResId() = R.layout.fragment_room_detail
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomDetailBinding {
return FragmentRoomDetailBinding.inflate(inflater, container, false)
}
override fun getMenuRes() = R.menu.menu_timeline
@ -303,7 +307,7 @@ class RoomDetailFragment @Inject constructor(
sharedCallActionViewModel = activityViewModelProvider.get(SharedActiveCallViewModel::class.java)
attachmentsHelper = AttachmentsHelper(requireContext(), this).register()
keyboardStateUtils = KeyboardStateUtils(requireActivity())
setupToolbar(roomToolbar)
setupToolbar(views.roomToolbar)
setupRecyclerView()
setupComposer()
setupInviteView()
@ -314,7 +318,7 @@ class RoomDetailFragment @Inject constructor(
setupConfBannerView()
setupEmojiPopup()
roomToolbarContentView.debouncedClicks {
views.roomToolbarContentView.debouncedClicks {
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
}
@ -349,7 +353,7 @@ class RoomDetailFragment @Inject constructor(
}
roomDetailViewModel.selectSubscribe(RoomDetailViewState::syncState) { syncState ->
syncStateView.render(syncState)
views.syncStateView.render(syncState)
}
roomDetailViewModel.observeViewEvents {
@ -396,8 +400,8 @@ class RoomDetailFragment @Inject constructor(
private fun handleChatEffect(chatEffect: ChatEffect) {
when (chatEffect) {
ChatEffect.CONFETTI -> {
viewKonfetti.isVisible = true
viewKonfetti.build()
views.viewKonfetti.isVisible = true
views.viewKonfetti.build()
.addColors(Color.YELLOW, Color.GREEN, Color.MAGENTA)
.setDirection(0.0, 359.0)
.setSpeed(2f, 5f)
@ -405,20 +409,20 @@ class RoomDetailFragment @Inject constructor(
.setTimeToLive(2000L)
.addShapes(Shape.Square, Shape.Circle)
.addSizes(Size(12))
.setPosition(-50f, viewKonfetti.width + 50f, -50f, -50f)
.setPosition(-50f, views.viewKonfetti.width + 50f, -50f, -50f)
.streamFor(150, 3000L)
}
ChatEffect.SNOW -> {
viewSnowFall.isVisible = true
viewSnowFall.restartFalling()
views.viewSnowFall.isVisible = true
views.viewSnowFall.restartFalling()
}
}
}
private fun handleStopChatEffects() {
TransitionManager.beginDelayedTransition(rootConstraintLayout)
viewSnowFall.isVisible = false
TransitionManager.beginDelayedTransition(views.rootConstraintLayout)
views.viewSnowFall.isVisible = false
// when gone the effect is a bit buggy
viewKonfetti.isInvisible = true
views.viewKonfetti.isInvisible = true
}
override fun onImageReady(uri: Uri?) {
@ -486,7 +490,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun setupConfBannerView() {
activeConferenceView.callback = object : ActiveConferenceView.Callback {
views.activeConferenceView.callback = object : ActiveConferenceView.Callback {
override fun onTapJoinAudio(jitsiWidget: Widget) {
// need to check if allowed first
roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed(
@ -513,13 +517,13 @@ class RoomDetailFragment @Inject constructor(
private fun setupEmojiPopup() {
val emojiPopup = EmojiPopup
.Builder
.fromRootView(rootConstraintLayout)
.fromRootView(views.rootConstraintLayout)
.setKeyboardAnimationStyle(R.style.emoji_fade_animation_style)
.setOnEmojiPopupShownListener { composerLayout?.composerEmojiButton?.setImageResource(R.drawable.ic_keyboard) }
.setOnEmojiPopupDismissListener { composerLayout?.composerEmojiButton?.setImageResource(R.drawable.ic_insert_emoji) }
.build(composerLayout.composerEditText)
.setOnEmojiPopupShownListener { views.composerLayout.views.composerEmojiButton.setImageResource(R.drawable.ic_keyboard) }
.setOnEmojiPopupDismissListener { views.composerLayout.views.composerEmojiButton.setImageResource(R.drawable.ic_insert_emoji) }
.build(views.composerLayout.views.composerEditText)
composerLayout.composerEmojiButton.debouncedClicks {
views.composerLayout.views.composerEmojiButton.debouncedClicks {
emojiPopup.toggle()
}
}
@ -585,11 +589,11 @@ class RoomDetailFragment @Inject constructor(
override fun onDestroyView() {
timelineEventController.callback = null
timelineEventController.removeModelBuildListener(modelBuildListener)
activeCallView.callback = null
views.activeCallView.callback = null
modelBuildListener = null
autoCompleter.clear()
debouncer.cancelAll()
timelineRecyclerView.cleanup()
views.timelineRecyclerView.cleanup()
super.onDestroyView()
}
@ -601,10 +605,10 @@ class RoomDetailFragment @Inject constructor(
}
private fun setupJumpToBottomView() {
jumpToBottomView.visibility = View.INVISIBLE
jumpToBottomView.debouncedClicks {
views.jumpToBottomView.visibility = View.INVISIBLE
views.jumpToBottomView.debouncedClicks {
roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState)
jumpToBottomView.visibility = View.INVISIBLE
views.jumpToBottomView.visibility = View.INVISIBLE
if (!roomDetailViewModel.timeline.isLive) {
scrollOnNewMessageCallback.forceScrollOnNextUpdate()
roomDetailViewModel.timeline.restartWithEventId(null)
@ -614,22 +618,22 @@ class RoomDetailFragment @Inject constructor(
}
jumpToBottomViewVisibilityManager = JumpToBottomViewVisibilityManager(
jumpToBottomView,
views.jumpToBottomView,
debouncer,
timelineRecyclerView,
views.timelineRecyclerView,
layoutManager
)
}
private fun setupJumpToReadMarkerView() {
jumpToReadMarkerView.callback = this
views.jumpToReadMarkerView.callback = this
}
private fun setupActiveCallView() {
activeCallViewHolder.bind(
activeCallPiP,
activeCallView,
activeCallPiPWrap,
views.activeCallPiP,
views.activeCallView,
views.activeCallPiPWrap,
this
)
}
@ -639,7 +643,7 @@ class RoomDetailFragment @Inject constructor(
if (scrollPosition == null) {
scrollOnHighlightedEventCallback.scheduleScrollTo(action.eventId)
} else {
timelineRecyclerView.stopScroll()
views.timelineRecyclerView.stopScroll()
layoutManager.scrollToPosition(scrollPosition)
}
}
@ -679,7 +683,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun setupNotificationView() {
notificationAreaView.delegate = object : NotificationAreaView.Delegate {
views.notificationAreaView.delegate = object : NotificationAreaView.Delegate {
override fun onTombstoneEventClicked(tombstoneEvent: Event) {
roomDetailViewModel.handle(RoomDetailAction.HandleTombstoneEvent(tombstoneEvent))
}
@ -906,10 +910,10 @@ class RoomDetailFragment @Inject constructor(
private fun renderRegularMode(text: String) {
autoCompleter.exitSpecialMode()
composerLayout.collapse()
views.composerLayout.collapse()
updateComposerText(text)
composerLayout.sendButton.contentDescription = getString(R.string.send)
views.composerLayout.views.sendButton.contentDescription = getString(R.string.send)
}
private fun renderSpecialMode(event: TimelineEvent,
@ -918,7 +922,7 @@ class RoomDetailFragment @Inject constructor(
defaultContent: String) {
autoCompleter.enterSpecialMode()
// switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply {
views.composerLayout.views.composerRelatedMessageTitle.apply {
text = event.senderInfo.disambiguatedDisplayName
setTextColor(matrixItemColorProvider.getColor(MatrixItem.UserItem(event.root.senderId ?: "@")))
}
@ -931,16 +935,16 @@ class RoomDetailFragment @Inject constructor(
val document = parser.parse(messageContent.formattedBody ?: messageContent.body)
formattedBody = eventHtmlRenderer.render(document, pillsPostProcessor)
}
composerLayout.composerRelatedMessageContent.text = (formattedBody ?: nonFormattedBody)
views.composerLayout.views.composerRelatedMessageContent.text = (formattedBody ?: nonFormattedBody)
updateComposerText(defaultContent)
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
composerLayout.sendButton.contentDescription = getString(descriptionRes)
views.composerLayout.views.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
views.composerLayout.views.sendButton.contentDescription = getString(descriptionRes)
avatarRenderer.render(event.senderInfo.toMatrixItem(), composerLayout.composerRelatedMessageAvatar)
avatarRenderer.render(event.senderInfo.toMatrixItem(), views.composerLayout.views.composerRelatedMessageAvatar)
composerLayout.expand {
views.composerLayout.expand {
if (isAdded) {
// need to do it here also when not using quick reply
focusComposerAndShowKeyboard()
@ -951,11 +955,10 @@ class RoomDetailFragment @Inject constructor(
private fun updateComposerText(text: String) {
// Do not update if this is the same text to avoid the cursor to move
if (text != composerLayout.composerEditText.text.toString()) {
if (text != views.composerLayout.text.toString()) {
// Ignore update to avoid saving a draft
composerLayout.composerEditText.setText(text)
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length
?: 0)
views.composerLayout.views.composerEditText.setText(text)
views.composerLayout.views.composerEditText.setSelection(views.composerLayout.text?.length ?: 0)
}
}
@ -982,7 +985,7 @@ class RoomDetailFragment @Inject constructor(
notificationDrawerManager.setCurrentRoom(null)
roomDetailViewModel.handle(RoomDetailAction.SaveDraft(composerLayout.composerEditText.text.toString()))
roomDetailViewModel.handle(RoomDetailAction.SaveDraft(views.composerLayout.text.toString()))
}
private val attachmentFileActivityResultLauncher = registerStartForActivityResult {
@ -1050,14 +1053,14 @@ class RoomDetailFragment @Inject constructor(
timelineEventController.callback = this
timelineEventController.timeline = roomDetailViewModel.timeline
timelineRecyclerView.trackItemsVisibilityChange()
views.timelineRecyclerView.trackItemsVisibilityChange()
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager, timelineEventController)
scrollOnHighlightedEventCallback = ScrollOnHighlightedEventCallback(timelineRecyclerView, layoutManager, timelineEventController)
timelineRecyclerView.layoutManager = layoutManager
timelineRecyclerView.itemAnimator = null
timelineRecyclerView.setHasFixedSize(true)
scrollOnHighlightedEventCallback = ScrollOnHighlightedEventCallback(views.timelineRecyclerView, layoutManager, timelineEventController)
views.timelineRecyclerView.layoutManager = layoutManager
views.timelineRecyclerView.itemAnimator = null
views.timelineRecyclerView.setHasFixedSize(true)
modelBuildListener = OnModelBuildFinishedListener {
it.dispatchTo(stateRestorer)
it.dispatchTo(scrollOnNewMessageCallback)
@ -1066,14 +1069,14 @@ class RoomDetailFragment @Inject constructor(
jumpToBottomViewVisibilityManager.maybeShowJumpToBottomViewVisibilityWithDelay()
}
timelineEventController.addModelBuildListener(modelBuildListener)
timelineRecyclerView.adapter = timelineEventController.adapter
views.timelineRecyclerView.adapter = timelineEventController.adapter
if (vectorPreferences.swipeToReplyIsEnabled()) {
val quickReplyHandler = object : RoomMessageTouchHelperCallback.QuickReplayHandler {
override fun performQuickReplyOnHolder(model: EpoxyModel<*>) {
(model as? AbsMessageItem)?.attributes?.informationData?.let {
val eventId = it.eventId
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(eventId, composerLayout.composerEditText.text.toString()))
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(eventId, views.composerLayout.text.toString()))
}
}
@ -1096,9 +1099,9 @@ class RoomDetailFragment @Inject constructor(
}
val swipeCallback = RoomMessageTouchHelperCallback(requireContext(), R.drawable.ic_reply, quickReplyHandler)
val touchHelper = ItemTouchHelper(swipeCallback)
touchHelper.attachToRecyclerView(timelineRecyclerView)
touchHelper.attachToRecyclerView(views.timelineRecyclerView)
}
timelineRecyclerView.addGlidePreloader(
views.timelineRecyclerView.addGlidePreloader(
epoxyController = timelineEventController,
requestManager = GlideApp.with(this),
preloader = glidePreloader { requestManager, epoxyModel: MessageImageVideoItem, _ ->
@ -1111,7 +1114,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun updateJumpToReadMarkerViewVisibility() {
jumpToReadMarkerView?.post {
views.jumpToReadMarkerView?.post {
withState(roomDetailViewModel) {
val showJumpToUnreadBanner = when (it.unreadState) {
UnreadState.Unknown,
@ -1131,13 +1134,13 @@ class RoomDetailFragment @Inject constructor(
}
}
}
jumpToReadMarkerView?.isVisible = showJumpToUnreadBanner
views.jumpToReadMarkerView?.isVisible = showJumpToUnreadBanner
}
}
}
private fun setupComposer() {
val composerEditText = composerLayout.composerEditText
val composerEditText = views.composerLayout.views.composerEditText
autoCompleter.setup(composerEditText)
observerUserTyping()
@ -1164,12 +1167,12 @@ class RoomDetailFragment @Inject constructor(
} else false
}
composerLayout.callback = object : TextComposerView.Callback {
views.composerLayout.callback = object : TextComposerView.Callback {
override fun onAddAttachment() {
if (!::attachmentTypeSelector.isInitialized) {
attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@RoomDetailFragment)
}
attachmentTypeSelector.show(composerLayout.attachmentButton, keyboardStateUtils.isKeyboardShowing)
attachmentTypeSelector.show(views.composerLayout.views.attachmentButton, keyboardStateUtils.isKeyboardShowing)
}
override fun onSendMessage(text: CharSequence) {
@ -1177,7 +1180,7 @@ class RoomDetailFragment @Inject constructor(
}
override fun onCloseRelatedMessage() {
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(composerLayout.text.toString(), false))
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(views.composerLayout.text.toString(), false))
}
override fun onRichContentSelected(contentUri: Uri): Boolean {
@ -1193,14 +1196,14 @@ class RoomDetailFragment @Inject constructor(
}
if (text.isNotBlank()) {
// We collapse ASAP, if not there will be a slight anoying delay
composerLayout.collapse(true)
views.composerLayout.collapse(true)
lockSendButton = true
roomDetailViewModel.handle(RoomDetailAction.SendMessage(text, vectorPreferences.isMarkdownEnabled()))
}
}
private fun observerUserTyping() {
composerLayout.composerEditText.textChanges()
views.composerLayout.views.composerEditText.textChanges()
.skipInitialValue()
.debounce(300, TimeUnit.MILLISECONDS)
.map { it.isNotEmpty() }
@ -1221,39 +1224,39 @@ class RoomDetailFragment @Inject constructor(
}
private fun setupInviteView() {
inviteView.callback = this
views.inviteView.callback = this
}
override fun invalidate() = withState(roomDetailViewModel) { state ->
invalidateOptionsMenu()
val summary = state.asyncRoomSummary()
renderToolbar(summary, state.typingMessage)
activeConferenceView.render(state)
views.activeConferenceView.render(state)
val inviter = state.asyncInviter()
if (summary?.membership == Membership.JOIN) {
jumpToBottomView.count = summary.notificationCount
jumpToBottomView.drawBadge = summary.hasUnreadMessages
views.jumpToBottomView.count = summary.notificationCount
views.jumpToBottomView.drawBadge = summary.hasUnreadMessages
scrollOnHighlightedEventCallback.timeline = roomDetailViewModel.timeline
timelineEventController.update(state)
inviteView.visibility = View.GONE
views.inviteView.visibility = View.GONE
if (state.tombstoneEvent == null) {
if (state.canSendMessage) {
composerLayout.visibility = View.VISIBLE
composerLayout.setRoomEncrypted(summary.isEncrypted, summary.roomEncryptionTrustLevel)
notificationAreaView.render(NotificationAreaView.State.Hidden)
views.composerLayout.visibility = View.VISIBLE
views.composerLayout.setRoomEncrypted(summary.isEncrypted, summary.roomEncryptionTrustLevel)
views.notificationAreaView.render(NotificationAreaView.State.Hidden)
} else {
composerLayout.visibility = View.GONE
notificationAreaView.render(NotificationAreaView.State.NoPermissionToPost)
views.composerLayout.visibility = View.GONE
views.notificationAreaView.render(NotificationAreaView.State.NoPermissionToPost)
}
} else {
composerLayout.visibility = View.GONE
notificationAreaView.render(NotificationAreaView.State.Tombstone(state.tombstoneEvent))
views.composerLayout.visibility = View.GONE
views.notificationAreaView.render(NotificationAreaView.State.Tombstone(state.tombstoneEvent))
}
} else if (summary?.membership == Membership.INVITE && inviter != null) {
inviteView.visibility = View.VISIBLE
inviteView.render(inviter, VectorInviteView.Mode.LARGE, state.changeMembershipState)
views.inviteView.visibility = View.VISIBLE
views.inviteView.render(inviter, VectorInviteView.Mode.LARGE, state.changeMembershipState)
// Intercept click event
inviteView.setOnClickListener { }
views.inviteView.setOnClickListener { }
} else if (state.asyncInviter.complete) {
vectorBaseActivity.finish()
}
@ -1261,14 +1264,14 @@ class RoomDetailFragment @Inject constructor(
private fun renderToolbar(roomSummary: RoomSummary?, typingMessage: String?) {
if (roomSummary == null) {
roomToolbarContentView.isClickable = false
views.roomToolbarContentView.isClickable = false
} else {
roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), roomToolbarAvatarImageView)
views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
views.roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
renderSubTitle(typingMessage, roomSummary.topic)
roomToolbarDecorationImageView.let {
views.roomToolbarDecorationImageView.let {
it.setImageResource(roomSummary.roomEncryptionTrustLevel.toImageRes())
it.isVisible = roomSummary.roomEncryptionTrustLevel != null
}
@ -1278,7 +1281,7 @@ class RoomDetailFragment @Inject constructor(
private fun renderSubTitle(typingMessage: String?, topic: String) {
// TODO Temporary place to put typing data
val subtitle = typingMessage?.takeIf { it.isNotBlank() } ?: topic
roomToolbarSubtitleView.apply {
views.roomToolbarSubtitleView.apply {
setTextOrHide(subtitle)
if (typingMessage.isNullOrBlank()) {
setTextColor(ThemeUtils.getColor(requireContext(), R.attr.vctr_toolbar_secondary_text_color))
@ -1294,9 +1297,11 @@ class RoomDetailFragment @Inject constructor(
when (async) {
is Loading -> {
// TODO Better handling progress
/* TODO BMA Yes, improve that
vectorBaseActivity.showWaitingView()
vectorBaseActivity.waitingStatusText.visibility = View.VISIBLE
vectorBaseActivity.waitingStatusText.text = getString(R.string.joining_room)
*/
}
is Success -> {
navigator.openRoom(vectorBaseActivity, async())
@ -1544,8 +1549,8 @@ class RoomDetailFragment @Inject constructor(
mediaData = mediaData,
view = view
) { pairs ->
pairs.add(Pair(roomToolbar, ViewCompat.getTransitionName(roomToolbar) ?: ""))
pairs.add(Pair(composerLayout, ViewCompat.getTransitionName(composerLayout) ?: ""))
pairs.add(Pair(views.roomToolbar, ViewCompat.getTransitionName(views.roomToolbar) ?: ""))
pairs.add(Pair(views.composerLayout, ViewCompat.getTransitionName(views.composerLayout) ?: ""))
}
}
@ -1556,8 +1561,8 @@ class RoomDetailFragment @Inject constructor(
mediaData = mediaData,
view = view
) { pairs ->
pairs.add(Pair(roomToolbar, ViewCompat.getTransitionName(roomToolbar) ?: ""))
pairs.add(Pair(composerLayout, ViewCompat.getTransitionName(composerLayout) ?: ""))
pairs.add(Pair(views.roomToolbar, ViewCompat.getTransitionName(views.roomToolbar) ?: ""))
pairs.add(Pair(views.composerLayout, ViewCompat.getTransitionName(views.composerLayout) ?: ""))
}
}
@ -1785,13 +1790,13 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
}
is EventSharedAction.Edit -> {
roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString()))
roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, views.composerLayout.text.toString()))
}
is EventSharedAction.Quote -> {
roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString()))
roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, views.composerLayout.text.toString()))
}
is EventSharedAction.Reply -> {
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString()))
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, views.composerLayout.text.toString()))
}
is EventSharedAction.CopyPermalink -> {
val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
@ -1857,13 +1862,13 @@ class RoomDetailFragment @Inject constructor(
*/
@SuppressLint("SetTextI18n")
private fun insertUserDisplayNameInTextEditor(userId: String) {
val startToCompose = composerLayout.composerEditText.text.isNullOrBlank()
val startToCompose = views.composerLayout.text.isNullOrBlank()
if (startToCompose
&& userId == session.myUserId) {
// Empty composer, current user: start an emote
composerLayout.composerEditText.setText(Command.EMOTE.command + " ")
composerLayout.composerEditText.setSelection(Command.EMOTE.length)
views.composerLayout.views.composerEditText.setText(Command.EMOTE.command + " ")
views.composerLayout.views.composerEditText.setSelection(Command.EMOTE.length)
} else {
val roomMember = roomDetailViewModel.getMember(userId)
// TODO move logic outside of fragment
@ -1879,7 +1884,7 @@ class RoomDetailFragment @Inject constructor(
requireContext(),
MatrixItem.UserItem(userId, displayName, roomMember?.avatarUrl)
)
.also { it.bind(composerLayout.composerEditText) },
.also { it.bind(views.composerLayout.views.composerEditText) },
0,
displayName.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
@ -1889,11 +1894,11 @@ class RoomDetailFragment @Inject constructor(
if (startToCompose) {
if (displayName.startsWith("/")) {
// Ensure displayName will not be interpreted as a Slash command
composerLayout.composerEditText.append("\\")
views.composerLayout.views.composerEditText.append("\\")
}
composerLayout.composerEditText.append(pill)
views.composerLayout.views.composerEditText.append(pill)
} else {
composerLayout.composerEditText.text?.insert(composerLayout.composerEditText.selectionStart, pill)
views.composerLayout.views.composerEditText.text?.insert(views.composerLayout.views.composerEditText.selectionStart, pill)
}
}
}
@ -1902,8 +1907,8 @@ class RoomDetailFragment @Inject constructor(
}
private fun focusComposerAndShowKeyboard() {
if (composerLayout.isVisible) {
composerLayout.composerEditText.showKeyboard(andRequestFocus = true)
if (views.composerLayout.isVisible) {
views.composerLayout.views.composerEditText.showKeyboard(andRequestFocus = true)
}
}
@ -1933,7 +1938,7 @@ class RoomDetailFragment @Inject constructor(
// JumpToReadMarkerView.Callback
override fun onJumpToReadMarkerClicked() = withState(roomDetailViewModel) {
jumpToReadMarkerView.isVisible = false
views.jumpToReadMarkerView.isVisible = false
if (it.unreadState is UnreadState.HasUnread) {
roomDetailViewModel.handle(RoomDetailAction.NavigateToEvent(it.unreadState.firstUnreadEventId, false))
}

View File

@ -20,7 +20,12 @@ import android.content.Context
import android.net.Uri
import android.text.Editable
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.toSpannable
@ -31,6 +36,7 @@ import androidx.transition.Transition
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import im.vector.app.R
import im.vector.app.databinding.ComposerLayoutBinding
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
@ -38,7 +44,9 @@ import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
* Encapsulate the timeline composer UX.
*
*/
class TextComposerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
class TextComposerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) {
interface Callback : ComposerEditText.Callback {
@ -47,6 +55,8 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
fun onAddAttachment()
}
val views: ComposerLayoutBinding
var callback: Callback? = null
private var currentConstraintSetId: Int = -1
@ -54,27 +64,30 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
private val animationDuration = 100L
val text: Editable?
get() = composerEditText.text
get() = views.composerEditText.text
init {
inflate(context, R.layout.composer_layout, this)
views = ComposerLayoutBinding.bind(this)
collapse(false)
composerEditText.callback = object : ComposerEditText.Callback {
views.composerEditText.callback = object : ComposerEditText.Callback {
override fun onRichContentSelected(contentUri: Uri): Boolean {
return callback?.onRichContentSelected(contentUri) ?: false
}
}
composerRelatedMessageCloseButton.setOnClickListener {
views.composerRelatedMessageCloseButton.setOnClickListener {
collapse()
callback?.onCloseRelatedMessage()
}
sendButton.setOnClickListener {
views.sendButton.setOnClickListener {
val textMessage = text?.toSpannable() ?: ""
callback?.onSendMessage(textMessage)
}
attachmentButton.setOnClickListener {
views.attachmentButton.setOnClickListener {
callback?.onAddAttachment()
}
}
@ -104,7 +117,7 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
ConstraintSet().also {
it.clone(context, currentConstraintSetId)
// in case shield is hidden, we will have glitch without this
it.getConstraint(R.id.composerShieldImageView).propertySet.visibility = composerShieldImageView.visibility
it.getConstraint(R.id.composerShieldImageView).propertySet.visibility = views.composerShieldImageView.visibility
it.applyTo(this)
}
}
@ -134,17 +147,17 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
fun setRoomEncrypted(isEncrypted: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) {
if (isEncrypted) {
composerEditText.setHint(R.string.room_message_placeholder)
composerShieldImageView.isVisible = true
views.composerEditText.setHint(R.string.room_message_placeholder)
views.composerShieldImageView.isVisible = true
val shieldRes = when (roomEncryptionTrustLevel) {
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
else -> R.drawable.ic_shield_black
}
composerShieldImageView.setImageResource(shieldRes)
views.composerShieldImageView.setImageResource(shieldRes)
} else {
composerEditText.setHint(R.string.room_message_placeholder)
composerShieldImageView.isVisible = false
views.composerEditText.setHint(R.string.room_message_placeholder)
views.composerShieldImageView.isVisible = false
}
}
}

View File

@ -23,6 +23,8 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
@ -43,6 +45,7 @@ import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.DialogShareQrCodeBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheet
@ -72,6 +75,14 @@ class RoomMemberProfileFragment @Inject constructor(
) : VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomMemberProfileController.Callback {
private lateinit var memberProfileStateView: StateView
private lateinit var memberProfileInfoContainer: View
private lateinit var memberProfileDecorationImageView: ImageView
private lateinit var memberProfileAvatarView: ImageView
private lateinit var memberProfileNameView: TextView
private lateinit var memberProfileIdView: TextView
private lateinit var memberProfilePowerLevelView: TextView
private val fragmentArgs: RoomMemberProfileArgs by args()
private val viewModel: RoomMemberProfileViewModel by fragmentViewModel()
@ -85,27 +96,28 @@ class RoomMemberProfileFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar(matrixProfileToolbar)
val headerView = matrixProfileHeaderView.let {
setupToolbar(views.matrixProfileToolbar)
val headerView = views.matrixProfileHeaderView.let {
it.layoutResource = R.layout.view_stub_room_member_profile_header
it.inflate()
}
findHeaderSubViews(headerView)
memberProfileStateView.eventCallback = object : StateView.EventCallback {
override fun onRetryClicked() {
viewModel.handle(RoomMemberProfileAction.RetryFetchingInfo)
}
}
memberProfileStateView.contentView = memberProfileInfoContainer
matrixProfileRecyclerView.configureWith(roomMemberProfileController, hasFixedSize = true, disableItemAnimation = true)
views.matrixProfileRecyclerView.configureWith(roomMemberProfileController, hasFixedSize = true, disableItemAnimation = true)
roomMemberProfileController.callback = this
appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView,
listOf(
matrixProfileToolbarAvatarImageView,
matrixProfileToolbarTitleView,
matrixProfileDecorationToolbarAvatarImageView
views.matrixProfileToolbarAvatarImageView,
views.matrixProfileToolbarTitleView,
views.matrixProfileDecorationToolbarAvatarImageView
)
)
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
views.matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
viewModel.observeViewEvents {
when (it) {
is RoomMemberProfileViewEvents.Loading -> showLoading(it.message)
@ -124,6 +136,16 @@ class RoomMemberProfileFragment @Inject constructor(
setupLongClicks()
}
private fun findHeaderSubViews(headerView: View) {
memberProfileStateView = headerView.findViewById(R.id.memberProfileStateView)
memberProfileInfoContainer = headerView.findViewById(R.id.memberProfileInfoContainer)
memberProfileNameView = headerView.findViewById(R.id.roomProfileNameView)
memberProfileIdView = headerView.findViewById(R.id.memberProfileIdView)
memberProfileAvatarView = headerView.findViewById(R.id.roomProfileAvatarView)
memberProfilePowerLevelView = headerView.findViewById(R.id.memberProfilePowerLevelView)
memberProfileDecorationImageView = headerView.findViewById(R.id.roomProfileDecorationImageView)
}
private fun setupLongClicks() {
memberProfileNameView.copyOnLongClick()
memberProfileIdView.copyOnLongClick()
@ -171,23 +193,23 @@ class RoomMemberProfileFragment @Inject constructor(
}
override fun onDestroyView() {
matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener)
views.matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener)
roomMemberProfileController.callback = null
appBarStateChangeListener = null
matrixProfileRecyclerView.cleanup()
views.matrixProfileRecyclerView.cleanup()
super.onDestroyView()
}
override fun invalidate() = withState(viewModel) { state ->
when (val asyncUserMatrixItem = state.userMatrixItem) {
is Incomplete -> {
matrixProfileToolbarTitleView.text = state.userId
avatarRenderer.render(MatrixItem.UserItem(state.userId, null, null), matrixProfileToolbarAvatarImageView)
views.matrixProfileToolbarTitleView.text = state.userId
avatarRenderer.render(MatrixItem.UserItem(state.userId, null, null), views.matrixProfileToolbarAvatarImageView)
memberProfileStateView.state = StateView.State.Loading
}
is Fail -> {
avatarRenderer.render(MatrixItem.UserItem(state.userId, null, null), matrixProfileToolbarAvatarImageView)
matrixProfileToolbarTitleView.text = state.userId
avatarRenderer.render(MatrixItem.UserItem(state.userId, null, null), views.matrixProfileToolbarAvatarImageView)
views.matrixProfileToolbarTitleView.text = state.userId
val failureMessage = errorFormatter.toHumanReadable(asyncUserMatrixItem.error)
memberProfileStateView.state = StateView.State.Error(failureMessage)
}
@ -197,9 +219,9 @@ class RoomMemberProfileFragment @Inject constructor(
memberProfileIdView.text = userMatrixItem.id
val bestName = userMatrixItem.getBestName()
memberProfileNameView.text = bestName
matrixProfileToolbarTitleView.text = bestName
views.matrixProfileToolbarTitleView.text = bestName
avatarRenderer.render(userMatrixItem, memberProfileAvatarView)
avatarRenderer.render(userMatrixItem, matrixProfileToolbarAvatarImageView)
avatarRenderer.render(userMatrixItem, views.matrixProfileToolbarAvatarImageView)
if (state.isRoomEncrypted) {
memberProfileDecorationImageView.isVisible = true
@ -217,15 +239,15 @@ class RoomMemberProfileFragment @Inject constructor(
}
memberProfileDecorationImageView.setImageResource(icon)
matrixProfileDecorationToolbarAvatarImageView.setImageResource(icon)
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(icon)
} else {
// Legacy
if (state.allDevicesAreTrusted) {
memberProfileDecorationImageView.setImageResource(R.drawable.ic_shield_trusted)
matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_trusted)
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_trusted)
} else {
memberProfileDecorationImageView.setImageResource(R.drawable.ic_shield_warning)
matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_warning)
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_warning)
}
}
} else {
@ -235,7 +257,7 @@ class RoomMemberProfileFragment @Inject constructor(
memberProfileAvatarView.setOnClickListener { view ->
onAvatarClicked(view, userMatrixItem)
}
matrixProfileToolbarAvatarImageView.setOnClickListener { view ->
views.matrixProfileToolbarAvatarImageView.setOnClickListener { view ->
onAvatarClicked(view, userMatrixItem)
}
}
@ -302,8 +324,8 @@ class RoomMemberProfileFragment @Inject constructor(
private fun handleShareRoomMemberProfile(permalink: String) {
val view = layoutInflater.inflate(R.layout.dialog_share_qr_code, null)
val qrCode = view.findViewById<im.vector.app.core.ui.views.QrCodeImageView>(R.id.itemShareQrCodeImage)
qrCode.setData(permalink)
val views = DialogShareQrCodeBinding.bind(view)
views.itemShareQrCodeImage.setData(permalink)
AlertDialog.Builder(requireContext())
.setView(view)
.setNeutralButton(R.string.ok, null)

View File

@ -23,6 +23,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.databinding.DialogEditPowerLevelBinding
import org.matrix.android.sdk.api.session.room.powerlevels.Role
@ -30,28 +31,29 @@ object EditPowerLevelDialogs {
fun showChoice(activity: Activity, currentRole: Role, listener: (Int) -> Unit) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null)
dialogLayout.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId ->
dialogLayout.powerLevelCustomEditLayout.isVisible = checkedId == R.id.powerLevelCustomRadio
val views = DialogEditPowerLevelBinding.bind(dialogLayout)
views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId ->
views.powerLevelCustomEditLayout.isVisible = checkedId == R.id.powerLevelCustomRadio
}
dialogLayout.powerLevelCustomEdit.setText(currentRole.value.toString())
views.powerLevelCustomEdit.setText(currentRole.value.toString())
when (currentRole) {
Role.Admin -> dialogLayout.powerLevelAdminRadio.isChecked = true
Role.Moderator -> dialogLayout.powerLevelModeratorRadio.isChecked = true
Role.Default -> dialogLayout.powerLevelDefaultRadio.isChecked = true
else -> dialogLayout.powerLevelCustomRadio.isChecked = true
Role.Admin -> views.powerLevelAdminRadio.isChecked = true
Role.Moderator -> views.powerLevelModeratorRadio.isChecked = true
Role.Default -> views.powerLevelDefaultRadio.isChecked = true
else -> views.powerLevelCustomRadio.isChecked = true
}
AlertDialog.Builder(activity)
.setTitle(R.string.power_level_edit_title)
.setView(dialogLayout)
.setPositiveButton(R.string.edit) { _, _ ->
val newValue = when (dialogLayout.powerLevelRadioGroup.checkedRadioButtonId) {
val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) {
R.id.powerLevelAdminRadio -> Role.Admin.value
R.id.powerLevelModeratorRadio -> Role.Moderator.value
R.id.powerLevelDefaultRadio -> Role.Default.value
else -> {
dialogLayout.powerLevelCustomEdit.text?.toString()?.toInt() ?: currentRole.value
views.powerLevelCustomEdit.text?.toString()?.toInt() ?: currentRole.value
}
}
listener(newValue)

View File

@ -29,6 +29,7 @@ import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
import im.vector.app.features.room.RequireActiveMembershipViewEvents
import im.vector.app.features.room.RequireActiveMembershipViewModel
@ -41,7 +42,7 @@ import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
import javax.inject.Inject
class RoomProfileActivity :
VectorBaseActivity(),
VectorBaseActivity<ActivitySimpleBinding>(),
ToolbarConfigurable,
RequireActiveMembershipViewModel.Factory {
@ -81,7 +82,9 @@ class RoomProfileActivity :
injector.inject(this)
}
override fun getLayoutRes() = R.layout.activity_simple
override fun getBinding(): ActivitySimpleBinding {
return ActivitySimpleBinding.inflate(layoutInflater)
}
override fun initUiAndData() {
sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)

View File

@ -23,6 +23,8 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.pm.ShortcutManagerCompat
@ -42,7 +44,6 @@ import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.features.crypto.util.toImageRes
import im.vector.app.features.home.AvatarRenderer
@ -70,9 +71,15 @@ class RoomProfileFragment @Inject constructor(
private val roomProfileController: RoomProfileController,
private val avatarRenderer: AvatarRenderer,
val roomProfileViewModelFactory: RoomProfileViewModel.Factory
) : VectorBaseFragment<FragmentMatrixProfileBinding>(),
) :
VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomProfileController.Callback {
private lateinit var roomProfileDecorationImageView: ImageView
private lateinit var roomProfileAvatarView: ImageView
private lateinit var roomProfileAliasView: TextView
private lateinit var roomProfileNameView: TextView
private val roomProfileArgs: RoomProfileArgs by args()
private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
@ -90,20 +97,21 @@ class RoomProfileFragment @Inject constructor(
super.onViewCreated(view, savedInstanceState)
roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
val headerView = matrixProfileHeaderView.let {
val headerView = views.matrixProfileHeaderView.let {
it.layoutResource = R.layout.view_stub_room_profile_header
it.inflate()
}
findHeaderSubViews(headerView)
setupWaitingView()
setupToolbar(matrixProfileToolbar)
setupToolbar(views.matrixProfileToolbar)
setupRecyclerView()
appBarStateChangeListener = MatrixItemAppBarStateChangeListener(
headerView,
listOf(matrixProfileToolbarAvatarImageView,
matrixProfileToolbarTitleView,
matrixProfileDecorationToolbarAvatarImageView)
listOf(views.matrixProfileToolbarAvatarImageView,
views. matrixProfileToolbarTitleView,
views. matrixProfileDecorationToolbarAvatarImageView)
)
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
views.matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
roomProfileViewModel.observeViewEvents {
when (it) {
is RoomProfileViewEvents.Loading -> showLoading(it.message)
@ -119,9 +127,16 @@ class RoomProfileFragment @Inject constructor(
setupLongClicks()
}
private fun findHeaderSubViews(headerView: View) {
roomProfileNameView = headerView.findViewById(R.id.roomProfileNameView)
roomProfileAliasView = headerView.findViewById(R.id.roomProfileAliasView)
roomProfileAvatarView = headerView.findViewById(R.id.roomProfileAvatarView)
roomProfileDecorationImageView = headerView.findViewById(R.id.roomProfileDecorationImageView)
}
private fun setupWaitingView() {
waitingStatusText.setText(R.string.please_wait)
waitingStatusText.isVisible = true
views.waitingView.waitingStatusText.setText(R.string.please_wait)
views.waitingView.waitingStatusText.isVisible = true
}
private fun setupLongClicks() {
@ -157,18 +172,18 @@ class RoomProfileFragment @Inject constructor(
private fun setupRecyclerView() {
roomProfileController.callback = this
matrixProfileRecyclerView.configureWith(roomProfileController, hasFixedSize = true, disableItemAnimation = true)
views.matrixProfileRecyclerView.configureWith(roomProfileController, hasFixedSize = true, disableItemAnimation = true)
}
override fun onDestroyView() {
super.onDestroyView()
matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener)
matrixProfileRecyclerView.cleanup()
views.matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener)
views.matrixProfileRecyclerView.cleanup()
appBarStateChangeListener = null
}
override fun invalidate() = withState(roomProfileViewModel) { state ->
waiting_view.isVisible = state.isLoading
views.waitingView.root.isVisible = state.isLoading
state.roomSummary()?.also {
if (it.membership.isLeft()) {
@ -176,19 +191,19 @@ class RoomProfileFragment @Inject constructor(
activity?.finish()
} else {
roomProfileNameView.text = it.displayName
matrixProfileToolbarTitleView.text = it.displayName
views.matrixProfileToolbarTitleView.text = it.displayName
roomProfileAliasView.setTextOrHide(it.canonicalAlias)
val matrixItem = it.toMatrixItem()
avatarRenderer.render(matrixItem, roomProfileAvatarView)
avatarRenderer.render(matrixItem, matrixProfileToolbarAvatarImageView)
avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView)
roomProfileDecorationImageView.isVisible = it.roomEncryptionTrustLevel != null
roomProfileDecorationImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
matrixProfileDecorationToolbarAvatarImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
roomProfileAvatarView.setOnClickListener { view ->
onAvatarClicked(view, matrixItem)
}
matrixProfileToolbarAvatarImageView.setOnClickListener { view ->
views.matrixProfileToolbarAvatarImageView.setOnClickListener { view ->
onAvatarClicked(view, matrixItem)
}
}

View File

@ -61,29 +61,29 @@ class RoomMemberListFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
roomMemberListController.callback = this
setupToolbar(roomSettingsToolbar)
setupToolbar(views.roomSettingGeneric.roomSettingsToolbar)
setupSearchView()
setupInviteUsersButton()
views.roomSettingsRecyclerView.configureWith(roomMemberListController, hasFixedSize = true)
views.roomSettingGeneric.roomSettingsRecyclerView.configureWith(roomMemberListController, hasFixedSize = true)
}
private fun setupInviteUsersButton() {
inviteUsersButton.debouncedClicks {
views.inviteUsersButton.debouncedClicks {
navigator.openInviteUsersToRoom(requireContext(), roomProfileArgs.roomId)
}
// Hide FAB when list is scrolling
views.roomSettingsRecyclerView.addOnScrollListener(
views.roomSettingGeneric.roomSettingsRecyclerView.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
if (withState(viewModel) { it.actionsPermissions.canInvite }) {
inviteUsersButton.show()
views.inviteUsersButton.show()
}
}
RecyclerView.SCROLL_STATE_DRAGGING,
RecyclerView.SCROLL_STATE_SETTLING -> {
inviteUsersButton.hide()
views.inviteUsersButton.hide()
}
}
}
@ -92,8 +92,8 @@ class RoomMemberListFragment @Inject constructor(
}
private fun setupSearchView() {
searchView.queryHint = getString(R.string.search_members_hint)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
views.roomSettingGeneric.searchView.queryHint = getString(R.string.search_members_hint)
views.roomSettingGeneric.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return true
}
@ -106,16 +106,16 @@ class RoomMemberListFragment @Inject constructor(
}
override fun onDestroyView() {
views.roomSettingsRecyclerView.cleanup()
views.roomSettingGeneric.roomSettingsRecyclerView.cleanup()
super.onDestroyView()
}
override fun invalidate() = withState(viewModel) { viewState ->
roomMemberListController.setData(viewState)
renderRoomSummary(viewState)
inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite
views.inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite
// Display filter only if there are more than 2 members in this room
searchViewAppBarLayout.isVisible = viewState.roomSummary()?.otherMemberIds.orEmpty().size > 1
views.roomSettingGeneric.searchViewAppBarLayout.isVisible = viewState.roomSummary()?.otherMemberIds.orEmpty().size > 1
}
override fun onRoomMemberClicked(roomMember: RoomMemberSummary) {
@ -140,8 +140,8 @@ class RoomMemberListFragment @Inject constructor(
private fun renderRoomSummary(state: RoomMemberListViewState) {
state.roomSummary()?.let {
roomSettingsToolbarTitleView.text = it.displayName
avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
views.roomSettingGeneric.roomSettingsToolbarTitleView.text = it.displayName
avatarRenderer.render(it.toMatrixItem(), views.roomSettingGeneric.roomSettingsToolbarAvatarImageView)
}
}
}

View File

@ -22,6 +22,7 @@ import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import im.vector.app.R
import im.vector.app.databinding.DialogBackgroundSyncModeBinding
class BackgroundSyncModeChooserDialog : DialogFragment() {
@ -31,25 +32,26 @@ class BackgroundSyncModeChooserDialog : DialogFragment() {
val initialMode = BackgroundSyncMode.fromString(arguments?.getString(ARG_INITIAL_MODE))
val view = requireActivity().layoutInflater.inflate(R.layout.dialog_background_sync_mode, null)
val views = DialogBackgroundSyncModeBinding.bind(view)
val dialog = AlertDialog.Builder(requireActivity())
.setTitle(R.string.settings_background_fdroid_sync_mode)
.setView(view)
.setPositiveButton(R.string.cancel, null)
.create()
view.findViewById<View>(R.id.backgroundSyncModeBattery).setOnClickListener {
views.backgroundSyncModeBattery.setOnClickListener {
interactionListener
?.takeIf { initialMode != BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY }
?.onOptionSelected(BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY)
dialog.dismiss()
}
view.findViewById<View>(R.id.backgroundSyncModeReal).setOnClickListener {
views.backgroundSyncModeReal.setOnClickListener {
interactionListener
?.takeIf { initialMode != BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME }
?.onOptionSelected(BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME)
dialog.dismiss()
}
view.findViewById<View>(R.id.backgroundSyncModeOff).setOnClickListener {
views.backgroundSyncModeOff.setOnClickListener {
interactionListener
?.takeIf { initialMode != BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED }
?.onOptionSelected(BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED)

View File

@ -49,6 +49,7 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.TextUtils
import im.vector.app.core.utils.getSizeOfFiles
import im.vector.app.core.utils.toast
import im.vector.app.databinding.DialogChangePasswordBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import im.vector.app.features.workers.signout.SignOutUiWorker
@ -352,25 +353,18 @@ class VectorSettingsGeneralFragment @Inject constructor(
private fun onPasswordUpdateClick() {
activity?.let { activity ->
val view: ViewGroup = activity.layoutInflater.inflate(R.layout.dialog_change_password, null) as ViewGroup
val showPassword: ImageView = view.findViewById(R.id.change_password_show_passwords)
val oldPasswordTil: TextInputLayout = view.findViewById(R.id.change_password_old_pwd_til)
val oldPasswordText: TextInputEditText = view.findViewById(R.id.change_password_old_pwd_text)
val newPasswordText: TextInputEditText = view.findViewById(R.id.change_password_new_pwd_text)
val confirmNewPasswordTil: TextInputLayout = view.findViewById(R.id.change_password_confirm_new_pwd_til)
val confirmNewPasswordText: TextInputEditText = view.findViewById(R.id.change_password_confirm_new_pwd_text)
val changePasswordLoader: View = view.findViewById(R.id.change_password_loader)
val views = DialogChangePasswordBinding.bind(view)
var passwordShown = false
showPassword.setOnClickListener {
views.changePasswordShowPasswords.setOnClickListener {
passwordShown = !passwordShown
oldPasswordText.showPassword(passwordShown)
newPasswordText.showPassword(passwordShown)
confirmNewPasswordText.showPassword(passwordShown)
views.changePasswordOldPwdText.showPassword(passwordShown)
views.changePasswordNewPwdText.showPassword(passwordShown)
views.changePasswordConfirmNewPwdText.showPassword(passwordShown)
showPassword.setImageResource(if (passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.changePasswordShowPasswords.setImageResource(if (passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
val dialog = AlertDialog.Builder(activity)
@ -389,53 +383,53 @@ class VectorSettingsGeneralFragment @Inject constructor(
updateButton.isEnabled = false
fun updateUi() {
val oldPwd = oldPasswordText.text.toString()
val newPwd = newPasswordText.text.toString()
val newConfirmPwd = confirmNewPasswordText.text.toString()
val oldPwd = views.changePasswordOldPwdText.text.toString()
val newPwd = views.changePasswordNewPwdText.text.toString()
val newConfirmPwd = views.changePasswordConfirmNewPwdText.text.toString()
updateButton.isEnabled = oldPwd.isNotEmpty() && newPwd.isNotEmpty() && newPwd == newConfirmPwd
if (newPwd.isNotEmpty() && newConfirmPwd.isNotEmpty() && newPwd != newConfirmPwd) {
confirmNewPasswordTil.error = getString(R.string.passwords_do_not_match)
views.changePasswordConfirmNewPwdTil.error = getString(R.string.passwords_do_not_match)
}
}
oldPasswordText.addTextChangedListener(object : SimpleTextWatcher() {
views.changePasswordOldPwdText.addTextChangedListener(object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
oldPasswordTil.error = null
views.changePasswordOldPwdTil.error = null
updateUi()
}
})
newPasswordText.addTextChangedListener(object : SimpleTextWatcher() {
views.changePasswordNewPwdText.addTextChangedListener(object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
confirmNewPasswordTil.error = null
views.changePasswordConfirmNewPwdTil.error = null
updateUi()
}
})
confirmNewPasswordText.addTextChangedListener(object : SimpleTextWatcher() {
views.changePasswordConfirmNewPwdText.addTextChangedListener(object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
confirmNewPasswordTil.error = null
views.changePasswordConfirmNewPwdTil.error = null
updateUi()
}
})
fun showPasswordLoadingView(toShow: Boolean) {
if (toShow) {
showPassword.isEnabled = false
oldPasswordText.isEnabled = false
newPasswordText.isEnabled = false
confirmNewPasswordText.isEnabled = false
changePasswordLoader.isVisible = true
views.changePasswordShowPasswords.isEnabled = false
views.changePasswordOldPwdText.isEnabled = false
views.changePasswordNewPwdText.isEnabled = false
views.changePasswordConfirmNewPwdText.isEnabled = false
views.changePasswordLoader.isVisible = true
updateButton.isEnabled = false
cancelButton.isEnabled = false
} else {
showPassword.isEnabled = true
oldPasswordText.isEnabled = true
newPasswordText.isEnabled = true
confirmNewPasswordText.isEnabled = true
changePasswordLoader.isVisible = false
views.changePasswordShowPasswords.isEnabled = true
views.changePasswordOldPwdText.isEnabled = true
views.changePasswordNewPwdText.isEnabled = true
views.changePasswordConfirmNewPwdText.isEnabled = true
views.changePasswordLoader.isVisible = false
updateButton.isEnabled = true
cancelButton.isEnabled = true
}
@ -444,13 +438,13 @@ class VectorSettingsGeneralFragment @Inject constructor(
updateButton.setOnClickListener {
if (passwordShown) {
// Hide passwords during processing
showPassword.performClick()
views.changePasswordShowPasswords.performClick()
}
view.hideKeyboard()
val oldPwd = oldPasswordText.text.toString()
val newPwd = newPasswordText.text.toString()
val oldPwd = views.changePasswordOldPwdText.text.toString()
val newPwd = views.changePasswordNewPwdText.text.toString()
showPasswordLoadingView(true)
lifecycleScope.launch {
@ -466,9 +460,9 @@ class VectorSettingsGeneralFragment @Inject constructor(
activity.toast(R.string.settings_password_updated)
}, { failure ->
if (failure.isInvalidPassword()) {
oldPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
views.changePasswordOldPwdTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
} else {
oldPasswordTil.error = getString(R.string.settings_fail_to_update_password)
views.changePasswordOldPwdTil.error = getString(R.string.settings_fail_to_update_password)
}
})
}

View File

@ -24,6 +24,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
@ -63,71 +64,70 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
super.onViewCreated(view, savedInstanceState)
val layoutManager = LinearLayoutManager(context)
troubleshoot_test_recycler_view.layoutManager = layoutManager
views.troubleshootTestRecyclerView.layoutManager = layoutManager
val dividerItemDecoration = DividerItemDecoration(troubleshoot_test_recycler_view.context,
layoutManager.orientation)
troubleshoot_test_recycler_view.addItemDecoration(dividerItemDecoration)
val dividerItemDecoration = DividerItemDecoration(view.context, layoutManager.orientation)
views.troubleshootTestRecyclerView.addItemDecoration(dividerItemDecoration)
troubleshoot_summ_button.debouncedClicks {
views.troubleshootSummButton.debouncedClicks {
bugReporter.openBugReportScreen(requireActivity())
}
troubleshoot_run_button.debouncedClicks {
views.troubleshootRunButton.debouncedClicks {
testManager?.retry(testStartForActivityResult)
}
startUI()
}
private fun startUI() {
toubleshoot_summ_description.text = getString(R.string.settings_troubleshoot_diagnostic_running_status, 0, 0)
views.toubleshootSummDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status, 0, 0)
testManager = testManagerFactory.create(this)
testManager?.statusListener = { troubleshootTestManager ->
if (isAdded) {
TransitionManager.beginDelayedTransition(troubleshoot_bottom_view)
TransitionManager.beginDelayedTransition(views.troubleshootBottomView)
when (troubleshootTestManager.diagStatus) {
TroubleshootTest.TestStatus.NOT_STARTED -> {
toubleshoot_summ_description.text = ""
troubleshoot_summ_button.visibility = View.GONE
troubleshoot_run_button.visibility = View.VISIBLE
views.toubleshootSummDescription.text = ""
views.troubleshootSummButton.visibility = View.GONE
views.troubleshootRunButton.visibility = View.VISIBLE
}
TroubleshootTest.TestStatus.RUNNING,
TroubleshootTest.TestStatus.WAITING_FOR_USER -> {
val size = troubleshootTestManager.testListSize
val currentTestIndex = troubleshootTestManager.currentTestIndex
toubleshoot_summ_description.text = getString(
views.toubleshootSummDescription.text = getString(
R.string.settings_troubleshoot_diagnostic_running_status,
currentTestIndex,
size
)
troubleshoot_summ_button.visibility = View.GONE
troubleshoot_run_button.visibility = View.GONE
views.troubleshootSummButton.visibility = View.GONE
views.troubleshootRunButton.visibility = View.GONE
}
TroubleshootTest.TestStatus.FAILED -> {
// check if there are quick fixes
val hasQuickFix = testManager?.hasQuickFix().orFalse()
if (hasQuickFix) {
toubleshoot_summ_description.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_with_quickfix)
views.toubleshootSummDescription.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_with_quickfix)
} else {
toubleshoot_summ_description.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_no_quickfix)
views.toubleshootSummDescription.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_no_quickfix)
}
troubleshoot_summ_button.visibility = View.VISIBLE
troubleshoot_run_button.visibility = View.VISIBLE
views.troubleshootSummButton.visibility = View.VISIBLE
views.troubleshootRunButton.visibility = View.VISIBLE
}
TroubleshootTest.TestStatus.SUCCESS -> {
toubleshoot_summ_description.text = getString(R.string.settings_troubleshoot_diagnostic_success_status)
troubleshoot_summ_button.visibility = View.VISIBLE
troubleshoot_run_button.visibility = View.VISIBLE
views.toubleshootSummDescription.text = getString(R.string.settings_troubleshoot_diagnostic_success_status)
views.troubleshootSummButton.visibility = View.VISIBLE
views.troubleshootRunButton.visibility = View.VISIBLE
}
}
}
}
troubleshoot_test_recycler_view.adapter = testManager?.adapter
views.troubleshootTestRecyclerView.adapter = testManager?.adapter
testManager?.runDiagnostic(testStartForActivityResult)
}
override fun onDestroyView() {
troubleshoot_test_recycler_view.cleanup()
views.troubleshootTestRecyclerView.cleanup()
super.onDestroyView()
}

View File

@ -50,6 +50,7 @@ import im.vector.app.core.preference.VectorPreferenceCategory
import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.openFileSelection
import im.vector.app.core.utils.toast
import im.vector.app.databinding.DialogImportE2eKeysBinding
import im.vector.app.features.crypto.keys.KeysExporter
import im.vector.app.features.crypto.keys.KeysImporter
import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity
@ -447,42 +448,37 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
val filename = getFilenameFromUri(appContext, uri)
val dialogLayout = thisActivity.layoutInflater.inflate(R.layout.dialog_import_e2e_keys, null)
val textView = dialogLayout.findViewById<TextView>(R.id.dialog_e2e_keys_passphrase_filename)
val views = DialogImportE2eKeysBinding.bind(dialogLayout)
if (filename.isNullOrBlank()) {
textView.isVisible = false
views.dialogE2eKeysPassphraseFilename.isVisible = false
} else {
textView.isVisible = true
textView.text = getString(R.string.import_e2e_keys_from_file, filename)
views.dialogE2eKeysPassphraseFilename.isVisible = true
views.dialogE2eKeysPassphraseFilename.text = getString(R.string.import_e2e_keys_from_file, filename)
}
val builder = AlertDialog.Builder(thisActivity)
.setTitle(R.string.encryption_import_room_keys)
.setView(dialogLayout)
val passPhraseEditText = dialogLayout.findViewById<TextInputEditText>(R.id.dialog_e2e_keys_passphrase_edit_text)
val importButton = dialogLayout.findViewById<Button>(R.id.dialog_e2e_keys_import_button)
val showPassword = dialogLayout.findViewById<ImageView>(R.id.importDialogShowPassword)
var passwordVisible = false
showPassword.setOnClickListener {
views.importDialogShowPassword.setOnClickListener {
passwordVisible = !passwordVisible
passPhraseEditText.showPassword(passwordVisible)
showPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.dialogE2eKeysPassphraseEditText.showPassword(passwordVisible)
views.importDialogShowPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
passPhraseEditText.addTextChangedListener(object : SimpleTextWatcher() {
views.dialogE2eKeysPassphraseEditText.addTextChangedListener(object : SimpleTextWatcher() {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
importButton.isEnabled = !passPhraseEditText.text.isNullOrEmpty()
views.dialogE2eKeysImportButton.isEnabled = !views.dialogE2eKeysPassphraseEditText.text.isNullOrEmpty()
}
})
val importDialog = builder.show()
importButton.setOnClickListener {
val password = passPhraseEditText.text.toString()
views.dialogE2eKeysImportButton.setOnClickListener {
val password = views.dialogE2eKeysPassphraseEditText.text.toString()
displayLoadingView()

View File

@ -21,6 +21,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.textChanges
@ -73,23 +74,23 @@ class DeactivateAccountFragment @Inject constructor(
}
private fun setupUi() {
deactivateAccountPassword.textChanges()
views.deactivateAccountPassword.textChanges()
.subscribe {
deactivateAccountPasswordTil.error = null
deactivateAccountSubmit.isEnabled = it.isNotEmpty()
views.deactivateAccountPasswordTil.error = null
views.deactivateAccountSubmit.isEnabled = it.isNotEmpty()
}
.disposeOnDestroyView()
}
private fun setupViewListeners() {
deactivateAccountPasswordReveal.setOnClickListener {
views.deactivateAccountPasswordReveal.setOnClickListener {
viewModel.handle(DeactivateAccountAction.TogglePassword)
}
deactivateAccountSubmit.debouncedClicks {
views.deactivateAccountSubmit.debouncedClicks {
viewModel.handle(DeactivateAccountAction.DeactivateAccount(
deactivateAccountPassword.text.toString(),
deactivateAccountEraseCheckbox.isChecked))
views.deactivateAccountPassword.text.toString(),
views.deactivateAccountEraseCheckbox.isChecked))
}
}
@ -102,11 +103,11 @@ class DeactivateAccountFragment @Inject constructor(
}
DeactivateAccountViewEvents.EmptyPassword -> {
settingsActivity?.ignoreInvalidTokenError = false
deactivateAccountPasswordTil.error = getString(R.string.error_empty_field_your_password)
views.deactivateAccountPasswordTil.error = getString(R.string.error_empty_field_your_password)
}
DeactivateAccountViewEvents.InvalidPassword -> {
settingsActivity?.ignoreInvalidTokenError = false
deactivateAccountPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
views.deactivateAccountPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
}
is DeactivateAccountViewEvents.OtherFailure -> {
settingsActivity?.ignoreInvalidTokenError = false
@ -119,7 +120,7 @@ class DeactivateAccountFragment @Inject constructor(
}
override fun invalidate() = withState(viewModel) { state ->
deactivateAccountPassword.showPassword(state.passwordShown)
deactivateAccountPasswordReveal.setImageResource(if (state.passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.deactivateAccountPassword.showPassword(state.passwordShown)
views.deactivateAccountPasswordReveal.setImageResource(if (state.passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
}

View File

@ -18,6 +18,10 @@ package im.vector.app.features.settings.troubleshoot
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import im.vector.app.R
@ -43,68 +47,73 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
override fun getItemCount(): Int = tests.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val troubleshootProgressBar = itemView.findViewById<ProgressBar>(R.id.troubleshootProgressBar)
private val troubleshootTestTitle = itemView.findViewById<TextView>(R.id.troubleshootTestTitle)
private val troubleshootTestDescription = itemView.findViewById<TextView>(R.id.troubleshootTestDescription)
private val troubleshootStatusIcon = itemView.findViewById<ImageView>(R.id.troubleshootStatusIcon)
private val troubleshootTestButton = itemView.findViewById<Button>(R.id.troubleshootTestButton)
fun bind(test: TroubleshootTest) {
val context = itemView.context
itemView.troubleshootTestTitle.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_primary))
itemView.troubleshootTestDescription.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))
troubleshootTestTitle.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_primary))
troubleshootTestDescription.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))
when (test.status) {
TroubleshootTest.TestStatus.NOT_STARTED -> {
itemView.troubleshootTestTitle.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))
troubleshootTestTitle.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))
itemView.troubleshootProgressBar.visibility = View.INVISIBLE
itemView.troubleshootStatusIcon.visibility = View.VISIBLE
itemView.troubleshootStatusIcon.setImageResource(R.drawable.unit_test)
troubleshootProgressBar.visibility = View.INVISIBLE
troubleshootStatusIcon.visibility = View.VISIBLE
troubleshootStatusIcon.setImageResource(R.drawable.unit_test)
}
TroubleshootTest.TestStatus.WAITING_FOR_USER -> {
itemView.troubleshootProgressBar.visibility = View.INVISIBLE
itemView.troubleshootStatusIcon.visibility = View.VISIBLE
troubleshootProgressBar.visibility = View.INVISIBLE
troubleshootStatusIcon.visibility = View.VISIBLE
val infoColor = ContextCompat.getColor(context, R.color.vector_info_color)
val drawable = ContextCompat.getDrawable(itemView.context, R.drawable.ic_notification_privacy_warning)?.apply {
ThemeUtils.tintDrawableWithColor(this, infoColor)
}
itemView.troubleshootStatusIcon.setImageDrawable(drawable)
itemView.troubleshootTestDescription.setTextColor(infoColor)
troubleshootStatusIcon.setImageDrawable(drawable)
troubleshootTestDescription.setTextColor(infoColor)
}
TroubleshootTest.TestStatus.RUNNING -> {
itemView.troubleshootProgressBar.visibility = View.VISIBLE
itemView.troubleshootStatusIcon.visibility = View.INVISIBLE
troubleshootProgressBar.visibility = View.VISIBLE
troubleshootStatusIcon.visibility = View.INVISIBLE
}
TroubleshootTest.TestStatus.FAILED -> {
itemView.troubleshootProgressBar.visibility = View.INVISIBLE
itemView.troubleshootStatusIcon.visibility = View.VISIBLE
itemView.troubleshootStatusIcon.setImageResource(R.drawable.unit_test_ko)
troubleshootProgressBar.visibility = View.INVISIBLE
troubleshootStatusIcon.visibility = View.VISIBLE
troubleshootStatusIcon.setImageResource(R.drawable.unit_test_ko)
itemView.troubleshootStatusIcon.imageTintList = null
troubleshootStatusIcon.imageTintList = null
itemView.troubleshootTestDescription.setTextColor(ContextCompat.getColor(context, R.color.riotx_notice))
troubleshootTestDescription.setTextColor(ContextCompat.getColor(context, R.color.riotx_notice))
}
TroubleshootTest.TestStatus.SUCCESS -> {
itemView.troubleshootProgressBar.visibility = View.INVISIBLE
itemView.troubleshootStatusIcon.visibility = View.VISIBLE
itemView.troubleshootStatusIcon.setImageResource(R.drawable.unit_test_ok)
troubleshootProgressBar.visibility = View.INVISIBLE
troubleshootStatusIcon.visibility = View.VISIBLE
troubleshootStatusIcon.setImageResource(R.drawable.unit_test_ok)
}
}
val quickFix = test.quickFix
if (quickFix != null) {
itemView.troubleshootTestButton.setText(test.quickFix!!.title)
itemView.troubleshootTestButton.setOnClickListener { _ ->
troubleshootTestButton.setText(test.quickFix!!.title)
troubleshootTestButton.setOnClickListener { _ ->
test.quickFix!!.doFix()
}
itemView.troubleshootTestButton.visibility = View.VISIBLE
troubleshootTestButton.visibility = View.VISIBLE
} else {
itemView.troubleshootTestButton.visibility = View.GONE
troubleshootTestButton.visibility = View.GONE
}
itemView.troubleshootTestTitle.setText(test.titleResId)
troubleshootTestTitle.setText(test.titleResId)
val description = test.description
if (description == null) {
itemView.troubleshootTestDescription.visibility = View.GONE
troubleshootTestDescription.visibility = View.GONE
} else {
itemView.troubleshootTestDescription.visibility = View.VISIBLE
itemView.troubleshootTestDescription.text = description
troubleshootTestDescription.visibility = View.VISIBLE
troubleshootTestDescription.text = description
}
}
}

View File

@ -19,6 +19,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
@ -58,10 +59,10 @@ class ReviewTermsFragment @Inject constructor(
}
termsController.listener = this
reviewTermsRecyclerView.configureWith(termsController)
views.reviewTermsRecyclerView.configureWith(termsController)
reviewTermsAccept.onClick { reviewTermsViewModel.handle(ReviewTermsAction.Accept) }
reviewTermsDecline.onClick { activity?.finish() }
views.reviewTermsAccept.onClick { reviewTermsViewModel.handle(ReviewTermsAction.Accept) }
views.reviewTermsDecline.onClick { activity?.finish() }
reviewTermsViewModel.observeViewEvents {
when (it) {
@ -79,7 +80,7 @@ class ReviewTermsFragment @Inject constructor(
}
override fun onDestroyView() {
reviewTermsRecyclerView.cleanup()
views.reviewTermsRecyclerView.cleanup()
termsController.listener = null
super.onDestroyView()
}
@ -94,11 +95,11 @@ class ReviewTermsFragment @Inject constructor(
when (state.termsList) {
is Loading -> {
reviewTermsBottomBar.isVisible = false
views.reviewTermsBottomBar.isVisible = false
}
is Success -> {
reviewTermsBottomBar.isVisible = true
reviewTermsAccept.isEnabled = state.termsList.invoke().all { it.accepted }
views.reviewTermsBottomBar.isVisible = true
views.reviewTermsAccept.isEnabled = state.termsList.invoke().all { it.accepted }
}
else -> Unit
}

View File

@ -57,11 +57,11 @@ class ScanUserCodeFragment @Inject constructor()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
userCodeMyCodeButton.debouncedClicks {
views.userCodeMyCodeButton.debouncedClicks {
sharedViewModel.handle(UserCodeActions.SwitchMode(UserCodeState.Mode.SHOW))
}
userCodeOpenGalleryButton.debouncedClicks {
views.userCodeOpenGalleryButton.debouncedClicks {
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
}
}
@ -94,11 +94,11 @@ class ScanUserCodeFragment @Inject constructor()
}
private fun startCamera() {
userCodeScannerView.startCamera()
userCodeScannerView.setAutoFocus(autoFocus)
userCodeScannerView.debouncedClicks {
views.userCodeScannerView.startCamera()
views.userCodeScannerView.setAutoFocus(autoFocus)
views.userCodeScannerView.debouncedClicks {
this.autoFocus = !autoFocus
userCodeScannerView.setAutoFocus(autoFocus)
views.userCodeScannerView.setAutoFocus(autoFocus)
}
}
@ -112,7 +112,7 @@ class ScanUserCodeFragment @Inject constructor()
override fun onResume() {
super.onResume()
// Register ourselves as a handler for scan results.
userCodeScannerView.setResultHandler(this)
views.userCodeScannerView.setResultHandler(this)
if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)) {
startCamera()
}
@ -120,9 +120,9 @@ class ScanUserCodeFragment @Inject constructor()
override fun onPause() {
super.onPause()
userCodeScannerView.setResultHandler(null)
views.userCodeScannerView.setResultHandler(null)
// Stop camera on pause
userCodeScannerView.stopCamera()
views.userCodeScannerView.stopCamera()
}
override fun handleResult(result: Result?) {

View File

@ -78,9 +78,9 @@ class WidgetFragment @Inject constructor() :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
widgetWebView.setupForWidget(this)
views.widgetWebView.setupForWidget(this)
if (fragmentArgs.kind.isAdmin()) {
viewModel.getPostAPIMediator().setWebView(widgetWebView)
viewModel.getPostAPIMediator().setWebView(views.widgetWebView)
}
viewModel.observeViewEvents {
Timber.v("Observed view events: $it")
@ -114,12 +114,12 @@ class WidgetFragment @Inject constructor() :
if (fragmentArgs.kind.isAdmin()) {
viewModel.getPostAPIMediator().clearWebView()
}
widgetWebView.clearAfterWidget()
views.widgetWebView.clearAfterWidget()
}
override fun onResume() {
super.onResume()
widgetWebView?.let {
views.widgetWebView?.let {
it.resumeTimers()
it.onResume()
}
@ -127,7 +127,7 @@ class WidgetFragment @Inject constructor() :
override fun onPause() {
super.onPause()
widgetWebView?.let {
views.widgetWebView?.let {
it.pauseTimers()
it.onPause()
}
@ -166,7 +166,7 @@ class WidgetFragment @Inject constructor() :
return@withState true
}
R.id.action_refresh -> if (state.formattedURL.complete) {
widgetWebView.reload()
views.widgetWebView.reload()
return@withState true
}
R.id.action_widget_open_ext -> if (state.formattedURL.complete) {
@ -183,8 +183,8 @@ class WidgetFragment @Inject constructor() :
override fun onBackPressed(toolbarButton: Boolean): Boolean = withState(viewModel) { state ->
if (state.formattedURL.complete) {
if (widgetWebView.canGoBack()) {
widgetWebView.goBack()
if (views.widgetWebView.canGoBack()) {
views.widgetWebView.goBack()
return@withState true
}
}
@ -196,37 +196,37 @@ class WidgetFragment @Inject constructor() :
when (state.formattedURL) {
is Incomplete -> {
setStateError(null)
widgetWebView.isInvisible = true
widgetProgressBar.isIndeterminate = true
widgetProgressBar.isVisible = true
views.widgetWebView.isInvisible = true
views.widgetProgressBar.isIndeterminate = true
views.widgetProgressBar.isVisible = true
}
is Success -> {
setStateError(null)
when (state.webviewLoadedUrl) {
Uninitialized -> {
widgetWebView.isInvisible = true
views.widgetWebView.isInvisible = true
}
is Loading -> {
setStateError(null)
widgetWebView.isInvisible = false
widgetProgressBar.isIndeterminate = true
widgetProgressBar.isVisible = true
views.widgetWebView.isInvisible = false
views.widgetProgressBar.isIndeterminate = true
views.widgetProgressBar.isVisible = true
}
is Success -> {
widgetWebView.isInvisible = false
widgetProgressBar.isVisible = false
views.widgetWebView.isInvisible = false
views.widgetProgressBar.isVisible = false
setStateError(null)
}
is Fail -> {
widgetProgressBar.isInvisible = true
views.widgetProgressBar.isInvisible = true
setStateError(state.webviewLoadedUrl.error.message)
}
}
}
is Fail -> {
// we need to show Error
widgetWebView.isInvisible = true
widgetProgressBar.isVisible = false
views.widgetWebView.isInvisible = true
views.widgetProgressBar.isVisible = false
setStateError(state.formattedURL.error.message)
}
}
@ -282,19 +282,19 @@ class WidgetFragment @Inject constructor() :
}
private fun loadFormattedUrl(event: WidgetViewEvents.OnURLFormatted) {
widgetWebView.clearHistory()
widgetWebView.loadUrl(event.formattedURL)
views.widgetWebView.clearHistory()
views.widgetWebView.loadUrl(event.formattedURL)
}
private fun setStateError(message: String?) {
if (message == null) {
widgetErrorLayout.isVisible = false
widgetErrorText.text = null
views.widgetErrorLayout.isVisible = false
views.widgetErrorText.text = null
} else {
widgetProgressBar.isVisible = false
widgetErrorLayout.isVisible = true
widgetWebView.isInvisible = true
widgetErrorText.text = getString(R.string.room_widget_failed_to_load, message)
views.widgetProgressBar.isVisible = false
views.widgetErrorLayout.isVisible = true
views.widgetWebView.isInvisible = true
views.widgetErrorText.text = getString(R.string.room_widget_failed_to_load, message)
}
}

View File

@ -20,7 +20,9 @@ import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide
@ -31,6 +33,9 @@ class SignOutBottomSheetActionButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private val actionIconImageView: ImageView
private val actionTitleText: TextView
var action: (() -> Unit)? = null
var title: String? = null
@ -72,9 +77,12 @@ class SignOutBottomSheetActionButton @JvmOverloads constructor(
tint = typedArray.getColor(R.styleable.SignOutBottomSheetActionButton_iconTint, ThemeUtils.getColor(context, android.R.attr.textColor))
textColor = typedArray.getColor(R.styleable.SignOutBottomSheetActionButton_textColor, ThemeUtils.getColor(context, android.R.attr.textColor))
actionIconImageView = findViewById(R.id.actionIconImageView)
actionTitleText = findViewById(R.id.actionTitleText)
typedArray.recycle()
signedOutActionClickable.setOnClickListener {
setOnClickListener {
action?.invoke()
}
}

View File

@ -4,7 +4,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/fragment_room_setting_generic" />
<include
android:id="@+id/room_setting_generic"
layout="@layout/fragment_room_setting_generic" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/inviteUsersButton"