Browse Source

make ControllerViewBindingDelegate nullable

check nullable bindings in all controllers

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 2 years ago
parent
commit
4124a65c7a
17 changed files with 896 additions and 865 deletions
  1. 27 23
      app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt
  2. 149 174
      app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
  3. 34 32
      app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt
  4. 129 113
      app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
  5. 169 150
      app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
  6. 4 4
      app/src/main/java/com/nextcloud/talk/controllers/GeocodingController.kt
  7. 28 28
      app/src/main/java/com/nextcloud/talk/controllers/LocationPickerController.kt
  8. 2 2
      app/src/main/java/com/nextcloud/talk/controllers/LockedController.kt
  9. 40 38
      app/src/main/java/com/nextcloud/talk/controllers/ProfileController.kt
  10. 5 5
      app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt
  11. 38 37
      app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt
  12. 151 141
      app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt
  13. 6 6
      app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt
  14. 21 21
      app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt
  15. 71 69
      app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.kt
  16. 17 17
      app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt
  17. 5 5
      app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt

+ 27 - 23
app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt

@@ -80,7 +80,8 @@ class AccountVerificationController(args: Bundle? = null) :
         R.layout.controller_account_verification,
         args
     ) {
-    private val binding: ControllerAccountVerificationBinding by viewBinding(ControllerAccountVerificationBinding::bind)
+    private val binding: ControllerAccountVerificationBinding? by
+    viewBinding(ControllerAccountVerificationBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -212,7 +213,7 @@ class AccountVerificationController(args: Bundle? = null) :
                     } else {
                         if (activity != null && resources != null) {
                             activity!!.runOnUiThread {
-                                binding.progressText.setText(
+                                binding?.progressText?.setText(
                                     String.format(
                                         resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed),
                                         resources!!.getString(R.string.nc_app_product_name)
@@ -230,17 +231,14 @@ class AccountVerificationController(args: Bundle? = null) :
                 override fun onError(e: Throwable) {
                     if (activity != null && resources != null) {
                         activity!!.runOnUiThread {
-                            binding.progressText.setText(
-                                String.format(
-                                    resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed),
-                                    resources!!.getString(R.string.nc_app_product_name)
-                                )
+                            binding?.progressText?.text = String.format(
+                                resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed),
+                                resources!!.getString(R.string.nc_app_product_name)
                             )
                         }
                     }
-                    ApplicationWideMessageHolder.getInstance().setMessageType(
+                    ApplicationWideMessageHolder.getInstance().messageType =
                         ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK
-                    )
                     abortVerification()
                 }
 
@@ -279,8 +277,8 @@ class AccountVerificationController(args: Bundle? = null) :
                         registerForPush()
                     } else {
                         activity!!.runOnUiThread {
-                            binding.progressText.text =
-                                """ ${binding.progressText.text}
+                            binding?.progressText?.text =
+                                """ ${binding?.progressText?.text}
                                     ${resources!!.getString(R.string.nc_push_disabled)}
                                 """.trimIndent()
                         }
@@ -290,8 +288,8 @@ class AccountVerificationController(args: Bundle? = null) :
 
                 @SuppressLint("SetTextI18n")
                 override fun onError(e: Throwable) {
-                    binding.progressText.text =
-                        """ ${binding.progressText.text}
+                    binding?.progressText?.text =
+                        """ ${binding?.progressText?.text}
                         """
                         .trimIndent() + resources!!.getString(R.string.nc_display_name_not_stored)
                     abortVerification()
@@ -331,9 +329,9 @@ class AccountVerificationController(args: Bundle? = null) :
                     } else {
                         if (activity != null) {
                             activity!!.runOnUiThread {
-                                binding.progressText.text =
+                                binding?.progressText?.text =
                                     """
-                                        ${binding.progressText.text}
+                                        ${binding?.progressText?.text}
                                         ${resources!!.getString(R.string.nc_display_name_not_fetched)}
                                     """.trimIndent()
                             }
@@ -346,9 +344,9 @@ class AccountVerificationController(args: Bundle? = null) :
                 override fun onError(e: Throwable) {
                     if (activity != null) {
                         activity!!.runOnUiThread {
-                            binding.progressText.text =
+                            binding?.progressText?.text =
                                 """
-                                    ${binding.progressText.text}
+                                    ${binding?.progressText?.text}
                                     ${resources!!.getString(R.string.nc_display_name_not_fetched)}
                                 """.trimIndent()
                         }
@@ -380,11 +378,17 @@ class AccountVerificationController(args: Bundle? = null) :
         if (eventStatus.eventType == EventStatus.EventType.PUSH_REGISTRATION) {
             if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood && activity != null) {
                 activity!!.runOnUiThread {
-                    binding.progressText.text =
+                    // try {
+                    binding?.progressText?.text =
                         """
-                            ${binding.progressText.text}
+                            ${binding?.progressText?.text}
                             ${resources!!.getString(R.string.nc_push_disabled)}
                         """.trimIndent()
+                    // } catch (npe: NullPointerException) {
+                    //     // view binding can be null
+                    //     // since this is called asynchronously and UI might have been destroyed in the meantime
+                    //     Log.i(TAG, "UI destroyed - view binding already gone")
+                    // }
                 }
             }
             fetchAndStoreCapabilities()
@@ -392,9 +396,9 @@ class AccountVerificationController(args: Bundle? = null) :
             if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood) {
                 if (activity != null) {
                     activity!!.runOnUiThread {
-                        binding.progressText.text =
+                        binding?.progressText?.text =
                             """
-                                ${binding.progressText.text}
+                                ${binding?.progressText?.text}
                                 ${resources!!.getString(R.string.nc_capabilities_failed)}
                             """.trimIndent()
                     }
@@ -407,9 +411,9 @@ class AccountVerificationController(args: Bundle? = null) :
             if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood) {
                 if (activity != null) {
                     activity!!.runOnUiThread {
-                        binding.progressText.text =
+                        binding?.progressText?.text =
                             """
-                                ${binding.progressText.text}
+                                ${binding?.progressText?.text}
                                 ${resources!!.getString(R.string.nc_external_server_failed)}
                             """.trimIndent()
                     }

+ 149 - 174
app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt

@@ -213,23 +213,7 @@ import java.util.Locale
 import java.util.Objects
 import java.util.concurrent.ExecutionException
 import javax.inject.Inject
-import kotlin.collections.ArrayList
-import kotlin.collections.HashMap
-import kotlin.collections.LinkedHashMap
-import kotlin.collections.List
-import kotlin.collections.MutableList
-import kotlin.collections.MutableMap
-import kotlin.collections.chunked
-import kotlin.collections.indexOfFirst
-import kotlin.collections.indices
-import kotlin.collections.isNotEmpty
-import kotlin.collections.iterator
-import kotlin.collections.map
 import kotlin.collections.set
-import kotlin.collections.toList
-import kotlin.collections.toMap
-import kotlin.collections.toMutableMap
-import kotlin.collections.toTypedArray
 import kotlin.math.roundToInt
 
 @AutoInjector(NextcloudTalkApplication::class)
@@ -246,7 +230,7 @@ class ChatController(args: Bundle) :
     CommonMessageInterface,
     PreviewMessageInterface {
 
-    private val binding: ControllerChatBinding by viewBinding(ControllerChatBinding::bind)
+    private val binding: ControllerChatBinding? by viewBinding(ControllerChatBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -437,7 +421,7 @@ class ChatController(args: Bundle) :
 
             withNullableControllerViewBinding {
                 val itemTouchHelper = ItemTouchHelper(messageSwipeController)
-                itemTouchHelper.attachToRecyclerView(binding.messagesListView)
+                itemTouchHelper.attachToRecyclerView(binding?.messagesListView)
             }
         }
     }
@@ -541,7 +525,7 @@ class ChatController(args: Bundle) :
         var adapterWasNull = false
 
         if (adapter == null) {
-            binding.progressBar.visibility = View.VISIBLE
+            binding?.progressBar?.visibility = View.VISIBLE
 
             adapterWasNull = true
 
@@ -651,10 +635,10 @@ class ChatController(args: Bundle) :
                 this
             )
         } else {
-            binding.messagesListView.visibility = View.VISIBLE
+            binding?.messagesListView?.visibility = View.VISIBLE
         }
 
-        binding.messagesListView.setAdapter(adapter)
+        binding?.messagesListView?.setAdapter(adapter)
         adapter?.setLoadMoreListener(this)
         adapter?.setDateHeadersFormatter { format(it) }
         adapter?.setOnMessageViewLongClickListener { view, message -> onMessageViewLongClick(view, message) }
@@ -677,11 +661,11 @@ class ChatController(args: Bundle) :
 
         setupSwipeToReply()
 
-        layoutManager = binding.messagesListView.layoutManager as LinearLayoutManager?
+        layoutManager = binding?.messagesListView?.layoutManager as LinearLayoutManager?
 
-        binding.popupBubbleView.setRecyclerView(binding.messagesListView)
+        binding?.popupBubbleView?.setRecyclerView(binding?.messagesListView)
 
-        binding.popupBubbleView.setPopupBubbleListener { context ->
+        binding?.popupBubbleView?.setPopupBubbleListener { context ->
             if (newMessagesCount != 0) {
                 val scrollPosition = if (newMessagesCount - 1 < 0) {
                     0
@@ -690,18 +674,18 @@ class ChatController(args: Bundle) :
                 }
                 Handler().postDelayed(
                     {
-                        binding.messagesListView.smoothScrollToPosition(scrollPosition)
+                        binding?.messagesListView?.smoothScrollToPosition(scrollPosition)
                     },
                     NEW_MESSAGES_POPUP_BUBBLE_DELAY
                 )
             }
         }
 
-        viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.popupBubbleView)
+        binding?.let { viewThemeUtils.material.colorMaterialButtonPrimaryFilled(it.popupBubbleView) }
 
-        binding.messageInputView.setPadding(0, 0, 0, 0)
+        binding?.messageInputView?.setPadding(0, 0, 0, 0)
 
-        binding.messagesListView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+        binding?.messagesListView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
             override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                 super.onScrollStateChanged(recyclerView, newState)
 
@@ -710,8 +694,8 @@ class ChatController(args: Bundle) :
                         if (layoutManager!!.findFirstCompletelyVisibleItemPosition() < newMessagesCount) {
                             newMessagesCount = 0
 
-                            if (binding.popupBubbleView.isShown) {
-                                binding.popupBubbleView.hide()
+                            if (binding?.popupBubbleView?.isShown == true) {
+                                binding?.popupBubbleView?.hide()
                             }
                         }
                     }
@@ -723,9 +707,9 @@ class ChatController(args: Bundle) :
         val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser)
 
         filters[0] = InputFilter.LengthFilter(lengthFilter)
-        binding.messageInputView.inputEditText?.filters = filters
+        binding?.messageInputView?.inputEditText?.filters = filters
 
-        binding.messageInputView.inputEditText?.addTextChangedListener(object : TextWatcher {
+        binding?.messageInputView?.inputEditText?.addTextChangedListener(object : TextWatcher {
             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                 // unused atm
             }
@@ -734,19 +718,19 @@ class ChatController(args: Bundle) :
             override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                 try {
                     if (s.length >= lengthFilter) {
-                        binding.messageInputView.inputEditText?.error = String.format(
+                        binding?.messageInputView?.inputEditText?.error = String.format(
                             Objects.requireNonNull<Resources>(resources).getString(R.string.nc_limit_hit),
                             lengthFilter.toString()
                         )
                     } else {
-                        binding.messageInputView.inputEditText?.error = null
+                        binding?.messageInputView?.inputEditText?.error = null
                     }
 
-                    val editable = binding.messageInputView.inputEditText?.editableText
-                    if (editable != null && binding.messageInputView.inputEditText != null) {
+                    val editable = binding?.messageInputView?.inputEditText?.editableText
+                    if (editable != null && binding?.messageInputView?.inputEditText != null) {
                         val mentionSpans = editable.getSpans(
                             0,
-                            binding.messageInputView.inputEditText!!.length(),
+                            binding?.messageInputView?.inputEditText!!.length(),
                             Spans.MentionChipSpan::class.java
                         )
                         var mentionSpan: Spans.MentionChipSpan
@@ -779,15 +763,15 @@ class ChatController(args: Bundle) :
 
         // Image keyboard support
         // See: https://developer.android.com/guide/topics/text/image-keyboard
-        (binding.messageInputView.inputEditText as ImageEmojiEditText).onCommitContentListener = {
+        (binding?.messageInputView?.inputEditText as ImageEmojiEditText).onCommitContentListener = {
             uploadFile(it.toString(), false)
         }
 
         showMicrophoneButton(true)
 
-        binding.messageInputView.messageInput.doAfterTextChanged {
+        binding?.messageInputView?.messageInput?.doAfterTextChanged {
             try {
-                if (binding.messageInputView.messageInput.text.isEmpty()) {
+                if (binding?.messageInputView?.messageInput?.text?.isEmpty() == true) {
                     showMicrophoneButton(true)
                 } else {
                     showMicrophoneButton(false)
@@ -806,7 +790,7 @@ class ChatController(args: Bundle) :
         var voiceRecordStartTime = 0L
         var voiceRecordEndTime = 0L
 
-        binding.messageInputView.recordAudioButton.setOnTouchListener(object : View.OnTouchListener {
+        binding?.messageInputView?.recordAudioButton?.setOnTouchListener(object : View.OnTouchListener {
             override fun onTouch(v: View?, event: MotionEvent?): Boolean {
                 view.performClick()
                 when (event?.action) {
@@ -835,7 +819,7 @@ class ChatController(args: Bundle) :
 
                         stopAndDiscardAudioRecording()
                         showRecordAudioUi(false)
-                        binding.messageInputView.slideToCancelDescription.x = sliderInitX
+                        binding?.messageInputView?.slideToCancelDescription?.x = sliderInitX
                     }
                     MotionEvent.ACTION_UP -> {
                         Log.d(TAG, "ACTION_UP. stop recording??")
@@ -861,7 +845,7 @@ class ChatController(args: Bundle) :
                             stopAndSendAudioRecording()
                         }
 
-                        binding.messageInputView.slideToCancelDescription.x = sliderInitX
+                        binding?.messageInputView?.slideToCancelDescription?.x = sliderInitX
                     }
                     MotionEvent.ACTION_MOVE -> {
                         Log.d(TAG, "ACTION_MOVE.")
@@ -873,26 +857,26 @@ class ChatController(args: Bundle) :
                         showRecordAudioUi(true)
 
                         if (sliderInitX == 0.0F) {
-                            sliderInitX = binding.messageInputView.slideToCancelDescription.x
+                            sliderInitX = binding?.messageInputView?.slideToCancelDescription?.x!!
                         }
 
                         val movedX: Float = event.x
                         deltaX = movedX - downX
 
                         // only allow slide to left
-                        if (binding.messageInputView.slideToCancelDescription.x > sliderInitX) {
-                            binding.messageInputView.slideToCancelDescription.x = sliderInitX
+                        if (binding?.messageInputView?.slideToCancelDescription?.x!! > sliderInitX) {
+                            binding?.messageInputView?.slideToCancelDescription?.x = sliderInitX
                         }
 
-                        if (binding.messageInputView.slideToCancelDescription.x < VOICE_RECORD_CANCEL_SLIDER_X) {
+                        if (binding?.messageInputView?.slideToCancelDescription?.x!! < VOICE_RECORD_CANCEL_SLIDER_X) {
                             Log.d(TAG, "stopping recording because slider was moved to left")
                             stopAndDiscardAudioRecording()
                             showRecordAudioUi(false)
-                            binding.messageInputView.slideToCancelDescription.x = sliderInitX
+                            binding?.messageInputView?.slideToCancelDescription?.x = sliderInitX
                             return true
                         } else {
-                            binding.messageInputView.slideToCancelDescription.x = binding.messageInputView
-                                .slideToCancelDescription.x + deltaX
+                            binding?.messageInputView?.slideToCancelDescription?.x =
+                                binding?.messageInputView?.slideToCancelDescription?.x!! + deltaX
                             downX = movedX
                         }
                     }
@@ -902,26 +886,24 @@ class ChatController(args: Bundle) :
             }
         })
 
-        binding.messageInputView.inputEditText?.setText(sharedText)
-        binding.messageInputView.setAttachmentsListener {
+        binding?.messageInputView?.inputEditText?.setText(sharedText)
+        binding?.messageInputView?.setAttachmentsListener {
             activity?.let { AttachmentDialog(it, this).show() }
         }
 
-        binding.messageInputView.button.setOnClickListener { submitMessage(false) }
+        binding?.messageInputView?.button?.setOnClickListener { submitMessage(false) }
 
         if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "silent-send")) {
-            binding.messageInputView.button.setOnLongClickListener {
+            binding?.messageInputView?.button?.setOnLongClickListener {
                 showSendButtonMenu()
                 true
             }
         }
 
-        binding.messageInputView.button.contentDescription = resources?.getString(
-            R.string
-                .nc_description_send_message_button
-        )
+        binding?.messageInputView?.button?.contentDescription =
+            resources?.getString(R.string.nc_description_send_message_button)
 
-        viewThemeUtils.platform.colorImageView(binding.messageInputView.button)
+        binding?.messageInputView?.button?.let { viewThemeUtils.platform.colorImageView(it) }
 
         if (currentConversation != null && currentConversation?.roomId != null) {
             loadAvatarForStatusBar()
@@ -942,7 +924,7 @@ class ChatController(args: Bundle) :
     private fun showSendButtonMenu() {
         val popupMenu = PopupMenu(
             ContextThemeWrapper(view?.context, R.style.ChatSendButtonMenu),
-            binding.messageInputView.button,
+            binding?.messageInputView?.button,
             Gravity.END
         )
         popupMenu.inflate(R.menu.chat_send_menu)
@@ -1150,23 +1132,23 @@ class ChatController(args: Bundle) :
 
     private fun showRecordAudioUi(show: Boolean) {
         if (show) {
-            binding.messageInputView.microphoneEnabledInfo.visibility = View.VISIBLE
-            binding.messageInputView.microphoneEnabledInfoBackground.visibility = View.VISIBLE
-            binding.messageInputView.audioRecordDuration.visibility = View.VISIBLE
-            binding.messageInputView.slideToCancelDescription.visibility = View.VISIBLE
-            binding.messageInputView.attachmentButton.visibility = View.GONE
-            binding.messageInputView.smileyButton.visibility = View.GONE
-            binding.messageInputView.messageInput.visibility = View.GONE
-            binding.messageInputView.messageInput.hint = ""
+            binding?.messageInputView?.microphoneEnabledInfo?.visibility = View.VISIBLE
+            binding?.messageInputView?.microphoneEnabledInfoBackground?.visibility = View.VISIBLE
+            binding?.messageInputView?.audioRecordDuration?.visibility = View.VISIBLE
+            binding?.messageInputView?.slideToCancelDescription?.visibility = View.VISIBLE
+            binding?.messageInputView?.attachmentButton?.visibility = View.GONE
+            binding?.messageInputView?.smileyButton?.visibility = View.GONE
+            binding?.messageInputView?.messageInput?.visibility = View.GONE
+            binding?.messageInputView?.messageInput?.hint = ""
         } else {
-            binding.messageInputView.microphoneEnabledInfo.visibility = View.GONE
-            binding.messageInputView.microphoneEnabledInfoBackground.visibility = View.GONE
-            binding.messageInputView.audioRecordDuration.visibility = View.GONE
-            binding.messageInputView.slideToCancelDescription.visibility = View.GONE
-            binding.messageInputView.attachmentButton.visibility = View.VISIBLE
-            binding.messageInputView.smileyButton.visibility = View.VISIBLE
-            binding.messageInputView.messageInput.visibility = View.VISIBLE
-            binding.messageInputView.messageInput.hint =
+            binding?.messageInputView?.microphoneEnabledInfo?.visibility = View.GONE
+            binding?.messageInputView?.microphoneEnabledInfoBackground?.visibility = View.GONE
+            binding?.messageInputView?.audioRecordDuration?.visibility = View.GONE
+            binding?.messageInputView?.slideToCancelDescription?.visibility = View.GONE
+            binding?.messageInputView?.attachmentButton?.visibility = View.VISIBLE
+            binding?.messageInputView?.smileyButton?.visibility = View.VISIBLE
+            binding?.messageInputView?.messageInput?.visibility = View.VISIBLE
+            binding?.messageInputView?.messageInput?.hint =
                 context.resources?.getString(R.string.nc_hint_enter_a_message)
         }
     }
@@ -1179,15 +1161,15 @@ class ChatController(args: Bundle) :
     }
 
     private fun startAudioRecording(file: String) {
-        binding.messageInputView.audioRecordDuration.base = SystemClock.elapsedRealtime()
-        binding.messageInputView.audioRecordDuration.start()
+        binding?.messageInputView?.audioRecordDuration?.base = SystemClock.elapsedRealtime()
+        binding?.messageInputView?.audioRecordDuration?.start()
 
         val animation: Animation = AlphaAnimation(1.0f, 0.0f)
         animation.duration = ANIMATION_DURATION
         animation.interpolator = LinearInterpolator()
         animation.repeatCount = Animation.INFINITE
         animation.repeatMode = Animation.REVERSE
-        binding.messageInputView.microphoneEnabledInfo.startAnimation(animation)
+        binding?.messageInputView?.microphoneEnabledInfo?.startAnimation(animation)
 
         recorder = MediaRecorder().apply {
             setAudioSource(MediaRecorder.AudioSource.MIC)
@@ -1227,8 +1209,8 @@ class ChatController(args: Bundle) :
 
     @Suppress("Detekt.TooGenericExceptionCaught")
     private fun stopAudioRecording() {
-        binding.messageInputView.audioRecordDuration.stop()
-        binding.messageInputView.microphoneEnabledInfo.clearAnimation()
+        binding?.messageInputView?.audioRecordDuration?.stop()
+        binding?.messageInputView?.microphoneEnabledInfo?.clearAnimation()
 
         if (isVoiceRecordingInProgress) {
             recorder?.apply {
@@ -1301,9 +1283,9 @@ class ChatController(args: Bundle) :
                 shouldShowLobby() ||
                 !participantPermissions.hasChatPermission()
             ) {
-                binding.messageInputView.visibility = View.GONE
+                binding?.messageInputView?.visibility = View.GONE
             } else {
-                binding.messageInputView.visibility = View.VISIBLE
+                binding?.messageInputView?.visibility = View.VISIBLE
             }
         }
     }
@@ -1359,10 +1341,10 @@ class ChatController(args: Bundle) :
             }
 
             if (shouldShowLobby()) {
-                binding.lobby.lobbyView.visibility = View.VISIBLE
-                binding.messagesListView.visibility = View.GONE
-                binding.messageInputView.visibility = View.GONE
-                binding.progressBar.visibility = View.GONE
+                binding?.lobby?.lobbyView?.visibility = View.VISIBLE
+                binding?.messagesListView?.visibility = View.GONE
+                binding?.messageInputView?.visibility = View.GONE
+                binding?.progressBar?.visibility = View.GONE
 
                 val sb = StringBuilder()
                 sb.append(resources!!.getText(R.string.nc_lobby_waiting))
@@ -1383,11 +1365,11 @@ class ChatController(args: Bundle) :
                 }
 
                 sb.append(currentConversation!!.description)
-                binding.lobby.lobbyTextView.text = sb.toString()
+                binding?.lobby?.lobbyTextView?.text = sb.toString()
             } else {
-                binding.lobby.lobbyView.visibility = View.GONE
-                binding.messagesListView.visibility = View.VISIBLE
-                binding.messageInputView.inputEditText?.visibility = View.VISIBLE
+                binding?.lobby?.lobbyView?.visibility = View.GONE
+                binding?.messagesListView?.visibility = View.VISIBLE
+                binding?.messageInputView?.inputEditText?.visibility = View.VISIBLE
                 if (isFirstMessagesProcessing && pastPreconditionFailed) {
                     pastPreconditionFailed = false
                     pullChatMessages(0)
@@ -1397,9 +1379,9 @@ class ChatController(args: Bundle) :
                 }
             }
         } else {
-            binding.lobby.lobbyView.visibility = View.GONE
-            binding.messagesListView.visibility = View.VISIBLE
-            binding.messageInputView.inputEditText?.visibility = View.VISIBLE
+            binding?.lobby?.lobbyView?.visibility = View.GONE
+            binding?.messagesListView?.visibility = View.VISIBLE
+            binding?.messageInputView?.inputEditText?.visibility = View.VISIBLE
         }
     }
 
@@ -1461,31 +1443,30 @@ class ChatController(args: Bundle) :
                         }
                     }
 
-                    val materialAlertDialogBuilder = MaterialAlertDialogBuilder(binding.messageInputView.context)
-                        .setTitle(confirmationQuestion)
-                        .setMessage(filenamesWithLineBreaks.toString())
-                        .setPositiveButton(R.string.nc_yes) { _, _ ->
-                            if (UploadAndShareFilesWorker.isStoragePermissionGranted(context)) {
-                                uploadFiles(filesToUpload)
-                            } else {
-                                UploadAndShareFilesWorker.requestStoragePermission(this)
+                    binding?.messageInputView?.context?.let {
+                        val materialAlertDialogBuilder = MaterialAlertDialogBuilder(it)
+                            .setTitle(confirmationQuestion)
+                            .setMessage(filenamesWithLineBreaks.toString())
+                            .setPositiveButton(R.string.nc_yes) { _, _ ->
+                                if (UploadAndShareFilesWorker.isStoragePermissionGranted(context)) {
+                                    uploadFiles(filesToUpload)
+                                } else {
+                                    UploadAndShareFilesWorker.requestStoragePermission(this)
+                                }
+                            }
+                            .setNegativeButton(R.string.nc_no) { _, _ ->
+                                // unused atm
                             }
-                        }
-                        .setNegativeButton(R.string.nc_no) { _, _ ->
-                            // unused atm
-                        }
 
-                    viewThemeUtils.dialog.colorMaterialAlertDialogBackground(
-                        binding.messageInputView.context,
-                        materialAlertDialogBuilder
-                    )
+                        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it, materialAlertDialogBuilder)
 
-                    val dialog = materialAlertDialogBuilder.show()
+                        val dialog = materialAlertDialogBuilder.show()
 
-                    viewThemeUtils.platform.colorTextButtons(
-                        dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                        dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-                    )
+                        viewThemeUtils.platform.colorTextButtons(
+                            dialog.getButton(AlertDialog.BUTTON_POSITIVE),
+                            dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+                        )
+                    }
                 } catch (e: IllegalStateException) {
                     Toast.makeText(context, context.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
                         .show()
@@ -1572,7 +1553,7 @@ class ChatController(args: Bundle) :
             it.item is ChatMessage && (it.item as ChatMessage).id == messageId
         }
         if (position != null && position >= 0) {
-            binding.messagesListView.smoothScrollToPosition(position)
+            binding?.messagesListView?.smoothScrollToPosition(position)
         } else {
             // TODO show error that we don't have that message?
         }
@@ -1732,12 +1713,12 @@ class ChatController(args: Bundle) :
                 val callback = MentionAutocompleteCallback(
                     activity,
                     conversationUser!!,
-                    binding.messageInputView.inputEditText,
+                    binding?.messageInputView?.inputEditText,
                     viewThemeUtils
                 )
 
-                if (mentionAutocomplete == null && binding.messageInputView.inputEditText != null) {
-                    mentionAutocomplete = Autocomplete.on<Mention>(binding.messageInputView.inputEditText)
+                if (mentionAutocomplete == null && binding?.messageInputView?.inputEditText != null) {
+                    mentionAutocomplete = Autocomplete.on<Mention>(binding?.messageInputView?.inputEditText)
                         .with(elevation)
                         .with(backgroundDrawable)
                         .with(MagicCharPolicy('@'))
@@ -1773,9 +1754,9 @@ class ChatController(args: Bundle) :
         ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = roomToken
         ApplicationWideCurrentRoomHolder.getInstance().userInRoom = conversationUser
 
-        val smileyButton = binding.messageInputView.findViewById<ImageButton>(R.id.smileyButton)
+        val smileyButton = binding?.messageInputView?.findViewById<ImageButton>(R.id.smileyButton)
 
-        emojiPopup = binding.messageInputView.inputEditText?.let {
+        emojiPopup = binding?.messageInputView?.inputEditText?.let {
             EmojiPopup(
                 rootView = view,
                 editText = it,
@@ -1793,7 +1774,7 @@ class ChatController(args: Bundle) :
                 },
                 onEmojiClickListener = {
                     try {
-                        binding.messageInputView.inputEditText?.editableText?.append(" ")
+                        binding?.messageInputView?.inputEditText?.editableText?.append(" ")
                     } catch (npe: NullPointerException) {
                         // view binding can be null
                         // since this is called asynchronously and UI might have been destroyed in the meantime
@@ -1807,12 +1788,14 @@ class ChatController(args: Bundle) :
             emojiPopup?.toggle()
         }
 
-        binding.messageInputView.findViewById<ImageButton>(R.id.cancelReplyButton)?.setOnClickListener {
+        binding?.messageInputView?.findViewById<ImageButton>(R.id.cancelReplyButton)?.setOnClickListener {
             cancelReply()
         }
 
-        viewThemeUtils.platform
-            .themeImageButton(binding.messageInputView.findViewById<ImageButton>(R.id.cancelReplyButton))
+        binding?.messageInputView?.findViewById<ImageButton>(R.id.cancelReplyButton)?.let {
+            viewThemeUtils.platform
+                .themeImageButton(it)
+        }
 
         cancelNotificationsForCurrentConversation()
 
@@ -1824,8 +1807,8 @@ class ChatController(args: Bundle) :
     }
 
     private fun cancelReply() {
-        binding.messageInputView.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.visibility = View.GONE
-        binding.messageInputView.findViewById<ImageButton>(R.id.attachmentButton)?.visibility = View.VISIBLE
+        binding?.messageInputView?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.visibility = View.GONE
+        binding?.messageInputView?.findViewById<ImageButton>(R.id.attachmentButton)?.visibility = View.VISIBLE
     }
 
     @Suppress("Detekt.TooGenericExceptionCaught")
@@ -2087,8 +2070,8 @@ class ChatController(args: Bundle) :
     }
 
     private fun submitMessage(sendWithoutNotification: Boolean) {
-        if (binding.messageInputView.inputEditText != null) {
-            val editable = binding.messageInputView.inputEditText!!.editableText
+        if (binding?.messageInputView?.inputEditText != null) {
+            val editable = binding?.messageInputView?.inputEditText!!.editableText
             val mentionSpans = editable.getSpans(
                 0,
                 editable.length,
@@ -2104,7 +2087,7 @@ class ChatController(args: Bundle) :
                 editable.replace(editable.getSpanStart(mentionSpan), editable.getSpanEnd(mentionSpan), "@$mentionId")
             }
 
-            binding.messageInputView.inputEditText?.setText("")
+            binding?.messageInputView?.inputEditText?.setText("")
             val replyMessageId: Int? = view?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.tag as Int?
             sendMessage(
                 editable,
@@ -2143,11 +2126,11 @@ class ChatController(args: Bundle) :
                         myFirstMessage = message
 
                         try {
-                            if (binding.popupBubbleView.isShown) {
-                                binding.popupBubbleView.hide()
+                            if (binding?.popupBubbleView?.isShown == true) {
+                                binding?.popupBubbleView?.hide()
                             }
 
-                            binding.messagesListView.smoothScrollToPosition(0)
+                            binding?.messagesListView?.smoothScrollToPosition(0)
                         } catch (npe: NullPointerException) {
                             // view binding can be null
                             // since this is called asynchronously and UI might have been destroyed in the meantime
@@ -2161,11 +2144,11 @@ class ChatController(args: Bundle) :
                             if (code.toString().startsWith("2")) {
                                 myFirstMessage = message
 
-                                if (binding.popupBubbleView.isShown) {
-                                    binding.popupBubbleView.hide()
+                                if (binding?.popupBubbleView?.isShown == true) {
+                                    binding?.popupBubbleView?.hide()
                                 }
 
-                                binding.messagesListView.smoothScrollToPosition(0)
+                                binding?.messagesListView?.smoothScrollToPosition(0)
                             }
                         }
                     }
@@ -2379,7 +2362,7 @@ class ChatController(args: Bundle) :
                 cancelNotificationsForCurrentConversation()
 
                 isFirstMessagesProcessing = false
-                binding.progressBar.visibility = View.GONE
+                binding?.progressBar?.visibility = View.GONE
             }
 
             historyRead = true
@@ -2406,9 +2389,9 @@ class ChatController(args: Bundle) :
             cancelNotificationsForCurrentConversation()
 
             isFirstMessagesProcessing = false
-            binding.progressBar.visibility = View.GONE
+            binding?.progressBar?.visibility = View.GONE
 
-            binding.messagesListView.visibility = View.VISIBLE
+            binding?.messagesListView?.visibility = View.VISIBLE
         }
 
         if (isFromTheFuture) {
@@ -2468,7 +2451,7 @@ class ChatController(args: Bundle) :
         if (shouldAddNewMessagesNotice && adapter != null) {
             layoutManager?.scrollToPositionWithOffset(
                 adapter!!.getMessagePositionByIdInReverse("-1"),
-                binding.messagesListView.height / 2
+                binding?.messagesListView?.height!! / 2
             )
         }
     }
@@ -2508,10 +2491,10 @@ class ChatController(args: Bundle) :
 
     private fun modifyMessageCount(shouldAddNewMessagesNotice: Boolean, shouldScroll: Boolean) {
         if (!shouldAddNewMessagesNotice && !shouldScroll) {
-            if (!binding.popupBubbleView.isShown) {
+            if (!binding?.popupBubbleView?.isShown!!) {
                 newMessagesCount = 1
-                binding.popupBubbleView.show()
-            } else if (binding.popupBubbleView.isShown) {
+                binding?.popupBubbleView?.show()
+            } else if (binding?.popupBubbleView?.isShown!!) {
                 newMessagesCount++
             }
         } else {
@@ -2622,15 +2605,15 @@ class ChatController(args: Bundle) :
         super.onCreateOptionsMenu(menu, inflater)
         inflater.inflate(R.menu.menu_conversation, menu)
 
-        viewThemeUtils.platform.colorToolbarMenuIcon(
-            binding.messageInputView.context,
-            menu.findItem(R.id.conversation_voice_call)
-        )
+        binding?.messageInputView?.context?.let {
+            viewThemeUtils.platform.colorToolbarMenuIcon(
+                it, menu.findItem(R.id.conversation_voice_call)
+            )
 
-        viewThemeUtils.platform.colorToolbarMenuIcon(
-            binding.messageInputView.context,
-            menu.findItem(R.id.conversation_video_call)
-        )
+            viewThemeUtils.platform.colorToolbarMenuIcon(
+                it, menu.findItem(R.id.conversation_video_call)
+            )
+        }
 
         if (conversationUser?.userId == "?") {
             menu.removeItem(R.id.conversation_info)
@@ -3148,25 +3131,21 @@ class ChatController(args: Bundle) :
     fun replyToMessage(message: IMessage?) {
         val chatMessage = message as ChatMessage?
         chatMessage?.let {
-            binding.messageInputView.findViewById<ImageButton>(R.id.attachmentButton)?.visibility =
+            binding?.messageInputView?.findViewById<ImageButton>(R.id.attachmentButton)?.visibility =
                 View.GONE
-            binding.messageInputView.findViewById<ImageButton>(R.id.cancelReplyButton)?.visibility =
+            binding?.messageInputView?.findViewById<ImageButton>(R.id.cancelReplyButton)?.visibility =
                 View.VISIBLE
 
-            val quotedMessage = binding
-                .messageInputView
-                .findViewById<EmojiTextView>(R.id.quotedMessage)
+            val quotedMessage = binding?.messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)
 
             quotedMessage?.maxLines = 2
             quotedMessage?.ellipsize = TextUtils.TruncateAt.END
             quotedMessage?.text = it.text
-            binding.messageInputView.findViewById<EmojiTextView>(R.id.quotedMessageAuthor)?.text =
+            binding?.messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessageAuthor)?.text =
                 it.actorDisplayName ?: context.getText(R.string.nc_nick_guest)
 
-            conversationUser?.let { currentUser ->
-                val quotedMessageImage = binding
-                    .messageInputView
-                    .findViewById<ImageView>(R.id.quotedMessageImage)
+            conversationUser?.let {
+                val quotedMessageImage = binding?.messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)
                 chatMessage.imageUrl?.let { previewImageUrl ->
                     quotedMessageImage?.visibility = View.VISIBLE
 
@@ -3184,16 +3163,12 @@ class ChatController(args: Bundle) :
                         addHeader("Authorization", credentials!!)
                     }
                 } ?: run {
-                    binding
-                        .messageInputView
-                        .findViewById<ImageView>(R.id.quotedMessageImage)
-                        ?.visibility = View.GONE
+                    binding?.messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.visibility = View.GONE
                 }
             }
 
-            val quotedChatMessageView = binding
-                .messageInputView
-                .findViewById<RelativeLayout>(R.id.quotedChatMessageView)
+            val quotedChatMessageView =
+                binding?.messageInputView?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)
             quotedChatMessageView?.tag = message?.jsonMessageId
             quotedChatMessageView?.visibility = View.VISIBLE
         }
@@ -3201,11 +3176,11 @@ class ChatController(args: Bundle) :
 
     private fun showMicrophoneButton(show: Boolean) {
         if (show && CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "voice-message-sharing")) {
-            binding.messageInputView.messageSendButton.visibility = View.GONE
-            binding.messageInputView.recordAudioButton.visibility = View.VISIBLE
+            binding?.messageInputView?.messageSendButton?.visibility = View.GONE
+            binding?.messageInputView?.recordAudioButton?.visibility = View.VISIBLE
         } else {
-            binding.messageInputView.messageSendButton.visibility = View.VISIBLE
-            binding.messageInputView.recordAudioButton.visibility = View.GONE
+            binding?.messageInputView?.messageSendButton?.visibility = View.VISIBLE
+            binding?.messageInputView?.recordAudioButton?.visibility = View.GONE
         }
     }
 
@@ -3236,7 +3211,7 @@ class ChatController(args: Bundle) :
         }
 
         if (message.reactionsSelf == null) {
-            message.reactionsSelf = ArrayList<String>()
+            message.reactionsSelf = ArrayList()
         }
 
         var amount = message.reactions!![emoji]
@@ -3254,7 +3229,7 @@ class ChatController(args: Bundle) :
         }
 
         if (message.reactionsSelf == null) {
-            message.reactionsSelf = ArrayList<String>()
+            message.reactionsSelf = ArrayList()
         }
 
         var amount = message.reactions!![emoji]

+ 34 - 32
app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt

@@ -92,7 +92,7 @@ class ContactsController(args: Bundle) :
     BaseController(R.layout.controller_contacts_rv),
     SearchView.OnQueryTextListener,
     FlexibleAdapter.OnItemClickListener {
-    private val binding: ControllerContactsRvBinding by viewBinding(ControllerContactsRvBinding::bind)
+    private val binding: ControllerContactsRvBinding? by viewBinding(ControllerContactsRvBinding::bind)
 
     @Inject
     lateinit var userManager: UserManager
@@ -153,13 +153,13 @@ class ContactsController(args: Bundle) :
             toggleConversationPrivacyLayout(!isPublicCall)
         }
         if (isAddingParticipantsView) {
-            binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.GONE
-            binding.conversationPrivacyToggle.callHeaderLayout.visibility = View.GONE
+            binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.GONE
+            binding?.conversationPrivacyToggle?.callHeaderLayout?.visibility = View.GONE
         } else {
-            binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.setOnClickListener {
+            binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.setOnClickListener {
                 joinConversationViaLink()
             }
-            binding.conversationPrivacyToggle.callHeaderLayout.setOnClickListener {
+            binding?.conversationPrivacyToggle?.callHeaderLayout?.setOnClickListener {
                 toggleCallHeader()
             }
         }
@@ -382,10 +382,12 @@ class ContactsController(args: Bundle) :
         super.onPrepareOptionsMenu(menu)
 
         if (searchItem != null) {
-            viewThemeUtils.platform.colorToolbarMenuIcon(
-                binding.titleTextView.context,
-                searchItem!!
-            )
+            binding?.titleTextView?.let {
+                viewThemeUtils.platform.colorToolbarMenuIcon(
+                    it.context,
+                    searchItem!!
+                )
+            }
         }
 
         checkAndHandleDoneMenuItem()
@@ -451,20 +453,20 @@ class ContactsController(args: Bundle) :
                     }
 
                     withNullableControllerViewBinding {
-                        binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
+                        binding?.controllerGenericRv?.swipeRefreshLayout?.isRefreshing = false
                     }
                 }
 
                 override fun onError(e: Throwable) {
                     withNullableControllerViewBinding {
-                        binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
+                        binding?.controllerGenericRv?.swipeRefreshLayout?.isRefreshing = false
                     }
                     dispose(contactsQueryDisposable)
                 }
 
                 override fun onComplete() {
                     withNullableControllerViewBinding {
-                        binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
+                        binding?.controllerGenericRv?.swipeRefreshLayout?.isRefreshing = false
                     }
                     dispose(contactsQueryDisposable)
                     alreadyFetching = false
@@ -627,31 +629,31 @@ class ContactsController(args: Bundle) :
 
     private fun prepareViews() {
         layoutManager = SmoothScrollLinearLayoutManager(activity)
-        binding.controllerGenericRv.recyclerView.layoutManager = layoutManager
-        binding.controllerGenericRv.recyclerView.setHasFixedSize(true)
-        binding.controllerGenericRv.recyclerView.adapter = adapter
-        binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() }
+        binding?.controllerGenericRv?.recyclerView?.layoutManager = layoutManager
+        binding?.controllerGenericRv?.recyclerView?.setHasFixedSize(true)
+        binding?.controllerGenericRv?.recyclerView?.adapter = adapter
+        binding?.controllerGenericRv?.swipeRefreshLayout?.setOnRefreshListener { fetchData() }
 
-        viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.controllerGenericRv.swipeRefreshLayout)
+        binding?.controllerGenericRv?.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it.swipeRefreshLayout) }
 
-        binding.joinConversationViaLink.joinConversationViaLinkImageView
-            .background
-            .setColorFilter(
+        binding?.joinConversationViaLink?.joinConversationViaLinkImageView
+            ?.background
+            ?.setColorFilter(
                 ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
                 PorterDuff.Mode.SRC_IN
             )
 
-        viewThemeUtils.platform.colorImageViewButton(binding.conversationPrivacyToggle.publicCallLink)
+        binding?.conversationPrivacyToggle?.let { viewThemeUtils.platform.colorImageViewButton(it.publicCallLink) }
         disengageProgressBar()
     }
 
     private fun disengageProgressBar() {
         if (!alreadyFetching) {
-            binding.loadingContent.visibility = View.GONE
-            binding.controllerGenericRv.root.visibility = View.VISIBLE
+            binding?.loadingContent?.visibility = View.GONE
+            binding?.controllerGenericRv?.root?.visibility = View.VISIBLE
             if (isNewConversationView) {
-                binding.conversationPrivacyToggle.callHeaderLayout.visibility = View.VISIBLE
-                binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.VISIBLE
+                binding?.conversationPrivacyToggle?.callHeaderLayout?.visibility = View.VISIBLE
+                binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.VISIBLE
             }
         }
     }
@@ -698,7 +700,7 @@ class ContactsController(args: Bundle) :
         }
 
         withNullableControllerViewBinding {
-            binding.controllerGenericRv.swipeRefreshLayout.isEnabled = !adapter!!.hasFilter()
+            binding?.controllerGenericRv?.swipeRefreshLayout?.isEnabled = !adapter!!.hasFilter()
         }
 
         return true
@@ -929,11 +931,11 @@ class ContactsController(args: Bundle) :
     private fun toggleConversationPrivacyLayout(showInitialLayout: Boolean) {
         withNullableControllerViewBinding {
             if (showInitialLayout) {
-                binding.conversationPrivacyToggle.initialRelativeLayout.visibility = View.VISIBLE
-                binding.conversationPrivacyToggle.secondaryRelativeLayout.visibility = View.GONE
+                binding?.conversationPrivacyToggle?.initialRelativeLayout?.visibility = View.VISIBLE
+                binding?.conversationPrivacyToggle?.secondaryRelativeLayout?.visibility = View.GONE
             } else {
-                binding.conversationPrivacyToggle.initialRelativeLayout.visibility = View.GONE
-                binding.conversationPrivacyToggle.secondaryRelativeLayout.visibility = View.VISIBLE
+                binding?.conversationPrivacyToggle?.initialRelativeLayout?.visibility = View.GONE
+                binding?.conversationPrivacyToggle?.secondaryRelativeLayout?.visibility = View.VISIBLE
             }
         }
     }
@@ -941,10 +943,10 @@ class ContactsController(args: Bundle) :
     private fun toggleConversationViaLinkVisibility(isPublicCall: Boolean) {
         withNullableControllerViewBinding {
             if (isPublicCall) {
-                binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.GONE
+                binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.GONE
                 updateGroupParticipantSelection()
             } else {
-                binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.VISIBLE
+                binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.VISIBLE
             }
         }
     }

+ 129 - 113
app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt

@@ -63,9 +63,9 @@ import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.databinding.ControllerConversationInfoBinding
 import com.nextcloud.talk.events.EventStatus
 import com.nextcloud.talk.extensions.loadAvatar
-import com.nextcloud.talk.extensions.loadSystemAvatar
 import com.nextcloud.talk.extensions.loadGroupCallAvatar
 import com.nextcloud.talk.extensions.loadPublicCallAvatar
+import com.nextcloud.talk.extensions.loadSystemAvatar
 import com.nextcloud.talk.jobs.DeleteConversationWorker
 import com.nextcloud.talk.jobs.LeaveConversationWorker
 import com.nextcloud.talk.models.json.conversations.Conversation
@@ -107,7 +107,7 @@ class ConversationInfoController(args: Bundle) :
     ),
     FlexibleAdapter.OnItemClickListener {
 
-    private val binding: ControllerConversationInfoBinding by viewBinding(ControllerConversationInfoBinding::bind)
+    private val binding: ControllerConversationInfoBinding? by viewBinding(ControllerConversationInfoBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -173,19 +173,19 @@ class ConversationInfoController(args: Bundle) :
             databaseStorageModule = DatabaseStorageModule(conversationUser!!, conversationToken)
         }
 
-        binding.notificationSettingsView.notificationSettings.setStorageModule(databaseStorageModule)
-        binding.webinarInfoView.webinarSettings.setStorageModule(databaseStorageModule)
-        binding.guestAccessView.guestAccessSettings.setStorageModule(databaseStorageModule)
+        binding?.notificationSettingsView?.notificationSettings?.setStorageModule(databaseStorageModule)
+        binding?.webinarInfoView?.webinarSettings?.setStorageModule(databaseStorageModule)
+        binding?.guestAccessView?.guestAccessSettings?.setStorageModule(databaseStorageModule)
 
-        binding.deleteConversationAction.setOnClickListener { showDeleteConversationDialog() }
-        binding.leaveConversationAction.setOnClickListener { leaveConversation() }
-        binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() }
-        binding.addParticipantsAction.setOnClickListener { addParticipants() }
+        binding?.deleteConversationAction?.setOnClickListener { showDeleteConversationDialog() }
+        binding?.leaveConversationAction?.setOnClickListener { leaveConversation() }
+        binding?.clearConversationHistory?.setOnClickListener { showClearHistoryDialog() }
+        binding?.addParticipantsAction?.setOnClickListener { addParticipants() }
 
         if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) {
-            binding.showSharedItemsAction.setOnClickListener { showSharedItems() }
+            binding?.showSharedItemsAction?.setOnClickListener { showSharedItems() }
         } else {
-            binding.categorySharedItems.visibility = GONE
+            binding?.categorySharedItems?.visibility = GONE
         }
 
         fetchRoomInfo()
@@ -195,19 +195,19 @@ class ConversationInfoController(args: Bundle) :
     }
 
     private fun themeSwitchPreferences() {
-        binding.run {
+        binding?.run {
             listOf(
-                binding.webinarInfoView.conversationInfoLobby,
-                binding.notificationSettingsView.callNotifications,
-                binding.notificationSettingsView.conversationInfoPriorityConversation,
-                binding.guestAccessView.guestAccessAllowSwitch,
-                binding.guestAccessView.guestAccessPasswordSwitch
+                binding?.webinarInfoView?.conversationInfoLobby!!,
+                binding?.notificationSettingsView?.callNotifications!!,
+                binding?.notificationSettingsView?.conversationInfoPriorityConversation!!,
+                binding?.guestAccessView?.guestAccessAllowSwitch!!,
+                binding?.guestAccessView?.guestAccessPasswordSwitch!!
             ).forEach(viewThemeUtils.talk::colorSwitchPreference)
         }
     }
 
     private fun themeCategories() {
-        binding.run {
+        binding?.run {
             listOf(
                 conversationInfoName,
                 conversationDescription,
@@ -216,9 +216,9 @@ class ConversationInfoController(args: Bundle) :
                 ownOptions,
                 categorySharedItems,
                 categoryConversationSettings,
-                binding.guestAccessView.guestAccessCategory,
-                binding.webinarInfoView.conversationInfoWebinar,
-                binding.notificationSettingsView.notificationSettingsCategory
+                binding?.guestAccessView?.guestAccessCategory!!,
+                binding?.webinarInfoView?.conversationInfoWebinar!!,
+                binding?.notificationSettingsView?.notificationSettingsCategory!!
             ).forEach(viewThemeUtils.talk::colorPreferenceCategory)
         }
     }
@@ -237,9 +237,9 @@ class ConversationInfoController(args: Bundle) :
     override fun onViewBound(view: View) {
         super.onViewBound(view)
 
-        binding.addParticipantsAction.visibility = GONE
+        binding?.addParticipantsAction?.visibility = GONE
 
-        viewThemeUtils.platform.colorCircularProgressBar(binding.progressBar)
+        binding?.progressBar?.let { viewThemeUtils.platform.colorCircularProgressBar(it) }
     }
 
     private fun setupWebinaryView() {
@@ -247,16 +247,16 @@ class ConversationInfoController(args: Bundle) :
             webinaryRoomType(conversation!!) &&
             conversation!!.canModerate(conversationUser!!)
         ) {
-            binding.webinarInfoView.webinarSettings.visibility = VISIBLE
+            binding?.webinarInfoView?.webinarSettings?.visibility = VISIBLE
 
             val isLobbyOpenToModeratorsOnly =
                 conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY
-            (binding.webinarInfoView.conversationInfoLobby.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
+            (binding?.webinarInfoView?.conversationInfoLobby?.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
                 .isChecked = isLobbyOpenToModeratorsOnly
 
             reconfigureLobbyTimerView()
 
-            binding.webinarInfoView.startTimePreferences.setOnClickListener {
+            binding?.webinarInfoView?.startTimePreferences?.setOnClickListener {
                 MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
                     val currentTimeCalendar = Calendar.getInstance()
                     if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != 0L) {
@@ -277,13 +277,13 @@ class ConversationInfoController(args: Bundle) :
                 }
             }
 
-            (binding.webinarInfoView.conversationInfoLobby.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
+            (binding?.webinarInfoView?.conversationInfoLobby?.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
                 .setOnCheckedChangeListener { _, _ ->
                     reconfigureLobbyTimerView()
                     submitLobbyChanges()
                 }
         } else {
-            binding.webinarInfoView.webinarSettings.visibility = GONE
+            binding?.webinarInfoView?.webinarSettings?.visibility = GONE
         }
     }
 
@@ -294,7 +294,7 @@ class ConversationInfoController(args: Bundle) :
 
     private fun reconfigureLobbyTimerView(dateTime: Calendar? = null) {
         val isChecked =
-            (binding.webinarInfoView.conversationInfoLobby.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
+            (binding?.webinarInfoView?.conversationInfoLobby?.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
                 .isChecked
 
         if (dateTime != null && isChecked) {
@@ -313,25 +313,25 @@ class ConversationInfoController(args: Bundle) :
             conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE &&
             conversation!!.lobbyTimer != 0L
         ) {
-            binding.webinarInfoView.startTimePreferences.setSummary(
+            binding?.webinarInfoView?.startTimePreferences?.setSummary(
                 dateUtils.getLocalDateTimeStringFromTimestamp(
                     conversation!!.lobbyTimer!! * DateConstants.SECOND_DIVIDER,
                 )
             )
         } else {
-            binding.webinarInfoView.startTimePreferences.setSummary(R.string.nc_manual)
+            binding?.webinarInfoView?.startTimePreferences?.setSummary(R.string.nc_manual)
         }
 
         if (isChecked) {
-            binding.webinarInfoView.startTimePreferences.visibility = VISIBLE
+            binding?.webinarInfoView?.startTimePreferences?.visibility = VISIBLE
         } else {
-            binding.webinarInfoView.startTimePreferences.visibility = GONE
+            binding?.webinarInfoView?.startTimePreferences?.visibility = GONE
         }
     }
 
     fun submitLobbyChanges() {
         val state = if (
-            (binding.webinarInfoView.conversationInfoLobby.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
+            (binding?.webinarInfoView?.conversationInfoLobby?.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
                 .isChecked
         ) 1 else 0
 
@@ -376,23 +376,30 @@ class ConversationInfoController(args: Bundle) :
 
     private fun showDeleteConversationDialog() {
         if (activity != null) {
-            val dialogBuilder = MaterialAlertDialogBuilder(binding.conversationInfoName.context)
-                .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp))
-                .setTitle(R.string.nc_delete_call)
-                .setMessage(R.string.nc_delete_conversation_more)
-                .setPositiveButton(R.string.nc_delete) { _, _ ->
-                    deleteConversation()
-                }
-                .setNegativeButton(R.string.nc_cancel) { _, _ ->
-                    // unused atm
-                }
-            viewThemeUtils.dialog
-                .colorMaterialAlertDialogBackground(binding.conversationInfoName.context, dialogBuilder)
-            val dialog = dialogBuilder.show()
-            viewThemeUtils.platform.colorTextButtons(
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-            )
+            binding?.conversationInfoName?.let {
+                val dialogBuilder = MaterialAlertDialogBuilder(it.context)
+                    .setIcon(
+                        viewThemeUtils.dialog.colorMaterialAlertDialogIcon(
+                            context, R.drawable.ic_delete_black_24dp
+                        )
+                    )
+                    .setTitle(R.string.nc_delete_call)
+                    .setMessage(R.string.nc_delete_conversation_more)
+                    .setPositiveButton(R.string.nc_delete) { _, _ ->
+                        deleteConversation()
+                    }
+                    .setNegativeButton(R.string.nc_cancel) { _, _ ->
+                        // unused atm
+                    }
+
+                viewThemeUtils.dialog
+                    .colorMaterialAlertDialogBackground(it.context, dialogBuilder)
+                val dialog = dialogBuilder.show()
+                viewThemeUtils.platform.colorTextButtons(
+                    dialog.getButton(AlertDialog.BUTTON_POSITIVE),
+                    dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+                )
+            }
         }
     }
 
@@ -403,9 +410,9 @@ class ConversationInfoController(args: Bundle) :
             }
 
             val layoutManager = SmoothScrollLinearLayoutManager(activity)
-            binding.recyclerView.layoutManager = layoutManager
-            binding.recyclerView.setHasFixedSize(true)
-            binding.recyclerView.adapter = adapter
+            binding?.recyclerView?.layoutManager = layoutManager
+            binding?.recyclerView?.setHasFixedSize(true)
+            binding?.recyclerView?.adapter = adapter
 
             adapter!!.addListener(this)
         }
@@ -446,7 +453,7 @@ class ConversationInfoController(args: Bundle) :
 
         setupAdapter()
 
-        binding.participantsListCategory.visibility = VISIBLE
+        binding?.participantsListCategory?.visibility = VISIBLE
         adapter!!.updateDataSet(userItems)
     }
 
@@ -548,23 +555,30 @@ class ConversationInfoController(args: Bundle) :
 
     private fun showClearHistoryDialog() {
         if (activity != null) {
-            val dialogBuilder = MaterialAlertDialogBuilder(binding.conversationInfoName.context)
-                .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp))
-                .setTitle(R.string.nc_clear_history)
-                .setMessage(R.string.nc_clear_history_warning)
-                .setPositiveButton(R.string.nc_delete_all) { _, _ ->
-                    clearHistory()
-                }
-                .setNegativeButton(R.string.nc_cancel) { _, _ ->
-                    // unused atm
-                }
-            viewThemeUtils.dialog
-                .colorMaterialAlertDialogBackground(binding.conversationInfoName.context, dialogBuilder)
-            val dialog = dialogBuilder.show()
-            viewThemeUtils.platform.colorTextButtons(
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-            )
+            binding?.conversationInfoName?.context?.let {
+                val dialogBuilder = MaterialAlertDialogBuilder(it)
+                    .setIcon(
+                        viewThemeUtils.dialog.colorMaterialAlertDialogIcon(
+                            context, R.drawable.ic_delete_black_24dp
+                        )
+                    )
+                    .setTitle(R.string.nc_clear_history)
+                    .setMessage(R.string.nc_clear_history_warning)
+                    .setPositiveButton(R.string.nc_delete_all) { _, _ ->
+                        clearHistory()
+                    }
+                    .setNegativeButton(R.string.nc_cancel) { _, _ ->
+                        // unused atm
+                    }
+
+                viewThemeUtils.dialog
+                    .colorMaterialAlertDialogBackground(it, dialogBuilder)
+                val dialog = dialogBuilder.show()
+                viewThemeUtils.platform.colorTextButtons(
+                    dialog.getButton(AlertDialog.BUTTON_POSITIVE),
+                    dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+                )
+            }
         }
     }
 
@@ -638,70 +652,72 @@ class ConversationInfoController(args: Bundle) :
                         val conversationCopy = conversation
 
                         if (conversationCopy!!.canModerate(conversationUser)) {
-                            binding.addParticipantsAction.visibility = VISIBLE
+                            binding?.addParticipantsAction?.visibility = VISIBLE
                             if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "clear-history")) {
-                                binding.clearConversationHistory.visibility = VISIBLE
+                                binding?.clearConversationHistory?.visibility = VISIBLE
                             } else {
-                                binding.clearConversationHistory.visibility = GONE
+                                binding?.clearConversationHistory?.visibility = GONE
                             }
                         } else {
-                            binding.addParticipantsAction.visibility = GONE
-                            binding.clearConversationHistory.visibility = GONE
+                            binding?.addParticipantsAction?.visibility = GONE
+                            binding?.clearConversationHistory?.visibility = GONE
                         }
 
                         if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
-                            binding.ownOptions.visibility = VISIBLE
+                            binding?.ownOptions?.visibility = VISIBLE
 
                             setupWebinaryView()
 
                             if (!conversation!!.canLeave()) {
-                                binding.leaveConversationAction.visibility = GONE
+                                binding?.leaveConversationAction?.visibility = GONE
                             } else {
-                                binding.leaveConversationAction.visibility = VISIBLE
+                                binding?.leaveConversationAction?.visibility = VISIBLE
                             }
 
                             if (!conversation!!.canDelete(conversationUser)) {
-                                binding.deleteConversationAction.visibility = GONE
+                                binding?.deleteConversationAction?.visibility = GONE
                             } else {
-                                binding.deleteConversationAction.visibility = VISIBLE
+                                binding?.deleteConversationAction?.visibility = VISIBLE
                             }
 
                             if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
-                                binding.notificationSettingsView.callNotifications.visibility = GONE
+                                binding?.notificationSettingsView?.callNotifications?.visibility = GONE
                             }
 
                             if (conversation!!.notificationCalls === null) {
-                                binding.notificationSettingsView.callNotifications.visibility = GONE
+                                binding?.notificationSettingsView?.callNotifications?.visibility = GONE
                             } else {
-                                binding.notificationSettingsView.callNotifications.value =
+                                binding?.notificationSettingsView?.callNotifications?.value =
                                     conversationCopy.notificationCalls == 1
                             }
 
                             getListOfParticipants()
 
-                            binding.progressBar.visibility = GONE
+                            binding?.progressBar?.visibility = GONE
 
-                            binding.conversationInfoName.visibility = VISIBLE
+                            binding?.conversationInfoName?.visibility = VISIBLE
 
-                            binding.displayNameText.text = conversation!!.displayName
+                            binding?.displayNameText?.text = conversation!!.displayName
 
                             if (conversation!!.description != null && !conversation!!.description!!.isEmpty()) {
-                                binding.descriptionText.text = conversation!!.description
-                                binding.conversationDescription.visibility = VISIBLE
+                                binding?.descriptionText?.text = conversation!!.description
+                                binding?.conversationDescription?.visibility = VISIBLE
                             }
 
                             loadConversationAvatar()
                             adjustNotificationLevelUI()
                             initExpiringMessageOption()
 
-                            GuestAccessHelper(
-                                this@ConversationInfoController,
-                                binding,
-                                conversation!!,
-                                conversationUser
-                            ).setupGuestAccess()
+                            binding?.let {
+                                GuestAccessHelper(
+                                    this@ConversationInfoController,
+                                    it,
+                                    conversation!!,
+                                    conversationUser
+                                ).setupGuestAccess()
+                            }
 
-                            binding.notificationSettingsView.notificationSettings.visibility = VISIBLE
+                            binding?.notificationSettingsView?.notificationSettings?.visibility = VISIBLE
                         }
                     } catch (npe: NullPointerException) {
                         // view binding can be null
@@ -725,11 +741,11 @@ class ConversationInfoController(args: Bundle) :
             CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration")
         ) {
             databaseStorageModule?.setMessageExpiration(conversation!!.messageExpiration)
-            binding.conversationInfoExpireMessages.setStorageModule(databaseStorageModule)
-            binding.conversationInfoExpireMessages.visibility = View.VISIBLE
-            binding.conversationInfoExpireMessagesExplanation.visibility = View.VISIBLE
+            binding?.conversationInfoExpireMessages?.setStorageModule(databaseStorageModule)
+            binding?.conversationInfoExpireMessages?.visibility = VISIBLE
+            binding?.conversationInfoExpireMessagesExplanation?.visibility = VISIBLE
         } else {
-            binding.categoryConversationSettings.visibility = View.GONE
+            binding?.categoryConversationSettings?.visibility = GONE
         }
     }
 
@@ -739,8 +755,8 @@ class ConversationInfoController(args: Bundle) :
                 conversationUser != null &&
                 CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")
             ) {
-                binding.notificationSettingsView.conversationInfoMessageNotifications.isEnabled = true
-                binding.notificationSettingsView.conversationInfoMessageNotifications.alpha = 1.0f
+                binding?.notificationSettingsView?.conversationInfoMessageNotifications?.isEnabled = true
+                binding?.notificationSettingsView?.conversationInfoMessageNotifications?.alpha = 1.0f
 
                 if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) {
                     val stringValue: String =
@@ -751,13 +767,13 @@ class ConversationInfoController(args: Bundle) :
                             else -> "mention"
                         }
 
-                    binding.notificationSettingsView.conversationInfoMessageNotifications.value = stringValue
+                    binding?.notificationSettingsView?.conversationInfoMessageNotifications?.value = stringValue
                 } else {
                     setProperNotificationValue(conversation)
                 }
             } else {
-                binding.notificationSettingsView.conversationInfoMessageNotifications.isEnabled = false
-                binding.notificationSettingsView.conversationInfoMessageNotifications.alpha = LOW_EMPHASIS_OPACITY
+                binding?.notificationSettingsView?.conversationInfoMessageNotifications?.isEnabled = false
+                binding?.notificationSettingsView?.conversationInfoMessageNotifications?.alpha = LOW_EMPHASIS_OPACITY
                 setProperNotificationValue(conversation)
             }
         }
@@ -767,28 +783,28 @@ class ConversationInfoController(args: Bundle) :
         if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
             // hack to see if we get mentioned always or just on mention
             if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag")) {
-                binding.notificationSettingsView.conversationInfoMessageNotifications.value = "always"
+                binding?.notificationSettingsView?.conversationInfoMessageNotifications?.value = "always"
             } else {
-                binding.notificationSettingsView.conversationInfoMessageNotifications.value = "mention"
+                binding?.notificationSettingsView?.conversationInfoMessageNotifications?.value = "mention"
             }
         } else {
-            binding.notificationSettingsView.conversationInfoMessageNotifications.value = "mention"
+            binding?.notificationSettingsView?.conversationInfoMessageNotifications?.value = "mention"
         }
     }
 
     private fun loadConversationAvatar() {
         when (conversation!!.type) {
             Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) {
-                conversation!!.name?.let { binding.avatarImage.loadAvatar(conversationUser!!, it) }
+                conversation!!.name?.let { binding?.avatarImage?.loadAvatar(conversationUser!!, it) }
             }
             Conversation.ConversationType.ROOM_GROUP_CALL -> {
-                binding.avatarImage.loadGroupCallAvatar(viewThemeUtils)
+                binding?.avatarImage?.loadGroupCallAvatar(viewThemeUtils)
             }
             Conversation.ConversationType.ROOM_PUBLIC_CALL -> {
-                binding.avatarImage.loadPublicCallAvatar(viewThemeUtils)
+                binding?.avatarImage?.loadPublicCallAvatar(viewThemeUtils)
             }
             Conversation.ConversationType.ROOM_SYSTEM -> {
-                binding.avatarImage.loadSystemAvatar()
+                binding?.avatarImage?.loadSystemAvatar()
             }
 
             else -> {

+ 169 - 150
app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt

@@ -157,7 +157,7 @@ class ConversationsListController(bundle: Bundle) :
     @Inject
     lateinit var unifiedSearchRepository: UnifiedSearchRepository
 
-    private val binding: ControllerConversationsRvBinding by viewBinding(ControllerConversationsRvBinding::bind)
+    private val binding: ControllerConversationsRvBinding? by viewBinding(ControllerConversationsRvBinding::bind)
 
     override val title: String
         get() = resources!!.getString(R.string.nc_app_product_name)
@@ -200,7 +200,7 @@ class ConversationsListController(bundle: Bundle) :
         if (adapter == null) {
             adapter = FlexibleAdapter(conversationItems, activity, true)
         } else {
-            binding.loadingContent.visibility = View.GONE
+            binding?.loadingContent?.visibility = View.GONE
         }
         adapter!!.addListener(this)
         prepareViews()
@@ -410,7 +410,7 @@ class ConversationsListController(bundle: Bundle) :
                     adapter!!.updateDataSet(searchableConversationItems, false)
                     adapter!!.showAllHeaders()
                     withNullableControllerViewBinding {
-                        binding.swipeRefreshLayoutView.isEnabled = false
+                        binding?.swipeRefreshLayoutView?.isEnabled = false
                     }
                     return true
                 }
@@ -422,10 +422,10 @@ class ConversationsListController(bundle: Bundle) :
                     if (searchHelper != null) {
                         // cancel any pending searches
                         searchHelper!!.cancelSearch()
-                        binding.swipeRefreshLayoutView.isRefreshing = false
+                        binding?.swipeRefreshLayoutView?.isRefreshing = false
                     }
                     withNullableControllerViewBinding {
-                        binding.swipeRefreshLayoutView.isEnabled = true
+                        binding?.swipeRefreshLayoutView?.isEnabled = true
                     }
                     searchView!!.onActionViewCollapsed()
                     val mainActivity = getActivity() as MainActivity?
@@ -441,7 +441,7 @@ class ConversationsListController(bundle: Bundle) :
                                 .resetStatusBar(mainActivity)
                         }
                     }
-                    val layoutManager = binding.recyclerView.layoutManager as SmoothScrollLinearLayoutManager?
+                    val layoutManager = binding?.recyclerView?.layoutManager as SmoothScrollLinearLayoutManager?
                     layoutManager?.scrollToPositionWithOffset(0, 0)
                     return true
                 }
@@ -503,7 +503,7 @@ class ConversationsListController(bundle: Bundle) :
                 }
                 if (adapterWasNull) {
                     adapterWasNull = false
-                    binding.loadingContent.visibility = View.GONE
+                    binding?.loadingContent?.visibility = View.GONE
                 }
                 initOverallLayout(ocs!!.data!!.isNotEmpty())
                 for (conversation in ocs.data!!) {
@@ -515,19 +515,19 @@ class ConversationsListController(bundle: Bundle) :
                 Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong())
                 fetchOpenConversations(apiVersion)
                 withNullableControllerViewBinding {
-                    binding.swipeRefreshLayoutView.isRefreshing = false
+                    binding?.swipeRefreshLayoutView?.isRefreshing = false
                 }
             }, { throwable: Throwable ->
                 handleHttpExceptions(throwable)
                 withNullableControllerViewBinding {
-                    binding.swipeRefreshLayoutView.isRefreshing = false
+                    binding?.swipeRefreshLayoutView?.isRefreshing = false
                     showErrorDialog()
                 }
                 dispose(roomsQueryDisposable)
             }) {
                 dispose(roomsQueryDisposable)
                 withNullableControllerViewBinding {
-                    binding.swipeRefreshLayoutView.isRefreshing = false
+                    binding?.swipeRefreshLayoutView?.isRefreshing = false
                 }
                 isRefreshing = false
             }
@@ -535,18 +535,18 @@ class ConversationsListController(bundle: Bundle) :
 
     private fun initOverallLayout(isConversationListNotEmpty: Boolean) {
         if (isConversationListNotEmpty) {
-            if (binding.emptyLayout.visibility != View.GONE) {
-                binding.emptyLayout.visibility = View.GONE
+            if (binding?.emptyLayout?.visibility != View.GONE) {
+                binding?.emptyLayout?.visibility = View.GONE
             }
-            if (binding.swipeRefreshLayoutView.visibility != View.VISIBLE) {
-                binding.swipeRefreshLayoutView.visibility = View.VISIBLE
+            if (binding?.swipeRefreshLayoutView?.visibility != View.VISIBLE) {
+                binding?.swipeRefreshLayoutView?.visibility = View.VISIBLE
             }
         } else {
-            if (binding.emptyLayout.visibility != View.VISIBLE) {
-                binding.emptyLayout.visibility = View.VISIBLE
+            if (binding?.emptyLayout?.visibility != View.VISIBLE) {
+                binding?.emptyLayout?.visibility = View.VISIBLE
             }
-            if (binding.swipeRefreshLayoutView.visibility != View.GONE) {
-                binding.swipeRefreshLayoutView.visibility = View.GONE
+            if (binding?.swipeRefreshLayoutView?.visibility != View.GONE) {
+                binding?.swipeRefreshLayoutView?.visibility = View.GONE
             }
         }
     }
@@ -584,21 +584,24 @@ class ConversationsListController(bundle: Bundle) :
     }
 
     private fun showErrorDialog() {
-        val dialogBuilder = MaterialAlertDialogBuilder(binding.floatingActionButton.context)
-            .setIcon(
-                viewThemeUtils.dialog.colorMaterialAlertDialogIcon(
-                    context,
-                    R.drawable.ic_baseline_error_outline_24dp,
+        binding?.floatingActionButton?.let {
+            val dialogBuilder = MaterialAlertDialogBuilder(it.context)
+                .setIcon(
+                    viewThemeUtils.dialog.colorMaterialAlertDialogIcon(
+                        context,
+                        R.drawable.ic_baseline_error_outline_24dp,
+                    )
                 )
+                .setTitle(R.string.error_loading_chats)
+                .setCancelable(false)
+                .setNegativeButton(R.string.close, null)
+
+            viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder)
+            val dialog = dialogBuilder.show()
+            viewThemeUtils.platform.colorTextButtons(
+                dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
             )
-            .setTitle(R.string.error_loading_chats)
-            .setCancelable(false)
-            .setNegativeButton(R.string.close, null)
-        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.floatingActionButton.context, dialogBuilder)
-        val dialog = dialogBuilder.show()
-        viewThemeUtils.platform.colorTextButtons(
-            dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-        )
+        }
     }
 
     private fun sortConversations(conversationItems: MutableList<AbstractFlexibleItem<*>>) {
@@ -678,10 +681,10 @@ class ConversationsListController(bundle: Bundle) :
     @SuppressLint("ClickableViewAccessibility")
     private fun prepareViews() {
         layoutManager = SmoothScrollLinearLayoutManager(Objects.requireNonNull(activity))
-        binding.recyclerView.layoutManager = layoutManager
-        binding.recyclerView.setHasFixedSize(true)
-        binding.recyclerView.adapter = adapter
-        binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+        binding?.recyclerView?.layoutManager = layoutManager
+        binding?.recyclerView?.setHasFixedSize(true)
+        binding?.recyclerView?.adapter = adapter
+        binding?.recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
             override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                 super.onScrollStateChanged(recyclerView, newState)
                 if (newState == RecyclerView.SCROLL_STATE_IDLE) {
@@ -689,21 +692,21 @@ class ConversationsListController(bundle: Bundle) :
                 }
             }
         })
-        binding.recyclerView.setOnTouchListener { v: View, _: MotionEvent? ->
+        binding?.recyclerView?.setOnTouchListener { v: View, _: MotionEvent? ->
             if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
                 val imm = activity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                 imm.hideSoftInputFromWindow(v.windowToken, 0)
             }
             false
         }
-        binding.swipeRefreshLayoutView.setOnRefreshListener { fetchRooms() }
-        viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeRefreshLayoutView)
-        binding.emptyLayout.setOnClickListener { showNewConversationsScreen() }
-        binding.floatingActionButton.setOnClickListener {
+        binding?.swipeRefreshLayoutView?.setOnRefreshListener { fetchRooms() }
+        binding?.swipeRefreshLayoutView?.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it) }
+        binding?.emptyLayout?.setOnClickListener { showNewConversationsScreen() }
+        binding?.floatingActionButton?.setOnClickListener {
             run(context)
             showNewConversationsScreen()
         }
-        viewThemeUtils.material.themeFAB(binding.floatingActionButton)
+        binding?.floatingActionButton?.let { viewThemeUtils.material.themeFAB(it) }
         if (activity != null && activity is MainActivity) {
             val activity = activity as MainActivity?
             activity!!.binding.switchAccountButton.setOnClickListener {
@@ -722,13 +725,13 @@ class ConversationsListController(bundle: Bundle) :
                 }
             }
         }
-        binding.newMentionPopupBubble.hide()
-        binding.newMentionPopupBubble.setPopupBubbleListener {
-            binding.recyclerView.smoothScrollToPosition(
+        binding?.newMentionPopupBubble?.hide()
+        binding?.newMentionPopupBubble?.setPopupBubbleListener {
+            binding?.recyclerView?.smoothScrollToPosition(
                 nextUnreadConversationScrollPosition
             )
         }
-        viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.newMentionPopupBubble)
+        binding?.newMentionPopupBubble?.let { viewThemeUtils.material.colorMaterialButtonPrimaryFilled(it) }
     }
 
     @Suppress("Detekt.TooGenericExceptionCaught")
@@ -740,14 +743,14 @@ class ConversationsListController(bundle: Bundle) :
                 val position = adapter!!.getGlobalPositionOf(flexItem)
                 if (hasUnreadItems(conversation) && position > lastVisibleItem) {
                     nextUnreadConversationScrollPosition = position
-                    if (!binding.newMentionPopupBubble.isShown) {
-                        binding.newMentionPopupBubble.show()
+                    if (!binding?.newMentionPopupBubble?.isShown!!) {
+                        binding?.newMentionPopupBubble?.show()
                     }
                     return
                 }
             }
             nextUnreadConversationScrollPosition = 0
-            binding.newMentionPopupBubble.hide()
+            binding?.newMentionPopupBubble?.hide()
         } catch (e: NullPointerException) {
             Log.d(
                 TAG,
@@ -852,7 +855,7 @@ class ConversationsListController(bundle: Bundle) :
     @SuppressLint("CheckResult") // handled by helper
     private fun startMessageSearch(search: String?) {
         withNullableControllerViewBinding {
-            binding.swipeRefreshLayoutView.isRefreshing = true
+            binding?.swipeRefreshLayoutView?.isRefreshing = true
         }
         searchHelper?.startMessageSearch(search!!)
             ?.subscribeOn(Schedulers.io())
@@ -866,7 +869,7 @@ class ConversationsListController(bundle: Bundle) :
 
     @SuppressLint("CheckResult") // handled by helper
     private fun loadMoreMessages() {
-        binding.swipeRefreshLayoutView.isRefreshing = true
+        binding?.swipeRefreshLayoutView?.isRefreshing = true
         val observable = searchHelper!!.loadMore()
         observable?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe({ results: MessageSearchResults -> onMessageSearchResult(results) }) { throwable: Throwable ->
@@ -977,24 +980,27 @@ class ConversationsListController(bundle: Bundle) :
                     selectedConversation!!.displayName
                 )
             }
-            val dialogBuilder = MaterialAlertDialogBuilder(binding.floatingActionButton.context)
-                .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.upload))
-                .setTitle(confirmationQuestion)
-                .setMessage(fileNamesWithLineBreaks.toString())
-                .setPositiveButton(R.string.nc_yes) { _, _ ->
-                    upload()
-                    openConversation()
-                }
-                .setNegativeButton(R.string.nc_no) { _, _ ->
-                    Log.d(TAG, "sharing files aborted, going back to share-to screen")
-                }
-            viewThemeUtils.dialog
-                .colorMaterialAlertDialogBackground(binding.floatingActionButton.context, dialogBuilder)
-            val dialog = dialogBuilder.show()
-            viewThemeUtils.platform.colorTextButtons(
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-            )
+            binding?.floatingActionButton?.let {
+                val dialogBuilder = MaterialAlertDialogBuilder(it.context)
+                    .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.upload))
+                    .setTitle(confirmationQuestion)
+                    .setMessage(fileNamesWithLineBreaks.toString())
+                    .setPositiveButton(R.string.nc_yes) { _, _ ->
+                        upload()
+                        openConversation()
+                    }
+                    .setNegativeButton(R.string.nc_no) { _, _ ->
+                        Log.d(TAG, "sharing files aborted, going back to share-to screen")
+                    }
+
+                viewThemeUtils.dialog
+                    .colorMaterialAlertDialogBackground(it.context, dialogBuilder)
+                val dialog = dialogBuilder.show()
+                viewThemeUtils.platform.colorTextButtons(
+                    dialog.getButton(AlertDialog.BUTTON_POSITIVE),
+                    dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+                )
+            }
         } else {
             requestStoragePermission(this@ConversationsListController)
         }
@@ -1161,28 +1167,85 @@ class ConversationsListController(bundle: Bundle) :
         ) {
             val conversation = Parcels.unwrap<Conversation>(conversationMenuBundle!!.getParcelable(KEY_ROOM))
             if (conversation != null) {
-                val dialogBuilder = MaterialAlertDialogBuilder(binding.floatingActionButton.context)
+                binding?.floatingActionButton?.let {
+                    val dialogBuilder = MaterialAlertDialogBuilder(it.context)
+                        .setIcon(
+                            viewThemeUtils.dialog
+                                .colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp)
+                        )
+                        .setTitle(R.string.nc_delete_call)
+                        .setMessage(R.string.nc_delete_conversation_more)
+                        .setPositiveButton(R.string.nc_delete) { _, _ ->
+                            val data = Data.Builder()
+                            data.putLong(
+                                KEY_INTERNAL_USER_ID,
+                                conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID)
+                            )
+                            data.putString(KEY_ROOM_TOKEN, conversation.token)
+                            conversationMenuBundle = null
+                            deleteConversation(data.build())
+                        }
+                        .setNegativeButton(R.string.nc_cancel) { _, _ ->
+                            conversationMenuBundle = null
+                        }
+
+                    viewThemeUtils.dialog
+                        .colorMaterialAlertDialogBackground(it.context, dialogBuilder)
+                    val dialog = dialogBuilder.show()
+                    viewThemeUtils.platform.colorTextButtons(
+                        dialog.getButton(AlertDialog.BUTTON_POSITIVE),
+                        dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+                    )
+                }
+            }
+        }
+    }
+
+    private fun isInternalUserEqualsCurrentUser(currentUser: User?, conversationMenuBundle: Bundle?): Boolean {
+        return currentUser != null && conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID) == currentUser.id
+    }
+
+    private fun showUnauthorizedDialog() {
+        if (activity != null) {
+            binding?.floatingActionButton?.let {
+                val dialogBuilder = MaterialAlertDialogBuilder(it.context)
                     .setIcon(
-                        viewThemeUtils.dialog
-                            .colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp)
+                        viewThemeUtils.dialog.colorMaterialAlertDialogIcon(
+                            context, R.drawable.ic_delete_black_24dp
+                        )
                     )
-                    .setTitle(R.string.nc_delete_call)
-                    .setMessage(R.string.nc_delete_conversation_more)
+                    .setTitle(R.string.nc_dialog_invalid_password)
+                    .setMessage(R.string.nc_dialog_reauth_or_delete)
+                    .setCancelable(false)
                     .setPositiveButton(R.string.nc_delete) { _, _ ->
-                        val data = Data.Builder()
-                        data.putLong(
-                            KEY_INTERNAL_USER_ID,
-                            conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID)
-                        )
-                        data.putString(KEY_ROOM_TOKEN, conversation.token)
-                        conversationMenuBundle = null
-                        deleteConversation(data.build())
+                        val otherUserExists = userManager
+                            .scheduleUserForDeletionWithId(currentUser!!.id!!)
+                            .blockingGet()
+                        val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
+                        WorkManager.getInstance().enqueue(accountRemovalWork)
+                        if (otherUserExists && view != null) {
+                            onViewBound(view!!)
+                            onAttach(view!!)
+                        } else if (!otherUserExists) {
+                            router.setRoot(
+                                RouterTransaction.with(ServerSelectionController())
+                                    .pushChangeHandler(VerticalChangeHandler())
+                                    .popChangeHandler(VerticalChangeHandler())
+                            )
+                        }
                     }
-                    .setNegativeButton(R.string.nc_cancel) { _, _ ->
-                        conversationMenuBundle = null
+                    .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ ->
+                        router.pushController(
+                            RouterTransaction.with(
+                                WebViewLoginController(currentUser!!.baseUrl, true)
+                            )
+                                .pushChangeHandler(VerticalChangeHandler())
+                                .popChangeHandler(VerticalChangeHandler())
+                        )
                     }
+
                 viewThemeUtils.dialog
-                    .colorMaterialAlertDialogBackground(binding.floatingActionButton.context, dialogBuilder)
+                    .colorMaterialAlertDialogBackground(it.context, dialogBuilder)
                 val dialog = dialogBuilder.show()
                 viewThemeUtils.platform.colorTextButtons(
                     dialog.getButton(AlertDialog.BUTTON_POSITIVE),
@@ -1192,18 +1255,14 @@ class ConversationsListController(bundle: Bundle) :
         }
     }
 
-    private fun isInternalUserEqualsCurrentUser(currentUser: User?, conversationMenuBundle: Bundle?): Boolean {
-        return currentUser != null && conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID) == currentUser.id
-    }
-
-    private fun showUnauthorizedDialog() {
-        if (activity != null) {
-            val dialogBuilder = MaterialAlertDialogBuilder(binding.floatingActionButton.context)
-                .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp))
-                .setTitle(R.string.nc_dialog_invalid_password)
-                .setMessage(R.string.nc_dialog_reauth_or_delete)
+    private fun showServerEOLDialog() {
+        binding?.floatingActionButton?.let {
+            val dialogBuilder = MaterialAlertDialogBuilder(it.context)
+                .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_warning_white))
+                .setTitle(R.string.nc_settings_server_eol_title)
+                .setMessage(R.string.nc_settings_server_eol)
                 .setCancelable(false)
-                .setPositiveButton(R.string.nc_delete) { _, _ ->
+                .setPositiveButton(R.string.nc_settings_remove_account) { _, _ ->
                     val otherUserExists = userManager
                         .scheduleUserForDeletionWithId(currentUser!!.id!!)
                         .blockingGet()
@@ -1214,23 +1273,24 @@ class ConversationsListController(bundle: Bundle) :
                         onAttach(view!!)
                     } else if (!otherUserExists) {
                         router.setRoot(
-                            RouterTransaction.with(ServerSelectionController())
+                            RouterTransaction.with(
+                                ServerSelectionController()
+                            )
                                 .pushChangeHandler(VerticalChangeHandler())
                                 .popChangeHandler(VerticalChangeHandler())
                         )
                     }
                 }
-                .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ ->
-                    router.pushController(
-                        RouterTransaction.with(
-                            WebViewLoginController(currentUser!!.baseUrl, true)
-                        )
-                            .pushChangeHandler(VerticalChangeHandler())
-                            .popChangeHandler(VerticalChangeHandler())
-                    )
+                .setNegativeButton(R.string.nc_cancel) { _, _ ->
+                    if (userManager.users.blockingGet().isNotEmpty()) {
+                        router.pushController(RouterTransaction.with(SwitchAccountController()))
+                    } else {
+                        activity!!.finishAffinity()
+                        activity!!.finish()
+                    }
                 }
-            viewThemeUtils.dialog
-                .colorMaterialAlertDialogBackground(binding.floatingActionButton.context, dialogBuilder)
+
+            viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder)
             val dialog = dialogBuilder.show()
             viewThemeUtils.platform.colorTextButtons(
                 dialog.getButton(AlertDialog.BUTTON_POSITIVE),
@@ -1239,47 +1299,6 @@ class ConversationsListController(bundle: Bundle) :
         }
     }
 
-    private fun showServerEOLDialog() {
-        val dialogBuilder = MaterialAlertDialogBuilder(binding.floatingActionButton.context)
-            .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_warning_white))
-            .setTitle(R.string.nc_settings_server_eol_title)
-            .setMessage(R.string.nc_settings_server_eol)
-            .setCancelable(false)
-            .setPositiveButton(R.string.nc_settings_remove_account) { _, _ ->
-                val otherUserExists = userManager
-                    .scheduleUserForDeletionWithId(currentUser!!.id!!)
-                    .blockingGet()
-                val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
-                WorkManager.getInstance().enqueue(accountRemovalWork)
-                if (otherUserExists && view != null) {
-                    onViewBound(view!!)
-                    onAttach(view!!)
-                } else if (!otherUserExists) {
-                    router.setRoot(
-                        RouterTransaction.with(
-                            ServerSelectionController()
-                        )
-                            .pushChangeHandler(VerticalChangeHandler())
-                            .popChangeHandler(VerticalChangeHandler())
-                    )
-                }
-            }
-            .setNegativeButton(R.string.nc_cancel) { _, _ ->
-                if (userManager.users.blockingGet().isNotEmpty()) {
-                    router.pushController(RouterTransaction.with(SwitchAccountController()))
-                } else {
-                    activity!!.finishAffinity()
-                    activity!!.finish()
-                }
-            }
-        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.floatingActionButton.context, dialogBuilder)
-        val dialog = dialogBuilder.show()
-        viewThemeUtils.platform.colorTextButtons(
-            dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-            dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-        )
-    }
-
     private fun deleteConversation(data: Data) {
         val deleteConversationWorker =
             OneTimeWorkRequest.Builder(DeleteConversationWorker::class.java).setInputData(data).build()
@@ -1309,18 +1328,18 @@ class ConversationsListController(bundle: Bundle) :
                 }
                 // add unified search result at the end of the list
                 adapter!!.addItems(adapter!!.mainItemCount + adapter!!.scrollableHeaders.size, adapterItems)
-                binding.recyclerView.scrollToPosition(0)
+                binding?.recyclerView?.scrollToPosition(0)
             }
         }
         withNullableControllerViewBinding {
-            binding.swipeRefreshLayoutView.isRefreshing = false
+            binding?.swipeRefreshLayoutView?.isRefreshing = false
         }
     }
 
     private fun onMessageSearchError(throwable: Throwable) {
         handleHttpExceptions(throwable)
         withNullableControllerViewBinding {
-            binding.swipeRefreshLayoutView.isRefreshing = false
+            binding?.swipeRefreshLayoutView?.isRefreshing = false
             showErrorDialog()
         }
     }

+ 4 - 4
app/src/main/java/com/nextcloud/talk/controllers/GeocodingController.kt

@@ -63,7 +63,7 @@ class GeocodingController(args: Bundle) :
         args
     ),
     SearchView.OnQueryTextListener {
-    private val binding: ControllerGeocodingBinding by viewBinding(ControllerGeocodingBinding::bind)
+    private val binding: ControllerGeocodingBinding? by viewBinding(ControllerGeocodingBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -94,8 +94,8 @@ class GeocodingController(args: Bundle) :
     }
 
     private fun initAdapter(addresses: List<Address>) {
-        adapter = GeocodingAdapter(binding.geocodingResults.context!!, addresses)
-        binding.geocodingResults.adapter = adapter
+        adapter = GeocodingAdapter(binding?.geocodingResults?.context!!, addresses)
+        binding?.geocodingResults?.adapter = adapter
     }
 
     override fun onAttach(view: View) {
@@ -110,7 +110,7 @@ class GeocodingController(args: Bundle) :
             Log.e(TAG, "search string that was passed to GeocodingController was null or empty")
         }
 
-        binding.geocodingResults.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
+        binding?.geocodingResults?.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
             val address: Address = adapter.getItem(position) as Address
             val listener: GeocodingResultListener? = targetController as GeocodingResultListener?
             listener?.receiveChosenGeocodingResult(address.latitude, address.longitude, address.displayName)

+ 28 - 28
app/src/main/java/com/nextcloud/talk/controllers/LocationPickerController.kt

@@ -90,7 +90,7 @@ class LocationPickerController(args: Bundle) :
     SearchView.OnQueryTextListener,
     LocationListener,
     GeocodingController.GeocodingResultListener {
-    private val binding: ControllerLocationBinding by viewBinding(ControllerLocationBinding::bind)
+    private val binding: ControllerLocationBinding? by viewBinding(ControllerLocationBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -164,13 +164,13 @@ class LocationPickerController(args: Bundle) :
 
     override fun onViewBound(view: View) {
         setLocationDescription(false, receivedChosenGeocodingResult)
-        binding.shareLocation.isClickable = false
-        binding.shareLocation.setOnClickListener {
+        binding?.shareLocation?.isClickable = false
+        binding?.shareLocation?.setOnClickListener {
             if (readyToShareLocation) {
                 shareLocation(
-                    binding.map.mapCenter?.latitude,
-                    binding.map.mapCenter?.longitude,
-                    binding.placeName.text.toString()
+                    binding?.map?.mapCenter?.latitude,
+                    binding?.map?.mapCenter?.longitude,
+                    binding?.placeName?.text.toString()
                 )
             } else {
                 Log.w(TAG, "readyToShareLocation was false while user tried to share location.")
@@ -217,8 +217,8 @@ class LocationPickerController(args: Bundle) :
 
     @Suppress("Detekt.TooGenericExceptionCaught", "Detekt.ComplexMethod")
     private fun initMap() {
-        binding.map.setTileSource(TileSourceFactory.MAPNIK)
-        binding.map.onResume()
+        binding?.map?.setTileSource(TileSourceFactory.MAPNIK)
+        binding?.map?.onResume()
 
         locationManager = activity!!.getSystemService(Context.LOCATION_SERVICE) as LocationManager
 
@@ -229,12 +229,12 @@ class LocationPickerController(args: Bundle) :
         }
 
         val copyrightOverlay = CopyrightOverlay(context)
-        binding.map.overlays?.add(copyrightOverlay)
+        binding?.map?.overlays?.add(copyrightOverlay)
 
-        binding.map.setMultiTouchControls(true)
-        binding.map.isTilesScaledToDpi = true
+        binding?.map?.setMultiTouchControls(true)
+        binding?.map?.isTilesScaledToDpi = true
 
-        locationOverlay = MyLocationNewOverlay(GpsMyLocationProvider(context), binding.map)
+        locationOverlay = MyLocationNewOverlay(GpsMyLocationProvider(context), binding?.map)
         locationOverlay.enableMyLocation()
         locationOverlay.setPersonHotspot(PERSON_HOT_SPOT_X, PERSON_HOT_SPOT_Y)
         locationOverlay.setPersonIcon(
@@ -242,9 +242,9 @@ class LocationPickerController(args: Bundle) :
                 ResourcesCompat.getDrawable(resources!!, R.drawable.current_location_circle, null)
             )
         )
-        binding.map.overlays?.add(locationOverlay)
+        binding?.map?.overlays?.add(locationOverlay)
 
-        val mapController = binding.map.controller
+        val mapController = binding?.map?.controller
 
         if (receivedChosenGeocodingResult) {
             mapController?.setZoom(ZOOM_LEVEL_RECEIVED_RESULT)
@@ -273,16 +273,16 @@ class LocationPickerController(args: Bundle) :
             mapController?.setCenter(GeoPoint(geocodedLat, geocodedLon))
         }
 
-        binding.centerMapButton.setOnClickListener {
+        binding?.centerMapButton?.setOnClickListener {
             if (myLocation.latitude == COORDINATE_ZERO && myLocation.longitude == COORDINATE_ZERO) {
-                Toast.makeText(context, context?.getString(R.string.nc_location_unknown), Toast.LENGTH_LONG).show()
+                Toast.makeText(context, context.getString(R.string.nc_location_unknown), Toast.LENGTH_LONG).show()
             } else {
                 mapController?.animateTo(myLocation)
                 moveToCurrentLocationWasClicked = true
             }
         }
 
-        binding.map.addMapListener(
+        binding?.map?.addMapListener(
             delayedMapListener()
         )
     }
@@ -298,12 +298,12 @@ class LocationPickerController(args: Bundle) :
                             moveToCurrentLocationWasClicked = false
                         }
                         receivedChosenGeocodingResult -> {
-                            binding.shareLocation.isClickable = true
+                            binding?.shareLocation?.isClickable = true
                             setLocationDescription(isGpsLocation = false, isGeocodedResult = true)
                             receivedChosenGeocodingResult = false
                         }
                         else -> {
-                            binding.shareLocation.isClickable = true
+                            binding?.shareLocation?.isClickable = true
                             setLocationDescription(isGpsLocation = false, isGeocodedResult = false)
                         }
                     }
@@ -364,19 +364,19 @@ class LocationPickerController(args: Bundle) :
     private fun setLocationDescription(isGpsLocation: Boolean, isGeocodedResult: Boolean) {
         when {
             isGpsLocation -> {
-                binding.shareLocationDescription.text = context!!.getText(R.string.nc_share_current_location)
-                binding.placeName.visibility = View.GONE
-                binding.placeName.text = ""
+                binding?.shareLocationDescription?.text = context!!.getText(R.string.nc_share_current_location)
+                binding?.placeName?.visibility = View.GONE
+                binding?.placeName?.text = ""
             }
             isGeocodedResult -> {
-                binding.shareLocationDescription.text = context!!.getText(R.string.nc_share_this_location)
-                binding.placeName.visibility = View.VISIBLE
-                binding.placeName.text = geocodedName
+                binding?.shareLocationDescription?.text = context!!.getText(R.string.nc_share_this_location)
+                binding?.placeName?.visibility = View.VISIBLE
+                binding?.placeName?.text = geocodedName
             }
             else -> {
-                binding.shareLocationDescription.text = context!!.getText(R.string.nc_share_this_location)
-                binding.placeName.visibility = View.GONE
-                binding.placeName.text = ""
+                binding?.shareLocationDescription?.text = context!!.getText(R.string.nc_share_this_location)
+                binding?.placeName?.visibility = View.GONE
+                binding?.placeName?.text = ""
             }
         }
     }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/controllers/LockedController.kt

@@ -47,7 +47,7 @@ import java.util.concurrent.Executors
 
 @AutoInjector(NextcloudTalkApplication::class)
 class LockedController : BaseController(R.layout.controller_locked) {
-    private val binding: ControllerLockedBinding by viewBinding(ControllerLockedBinding::bind)
+    private val binding: ControllerLockedBinding? by viewBinding(ControllerLockedBinding::bind)
 
     override val appBarLayoutType: AppBarLayoutType
         get() = AppBarLayoutType.EMPTY
@@ -60,7 +60,7 @@ class LockedController : BaseController(R.layout.controller_locked) {
     override fun onViewBound(view: View) {
         super.onViewBound(view)
         sharedApplication!!.componentApplication.inject(this)
-        binding.unlockContainer.setOnClickListener {
+        binding?.unlockContainer?.setOnClickListener {
             unlock()
         }
     }

+ 40 - 38
app/src/main/java/com/nextcloud/talk/controllers/ProfileController.kt

@@ -96,7 +96,7 @@ import javax.inject.Inject
 @AutoInjector(NextcloudTalkApplication::class)
 @Suppress("Detekt.TooManyFunctions")
 class ProfileController : BaseController(R.layout.controller_profile) {
-    private val binding: ControllerProfileBinding by viewBinding(ControllerProfileBinding::bind)
+    private val binding: ControllerProfileBinding? by viewBinding(ControllerProfileBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -146,11 +146,11 @@ class ProfileController : BaseController(R.layout.controller_profile) {
             edit = !edit
             if (edit) {
                 item.setTitle(R.string.save)
-                binding.emptyList.root.visibility = View.GONE
-                binding.userinfoList.visibility = View.VISIBLE
+                binding?.emptyList?.root?.visibility = View.GONE
+                binding?.userinfoList?.visibility = View.VISIBLE
                 if (CapabilitiesUtilNew.isAvatarEndpointAvailable(currentUser!!)) {
                     // TODO later avatar can also be checked via user fields, for now it is in Talk capability
-                    binding.avatarButtons.visibility = View.VISIBLE
+                    binding?.avatarButtons?.visibility = View.VISIBLE
                 }
                 ncApi.getEditableUserProfileFields(
                     ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
@@ -179,10 +179,10 @@ class ProfileController : BaseController(R.layout.controller_profile) {
                     })
             } else {
                 item.setTitle(R.string.edit)
-                binding.avatarButtons.visibility = View.INVISIBLE
+                binding?.avatarButtons?.visibility = View.INVISIBLE
                 if (adapter!!.filteredDisplayList.isEmpty()) {
-                    binding.emptyList.root.visibility = View.VISIBLE
-                    binding.userinfoList.visibility = View.GONE
+                    binding?.emptyList?.root?.visibility = View.VISIBLE
+                    binding?.userinfoList?.visibility = View.GONE
                 }
             }
             adapter!!.notifyDataSetChanged()
@@ -194,14 +194,14 @@ class ProfileController : BaseController(R.layout.controller_profile) {
     override fun onAttach(view: View) {
         super.onAttach(view)
         adapter = UserInfoAdapter(null, viewThemeUtils, this)
-        binding.userinfoList.adapter = adapter
-        binding.userinfoList.setItemViewCacheSize(DEFAULT_CACHE_SIZE)
+        binding?.userinfoList?.adapter = adapter
+        binding?.userinfoList?.setItemViewCacheSize(DEFAULT_CACHE_SIZE)
         currentUser = userManager.currentUser.blockingGet()
         val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
-        binding.avatarUpload.setOnClickListener { sendSelectLocalFileIntent() }
-        binding.avatarChoose.setOnClickListener { showBrowserScreen() }
-        binding.avatarCamera.setOnClickListener { checkPermissionAndTakePicture() }
-        binding.avatarDelete.setOnClickListener {
+        binding?.avatarUpload?.setOnClickListener { sendSelectLocalFileIntent() }
+        binding?.avatarChoose?.setOnClickListener { showBrowserScreen() }
+        binding?.avatarCamera?.setOnClickListener { checkPermissionAndTakePicture() }
+        binding?.avatarDelete?.setOnClickListener {
             ncApi.deleteAvatar(
                 credentials,
                 ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl)
@@ -216,7 +216,7 @@ class ProfileController : BaseController(R.layout.controller_profile) {
                     override fun onNext(genericOverall: GenericOverall) {
                         DisplayUtils.loadAvatarImage(
                             currentUser,
-                            binding.avatarImage,
+                            binding?.avatarImage,
                             true
                         )
                     }
@@ -230,7 +230,7 @@ class ProfileController : BaseController(R.layout.controller_profile) {
                     }
                 })
         }
-        ViewCompat.setTransitionName(binding.avatarImage, "userAvatar.transitionTag")
+        binding?.avatarImage?.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
         ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl))
             .retry(DEFAULT_RETRIES)
             .subscribeOn(Schedulers.io())
@@ -262,10 +262,12 @@ class ProfileController : BaseController(R.layout.controller_profile) {
     }
 
     private fun colorIcons() {
-        viewThemeUtils.material.themeFAB(binding.avatarChoose)
-        viewThemeUtils.material.themeFAB(binding.avatarCamera)
-        viewThemeUtils.material.themeFAB(binding.avatarUpload)
-        viewThemeUtils.material.themeFAB(binding.avatarDelete)
+        binding?.let {
+            viewThemeUtils.material.themeFAB(it.avatarChoose)
+            viewThemeUtils.material.themeFAB(it.avatarCamera)
+            viewThemeUtils.material.themeFAB(it.avatarUpload)
+            viewThemeUtils.material.themeFAB(it.avatarDelete)
+        }
     }
 
     private fun isAllEmpty(items: Array<String?>): Boolean {
@@ -283,13 +285,13 @@ class ProfileController : BaseController(R.layout.controller_profile) {
             return
         }
         if (currentUser!!.baseUrl != null) {
-            binding.userinfoBaseurl.text = Uri.parse(currentUser!!.baseUrl).host
+            binding?.userinfoBaseurl?.text = Uri.parse(currentUser!!.baseUrl).host
         }
-        DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false)
+        DisplayUtils.loadAvatarImage(currentUser, binding?.avatarImage, false)
         if (!TextUtils.isEmpty(userInfo?.displayName)) {
-            binding.userinfoFullName.text = userInfo?.displayName
+            binding?.userinfoFullName?.text = userInfo?.displayName
         }
-        binding.loadingContent.visibility = View.VISIBLE
+        binding?.loadingContent?.visibility = View.VISIBLE
         adapter!!.setData(createUserInfoDetails(userInfo))
         if (isAllEmpty(
                 arrayOf(
@@ -302,18 +304,18 @@ class ProfileController : BaseController(R.layout.controller_profile) {
                     )
             )
         ) {
-            binding.userinfoList.visibility = View.GONE
-            binding.loadingContent.visibility = View.GONE
-            binding.emptyList.root.visibility = View.VISIBLE
+            binding?.userinfoList?.visibility = View.GONE
+            binding?.loadingContent?.visibility = View.GONE
+            binding?.emptyList?.root?.visibility = View.VISIBLE
             setErrorMessageForMultiList(
                 activity!!.getString(R.string.userinfo_no_info_headline),
                 activity!!.getString(R.string.userinfo_no_info_text),
                 R.drawable.ic_user
             )
         } else {
-            binding.emptyList.root.visibility = View.GONE
-            binding.loadingContent.visibility = View.GONE
-            binding.userinfoList.visibility = View.VISIBLE
+            binding?.emptyList?.root?.visibility = View.GONE
+            binding?.loadingContent?.visibility = View.GONE
+            binding?.userinfoList?.visibility = View.VISIBLE
         }
 
         // show edit button
@@ -354,13 +356,13 @@ class ProfileController : BaseController(R.layout.controller_profile) {
         }
 
         try {
-            binding.emptyList.emptyListViewHeadline.text = headline
-            binding.emptyList.emptyListViewText.text = message
-            binding.emptyList.emptyListIcon.setImageResource(errorResource)
-            binding.emptyList.emptyListIcon.visibility = View.VISIBLE
-            binding.emptyList.emptyListViewText.visibility = View.VISIBLE
-            binding.userinfoList.visibility = View.GONE
-            binding.loadingContent.visibility = View.GONE
+            binding?.emptyList?.emptyListViewHeadline?.text = headline
+            binding?.emptyList?.emptyListViewText?.text = message
+            binding?.emptyList?.emptyListIcon?.setImageResource(errorResource)
+            binding?.emptyList?.emptyListIcon?.visibility = View.VISIBLE
+            binding?.emptyList?.emptyListViewText?.visibility = View.VISIBLE
+            binding?.userinfoList?.visibility = View.GONE
+            binding?.loadingContent?.visibility = View.GONE
         } catch (npe: NullPointerException) {
             // view binding can be null
             // since this is called asynchronously and UI might have been destroyed in the meantime
@@ -453,7 +455,7 @@ class ProfileController : BaseController(R.layout.controller_profile) {
                         override fun onNext(userProfileOverall: GenericOverall) {
                             Log.d(TAG, "Successfully saved: " + item.text + " as " + item.field)
                             if (item.field == Field.DISPLAYNAME) {
-                                binding.userinfoFullName.text = item.text
+                                binding?.userinfoFullName?.text = item.text
                             }
                         }
 
@@ -646,7 +648,7 @@ class ProfileController : BaseController(R.layout.controller_profile) {
                 }
 
                 override fun onNext(genericOverall: GenericOverall) {
-                    DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, true)
+                    DisplayUtils.loadAvatarImage(currentUser, binding?.avatarImage, true)
                 }
 
                 override fun onError(e: Throwable) {

+ 5 - 5
app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt

@@ -57,7 +57,7 @@ class RingtoneSelectionController(args: Bundle) :
         args
     ),
     FlexibleAdapter.OnItemClickListener {
-    private val binding: ControllerGenericRvBinding by viewBinding(ControllerGenericRvBinding::bind)
+    private val binding: ControllerGenericRvBinding? by viewBinding(ControllerGenericRvBinding::bind)
 
     private var adapter: FlexibleAdapter<*>? = null
     private var adapterDataObserver: RecyclerView.AdapterDataObserver? = null
@@ -89,9 +89,9 @@ class RingtoneSelectionController(args: Bundle) :
 
     private fun prepareViews() {
         val layoutManager: RecyclerView.LayoutManager = SmoothScrollLinearLayoutManager(activity)
-        binding.recyclerView.layoutManager = layoutManager
-        binding.recyclerView.setHasFixedSize(true)
-        binding.recyclerView.adapter = adapter
+        binding?.recyclerView?.layoutManager = layoutManager
+        binding?.recyclerView?.setHasFixedSize(true)
+        binding?.recyclerView?.adapter = adapter
         adapterDataObserver = object : RecyclerView.AdapterDataObserver() {
             override fun onChanged() {
                 super.onChanged()
@@ -99,7 +99,7 @@ class RingtoneSelectionController(args: Bundle) :
             }
         }
         adapter!!.registerAdapterDataObserver(adapterDataObserver!!)
-        binding.swipeRefreshLayout.isEnabled = false
+        binding?.swipeRefreshLayout?.isEnabled = false
     }
 
     @SuppressLint("LongLogTag")

+ 38 - 37
app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt

@@ -63,7 +63,7 @@ import javax.inject.Inject
 class ServerSelectionController :
     BaseController(R.layout.controller_server_selection) {
 
-    private val binding: ControllerServerSelectionBinding by viewBinding(ControllerServerSelectionBinding::bind)
+    private val binding: ControllerServerSelectionBinding? by viewBinding(ControllerServerSelectionBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -99,14 +99,14 @@ class ServerSelectionController :
 
         actionBar?.hide()
 
-        binding.hostUrlInputHelperText.text = String.format(
+        binding?.hostUrlInputHelperText?.text = String.format(
             resources!!.getString(R.string.nc_server_helper_text),
             resources!!.getString(R.string.nc_server_product_name)
         )
-        binding.serverEntryTextInputLayout.setEndIconOnClickListener { checkServerAndProceed() }
+        binding?.serverEntryTextInputLayout?.setEndIconOnClickListener { checkServerAndProceed() }
 
         if (resources!!.getBoolean(R.bool.hide_auth_cert)) {
-            binding.certTextView.visibility = View.GONE
+            binding?.certTextView?.visibility = View.GONE
         }
 
         val loggedInUsers = userManager.users.blockingGet()
@@ -117,21 +117,21 @@ class ServerSelectionController :
         } else if (isAbleToShowProviderLink() && loggedInUsers.isEmpty()) {
             showVisitProvidersInfo()
         } else {
-            binding.importOrChooseProviderText.visibility = View.INVISIBLE
+            binding?.importOrChooseProviderText?.visibility = View.INVISIBLE
         }
 
-        binding.serverEntryTextInputEditText.requestFocus()
+        binding?.serverEntryTextInputEditText?.requestFocus()
         if (!TextUtils.isEmpty(resources!!.getString(R.string.weblogin_url))) {
-            binding.serverEntryTextInputEditText.setText(resources!!.getString(R.string.weblogin_url))
+            binding?.serverEntryTextInputEditText?.setText(resources!!.getString(R.string.weblogin_url))
             checkServerAndProceed()
         }
-        binding.serverEntryTextInputEditText.setOnEditorActionListener { _: TextView?, i: Int, _: KeyEvent? ->
+        binding?.serverEntryTextInputEditText?.setOnEditorActionListener { _: TextView?, i: Int, _: KeyEvent? ->
             if (i == EditorInfo.IME_ACTION_DONE) {
                 checkServerAndProceed()
             }
             false
         }
-        binding.certTextView.setOnClickListener { onCertClick() }
+        binding?.certTextView?.setOnClickListener { onCertClick() }
     }
 
     private fun isAbleToShowProviderLink(): Boolean {
@@ -145,25 +145,26 @@ class ServerSelectionController :
             )
         ) {
             if (availableAccounts.size > 1) {
-                binding.importOrChooseProviderText.text = String.format(
+                binding?.importOrChooseProviderText?.text = String.format(
                     resources!!.getString(R.string.nc_server_import_accounts),
                     AccountUtils.getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from))
                 )
             } else {
-                binding.importOrChooseProviderText.text = String.format(
+                binding?.importOrChooseProviderText?.text = String.format(
                     resources!!.getString(R.string.nc_server_import_account),
                     AccountUtils.getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from))
                 )
             }
         } else {
             if (availableAccounts.size > 1) {
-                binding.importOrChooseProviderText.text =
+                binding?.importOrChooseProviderText?.text =
                     resources!!.getString(R.string.nc_server_import_accounts_plain)
             } else {
-                binding.importOrChooseProviderText.text = resources!!.getString(R.string.nc_server_import_account_plain)
+                binding?.importOrChooseProviderText?.text =
+                    resources!!.getString(R.string.nc_server_import_account_plain)
             }
         }
-        binding.importOrChooseProviderText.setOnClickListener {
+        binding?.importOrChooseProviderText?.setOnClickListener {
             val bundle = Bundle()
             bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true)
             router.pushController(
@@ -177,8 +178,8 @@ class ServerSelectionController :
     }
 
     private fun showVisitProvidersInfo() {
-        binding.importOrChooseProviderText.setText(R.string.nc_get_from_provider)
-        binding.importOrChooseProviderText.setOnClickListener {
+        binding?.importOrChooseProviderText?.setText(R.string.nc_get_from_provider)
+        binding?.importOrChooseProviderText?.setOnClickListener {
             val browserIntent = Intent(
                 Intent.ACTION_VIEW,
                 Uri.parse(
@@ -199,12 +200,12 @@ class ServerSelectionController :
     private fun checkServerAndProceed() {
         dispose()
         try {
-            var url: String = binding.serverEntryTextInputEditText.text.toString().trim { it <= ' ' }
-            binding.serverEntryTextInputEditText.isEnabled = false
+            var url: String = binding?.serverEntryTextInputEditText?.text.toString().trim { it <= ' ' }
+            binding?.serverEntryTextInputEditText?.isEnabled = false
             showserverEntryProgressBar()
-            if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) {
-                binding.importOrChooseProviderText.visibility = View.INVISIBLE
-                binding.certTextView.visibility = View.INVISIBLE
+            if (binding?.importOrChooseProviderText?.visibility != View.INVISIBLE) {
+                binding?.importOrChooseProviderText?.visibility = View.INVISIBLE
+                binding?.certTextView?.visibility = View.INVISIBLE
             }
             if (url.endsWith("/")) {
                 url = url.substring(0, url.length - 1)
@@ -282,19 +283,19 @@ class ServerSelectionController :
                         hideserverEntryProgressBar()
                     }
 
-                    binding.serverEntryTextInputEditText.isEnabled = true
+                    binding?.serverEntryTextInputEditText?.isEnabled = true
 
-                    if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) {
-                        binding.importOrChooseProviderText.visibility = View.VISIBLE
-                        binding.certTextView.visibility = View.VISIBLE
+                    if (binding?.importOrChooseProviderText?.visibility != View.INVISIBLE) {
+                        binding?.importOrChooseProviderText?.visibility = View.VISIBLE
+                        binding?.certTextView?.visibility = View.VISIBLE
                     }
                     dispose()
                 }
             }) {
                 hideserverEntryProgressBar()
-                if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) {
-                    binding.importOrChooseProviderText.visibility = View.VISIBLE
-                    binding.certTextView.visibility = View.VISIBLE
+                if (binding?.importOrChooseProviderText?.visibility != View.INVISIBLE) {
+                    binding?.importOrChooseProviderText?.visibility = View.VISIBLE
+                    binding?.certTextView?.visibility = View.VISIBLE
                 }
                 dispose()
             }
@@ -305,19 +306,19 @@ class ServerSelectionController :
     }
 
     private fun setErrorText(text: String) {
-        binding.errorText.text = text
-        binding.errorText.visibility = View.VISIBLE
-        binding.serverEntryProgressBar.visibility = View.GONE
+        binding?.errorText?.text = text
+        binding?.errorText?.visibility = View.VISIBLE
+        binding?.serverEntryProgressBar?.visibility = View.GONE
     }
 
     private fun showserverEntryProgressBar() {
-        binding.errorText.visibility = View.GONE
-        binding.serverEntryProgressBar.visibility = View.VISIBLE
+        binding?.errorText?.visibility = View.GONE
+        binding?.serverEntryProgressBar?.visibility = View.VISIBLE
     }
 
     private fun hideserverEntryProgressBar() {
-        binding.errorText.visibility = View.GONE
-        binding.serverEntryProgressBar.visibility = View.INVISIBLE
+        binding?.errorText?.visibility = View.GONE
+        binding?.serverEntryProgressBar?.visibility = View.INVISIBLE
     }
 
     override fun onAttach(view: View) {
@@ -358,9 +359,9 @@ class ServerSelectionController :
             activity!!.runOnUiThread {
                 try {
                     if (!TextUtils.isEmpty(appPreferences!!.temporaryClientCertAlias)) {
-                        binding.certTextView.setText(R.string.nc_change_cert_auth)
+                        binding?.certTextView?.setText(R.string.nc_change_cert_auth)
                     } else {
-                        binding.certTextView.setText(R.string.nc_configure_cert_auth)
+                        binding?.certTextView?.setText(R.string.nc_configure_cert_auth)
                     }
                     hideserverEntryProgressBar()
                 } catch (npe: java.lang.NullPointerException) {

+ 151 - 141
app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt

@@ -106,7 +106,7 @@ import javax.inject.Inject
 
 @AutoInjector(NextcloudTalkApplication::class)
 class SettingsController : BaseController(R.layout.controller_settings) {
-    private val binding: ControllerSettingsBinding by viewBinding(ControllerSettingsBinding::bind)
+    private val binding: ControllerSettingsBinding? by viewBinding(ControllerSettingsBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -144,7 +144,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
         setHasOptionsMenu(true)
         sharedApplication!!.componentApplication.inject(this)
 
-        ViewCompat.setTransitionName((binding.avatarImage), "userAvatar.transitionTag")
+        binding?.avatarImage?.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
 
         getCurrentUser()
 
@@ -154,10 +154,10 @@ class SettingsController : BaseController(R.layout.controller_settings) {
         setupLicenceSetting()
 
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
-            binding.settingsIncognitoKeyboard.visibility = View.GONE
+            binding?.settingsIncognitoKeyboard?.visibility = View.GONE
         }
 
-        binding.settingsScreenLock.setSummary(
+        binding?.settingsScreenLock?.setSummary(
             String.format(
                 Locale.getDefault(),
                 resources!!.getString(R.string.nc_settings_screen_lock_desc),
@@ -167,7 +167,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
         setupPrivacyUrl()
         setupSourceCodeUrl()
-        binding.settingsVersion.setSummary("v" + BuildConfig.VERSION_NAME)
+        binding?.settingsVersion?.setSummary("v" + BuildConfig.VERSION_NAME)
 
         setupSoundSettings()
 
@@ -178,15 +178,15 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
     private fun setupPhoneBookIntegration() {
         if (CapabilitiesUtilNew.isPhoneBookIntegrationAvailable(currentUser!!)) {
-            binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
+            binding?.settingsPhoneBookIntegration?.visibility = View.VISIBLE
         } else {
-            binding.settingsPhoneBookIntegration.visibility = View.GONE
+            binding?.settingsPhoneBookIntegration?.visibility = View.GONE
         }
     }
 
     private fun setupSoundSettings() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            binding.settingsCallSound.setOnClickListener {
+            binding?.settingsCallSound?.setOnClickListener {
                 val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
                 intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
                 intent.putExtra(
@@ -196,7 +196,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
                 startActivity(intent)
             }
-            binding.settingsMessageSound.setOnClickListener {
+            binding?.settingsMessageSound?.setOnClickListener {
                 val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
                 intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
                 intent.putExtra(
@@ -206,7 +206,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                 startActivity(intent)
             }
         } else {
-            binding.settingsCallSound.setOnClickListener {
+            binding?.settingsCallSound?.setOnClickListener {
                 val bundle = Bundle()
                 bundle.putBoolean(KEY_ARE_CALL_SOUNDS, true)
 
@@ -216,7 +216,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                         .popChangeHandler(HorizontalChangeHandler())
                 )
             }
-            binding.settingsMessageSound.setOnClickListener {
+            binding?.settingsMessageSound?.setOnClickListener {
                 val bundle = Bundle()
                 bundle.putBoolean(KEY_ARE_CALL_SOUNDS, false)
 
@@ -231,7 +231,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
     private fun setupSourceCodeUrl() {
         if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_source_code_url))) {
-            binding.settingsSourceCode.addPreferenceClickListener {
+            binding?.settingsSourceCode?.addPreferenceClickListener {
                 startActivity(
                     Intent(
                         Intent.ACTION_VIEW,
@@ -240,13 +240,13 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                 )
             }
         } else {
-            binding.settingsSourceCode.visibility = View.GONE
+            binding?.settingsSourceCode?.visibility = View.GONE
         }
     }
 
     private fun setupPrivacyUrl() {
         if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_privacy_url))) {
-            binding.settingsPrivacy.addPreferenceClickListener {
+            binding?.settingsPrivacy?.addPreferenceClickListener {
                 startActivity(
                     Intent(
                         Intent.ACTION_VIEW,
@@ -255,13 +255,13 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                 )
             }
         } else {
-            binding.settingsPrivacy.visibility = View.GONE
+            binding?.settingsPrivacy?.visibility = View.GONE
         }
     }
 
     private fun setupLicenceSetting() {
         if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_gpl3_url))) {
-            binding.settingsLicence.addPreferenceClickListener {
+            binding?.settingsLicence?.addPreferenceClickListener {
                 startActivity(
                     Intent(
                         Intent.ACTION_VIEW,
@@ -270,15 +270,15 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                 )
             }
         } else {
-            binding.settingsLicence.visibility = View.GONE
+            binding?.settingsLicence?.visibility = View.GONE
         }
     }
 
     private fun setupSettingsScreen() {
         val listWithIntFields: MutableList<String> = ArrayList()
         listWithIntFields.add("proxy_port")
-        binding.settingsScreen.setUserInputModule(MagicUserInputModule(activity, listWithIntFields))
-        binding.settingsScreen.setVisibilityController(
+        binding?.settingsScreen?.setUserInputModule(MagicUserInputModule(activity, listWithIntFields))
+        binding?.settingsScreen?.setVisibilityController(
             R.id.settings_proxy_use_credentials,
             Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit),
             true
@@ -297,7 +297,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
             Log.e(TAG, "Failed to create uri")
         }
 
-        binding.settingsClientCert.addPreferenceClickListener {
+        binding?.settingsClientCert?.addPreferenceClickListener {
             KeyChain.choosePrivateKeyAlias(
                 activity!!,
                 { alias: String? ->
@@ -305,9 +305,9 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
                     activity!!.runOnUiThread {
                         if (finalAlias != null) {
-                            binding.settingsClientCert.setTitle(R.string.nc_client_cert_change)
+                            binding?.settingsClientCert?.setTitle(R.string.nc_client_cert_change)
                         } else {
-                            binding.settingsClientCert.setTitle(R.string.nc_client_cert_setup)
+                            binding?.settingsClientCert?.setTitle(R.string.nc_client_cert_setup)
                         }
                     }
 
@@ -370,27 +370,29 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
     private fun showRemoveAccountWarning() {
         if (activity != null) {
-            val materialAlertDialogBuilder = MaterialAlertDialogBuilder(binding.messageText.context)
-                .setTitle(R.string.nc_settings_remove_account)
-                .setMessage(R.string.nc_settings_remove_confirmation)
-                .setPositiveButton(R.string.nc_settings_remove) { _, _ ->
-                    removeCurrentAccount()
-                }
-                .setNegativeButton(R.string.nc_cancel) { _, _ ->
-                    // unused atm
-                }
+            binding?.messageText?.context?.let {
+                val materialAlertDialogBuilder = MaterialAlertDialogBuilder(it)
+                    .setTitle(R.string.nc_settings_remove_account)
+                    .setMessage(R.string.nc_settings_remove_confirmation)
+                    .setPositiveButton(R.string.nc_settings_remove) { _, _ ->
+                        removeCurrentAccount()
+                    }
+                    .setNegativeButton(R.string.nc_cancel) { _, _ ->
+                        // unused atm
+                    }
 
-            viewThemeUtils.dialog.colorMaterialAlertDialogBackground(
-                binding.messageText.context,
-                materialAlertDialogBuilder
-            )
+                viewThemeUtils.dialog.colorMaterialAlertDialogBackground(
+                    it,
+                    materialAlertDialogBuilder
+                )
 
-            val dialog = materialAlertDialogBuilder.show()
+                val dialog = materialAlertDialogBuilder.show()
 
-            viewThemeUtils.platform.colorTextButtons(
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-            )
+                viewThemeUtils.platform.colorTextButtons(
+                    dialog.getButton(AlertDialog.BUTTON_POSITIVE),
+                    dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+                )
+            }
         }
     }
 
@@ -428,38 +430,38 @@ class SettingsController : BaseController(R.layout.controller_settings) {
         actionBar?.show()
         dispose(null)
 
-        binding.settingsVersion.setOnClickListener {
+        binding?.settingsVersion?.setOnClickListener {
             sendLogs()
         }
 
         if (!TextUtils.isEmpty(currentUser!!.clientCertificate)) {
-            binding.settingsClientCert.setTitle(R.string.nc_client_cert_change)
+            binding?.settingsClientCert?.setTitle(R.string.nc_client_cert_change)
         } else {
-            binding.settingsClientCert.setTitle(R.string.nc_client_cert_setup)
+            binding?.settingsClientCert?.setTitle(R.string.nc_client_cert_setup)
         }
 
         setupCheckables()
         setupScreenLockSetting()
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            binding.settingsNotificationsCategory.setTitle(
+            binding?.settingsNotificationsCategory?.setTitle(
                 resources!!.getString(R.string.nc_settings_notification_sounds_post_oreo)
             )
         }
 
         val callRingtoneUri = getCallRingtoneUri(view.context, (appPreferences)!!)
-        binding.settingsCallSound.setSummary(getRingtoneName(view.context, callRingtoneUri))
+        binding?.settingsCallSound?.setSummary(getRingtoneName(view.context, callRingtoneUri))
         val messageRingtoneUri = getMessageRingtoneUri(view.context, (appPreferences)!!)
-        binding.settingsMessageSound.setSummary(getRingtoneName(view.context, messageRingtoneUri))
+        binding?.settingsMessageSound?.setSummary(getRingtoneName(view.context, messageRingtoneUri))
 
         setupProxyTypeSettings()
         setupProxyCredentialSettings()
 
         if (currentUser != null) {
-            binding.baseUrlText.text = Uri.parse(currentUser!!.baseUrl).host
+            binding?.baseUrlText?.text = Uri.parse(currentUser!!.baseUrl).host
             setupServerAgeWarning()
 
-            binding.settingsReauthorize.addPreferenceClickListener {
+            binding?.settingsReauthorize?.addPreferenceClickListener {
                 router.pushController(
                     RouterTransaction.with(WebViewLoginController(currentUser!!.baseUrl, true))
                         .pushChangeHandler(VerticalChangeHandler())
@@ -468,19 +470,19 @@ class SettingsController : BaseController(R.layout.controller_settings) {
             }
 
             if (currentUser!!.displayName != null) {
-                binding.displayNameText.text = currentUser!!.displayName
+                binding?.displayNameText?.text = currentUser!!.displayName
             }
-            DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false)
+            DisplayUtils.loadAvatarImage(currentUser, binding?.avatarImage, false)
 
             setupProfileQueryDisposable()
 
-            binding.settingsRemoveAccount.addPreferenceClickListener {
+            binding?.settingsRemoveAccount?.addPreferenceClickListener {
                 showRemoveAccountWarning()
             }
         }
         setupMessageView()
 
-        binding.avatarContainer.setOnClickListener {
+        binding?.avatarContainer?.setOnClickListener {
             router
                 .pushController(
                     RouterTransaction.with(ProfileController())
@@ -495,7 +497,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
     }
 
     private fun themeSwitchPreferences() {
-        binding.run {
+        binding?.run {
             listOf(
                 settingsScreenLock,
                 settingsScreenSecurity,
@@ -508,7 +510,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
     }
 
     private fun themeCategories() {
-        binding.run {
+        binding?.run {
             listOf(
                 settingsNotificationsCategory,
                 settingsAboutCategory,
@@ -539,54 +541,62 @@ class SettingsController : BaseController(R.layout.controller_settings) {
         if (ApplicationWideMessageHolder.getInstance().messageType != null) {
             when (ApplicationWideMessageHolder.getInstance().messageType) {
                 ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED -> {
-                    binding.messageText.setTextColor(
-                        viewThemeUtils.getScheme(binding.messageText.context).primary
-                    )
-                    binding.messageText.text = resources!!.getString(R.string.nc_settings_account_updated)
-                    binding.messageView.visibility = View.VISIBLE
+                    binding?.messageText?.let {
+                        it.setTextColor(
+                            viewThemeUtils.getScheme(it.context).primary
+                        )
+                        it.text = resources!!.getString(R.string.nc_settings_account_updated)
+                        binding?.messageView?.visibility = View.VISIBLE
+                    }
                 }
 
                 ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK -> {
-                    binding.messageText.setTextColor(resources!!.getColor(R.color.nc_darkRed))
-                    binding.messageText.text = resources!!.getString(R.string.nc_settings_wrong_account)
-                    binding.messageView.visibility = View.VISIBLE
-                    binding.messageText.setTextColor(
-                        viewThemeUtils.getScheme(binding.messageText.context).primary
-                    )
-                    binding.messageText.text = resources!!.getString(R.string.nc_Server_account_imported)
-                    binding.messageView.visibility = View.VISIBLE
+                    binding?.messageText?.let {
+                        it.setTextColor(resources!!.getColor(R.color.nc_darkRed))
+                        it.text = resources!!.getString(R.string.nc_settings_wrong_account)
+                        binding?.messageView?.visibility = View.VISIBLE
+                        it.setTextColor(
+                            viewThemeUtils.getScheme(it.context).primary
+                        )
+                        it.text = resources!!.getString(R.string.nc_Server_account_imported)
+                        binding?.messageView?.visibility = View.VISIBLE
+                    }
                 }
 
                 ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED -> {
-                    binding.messageText.setTextColor(
-                        viewThemeUtils.getScheme(binding.messageText.context).primary
-                    )
-                    binding.messageText.text = resources!!.getString(R.string.nc_Server_account_imported)
-                    binding.messageView.visibility = View.VISIBLE
+                    binding?.messageText?.let {
+                        it.setTextColor(
+                            viewThemeUtils.getScheme(it.context).primary
+                        )
+                        it.text = resources!!.getString(R.string.nc_Server_account_imported)
+                        binding?.messageView?.visibility = View.VISIBLE
+                    }
                 }
 
                 ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT -> {
-                    binding.messageText.setTextColor(resources!!.getColor(R.color.nc_darkRed))
-                    binding.messageText.text = resources!!.getString(R.string.nc_server_failed_to_import_account)
-                    binding.messageView.visibility = View.VISIBLE
+                    binding?.messageText?.let {
+                        it.setTextColor(resources!!.getColor(R.color.nc_darkRed))
+                        it.text = resources!!.getString(R.string.nc_server_failed_to_import_account)
+                        binding?.messageView?.visibility = View.VISIBLE
+                    }
                 }
 
-                else -> binding.messageView.visibility = View.GONE
+                else -> binding?.messageView?.visibility = View.GONE
             }
-            ApplicationWideMessageHolder.getInstance().setMessageType(null)
-            binding.messageView.animate()
-                .translationY(0f)
-                .alpha(0.0f)
-                .setDuration(DURATION)
-                .setStartDelay(START_DELAY)
-                .setListener(object : AnimatorListenerAdapter() {
+            ApplicationWideMessageHolder.getInstance().messageType = null
+            binding?.messageView?.animate()
+                ?.translationY(0f)
+                ?.alpha(0.0f)
+                ?.setDuration(DURATION)
+                ?.setStartDelay(START_DELAY)
+                ?.setListener(object : AnimatorListenerAdapter() {
                     override fun onAnimationEnd(animation: Animator) {
                         super.onAnimationEnd(animation)
-                        binding.messageView.visibility = View.GONE
+                        binding?.messageView?.visibility = View.GONE
                     }
                 })
         } else {
-            binding.messageView.visibility = View.GONE
+            binding?.messageView?.visibility = View.GONE
         }
     }
 
@@ -614,7 +624,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                     if ((!TextUtils.isEmpty(displayName) && !(displayName == currentUser!!.displayName))) {
                         currentUser!!.displayName = displayName
                         userManager.updateOrCreateUser(currentUser!!)
-                        binding.displayNameText.text = currentUser!!.displayName
+                        binding?.displayNameText?.text = currentUser!!.displayName
                     }
                 },
                 { dispose(profileQueryDisposable) },
@@ -625,74 +635,74 @@ class SettingsController : BaseController(R.layout.controller_settings) {
     private fun setupServerAgeWarning() {
         when {
             CapabilitiesUtilNew.isServerEOL(currentUser!!) -> {
-                binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context)!!, R.color.nc_darkRed))
-                binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol)
-                binding.serverAgeWarningIcon.setColorFilter(
-                    ContextCompat.getColor((context)!!, R.color.nc_darkRed),
+                binding?.serverAgeWarningText?.setTextColor(ContextCompat.getColor((context)!!, R.color.nc_darkRed))
+                binding?.serverAgeWarningText?.setText(R.string.nc_settings_server_eol)
+                binding?.serverAgeWarningIcon?.setColorFilter(
+                    ContextCompat.getColor((context), R.color.nc_darkRed),
                     PorterDuff.Mode.SRC_IN
                 )
             }
             CapabilitiesUtilNew.isServerAlmostEOL(currentUser!!) -> {
-                binding.serverAgeWarningText.setTextColor(
-                    ContextCompat.getColor((context)!!, R.color.nc_darkYellow)
+                binding?.serverAgeWarningText?.setTextColor(
+                    ContextCompat.getColor((context), R.color.nc_darkYellow)
                 )
-                binding.serverAgeWarningText.setText(R.string.nc_settings_server_almost_eol)
-                binding.serverAgeWarningIcon.setColorFilter(
-                    ContextCompat.getColor((context)!!, R.color.nc_darkYellow),
+                binding?.serverAgeWarningText?.setText(R.string.nc_settings_server_almost_eol)
+                binding?.serverAgeWarningIcon?.setColorFilter(
+                    ContextCompat.getColor((context), R.color.nc_darkYellow),
                     PorterDuff.Mode.SRC_IN
                 )
             }
             else -> {
-                binding.serverAgeWarningTextCard.visibility = View.GONE
+                binding?.serverAgeWarningTextCard?.visibility = View.GONE
             }
         }
     }
 
     private fun setupCheckables() {
-        (binding.settingsScreenSecurity.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+        (binding?.settingsScreenSecurity?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
             appPreferences.isScreenSecured
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            (binding.settingsIncognitoKeyboard.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+            (binding?.settingsIncognitoKeyboard?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
                 appPreferences.isKeyboardIncognito
         }
 
-        (binding.settingsIncognitoKeyboard.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+        (binding?.settingsIncognitoKeyboard?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
             appPreferences.isKeyboardIncognito
 
         if (CapabilitiesUtilNew.isReadStatusAvailable(userManager.currentUser.blockingGet())) {
-            (binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+            (binding?.settingsReadPrivacy?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
                 !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!)
         } else {
-            binding.settingsReadPrivacy.visibility = View.GONE
+            binding?.settingsReadPrivacy?.visibility = View.GONE
         }
 
-        (binding.settingsPhoneBookIntegration.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+        (binding?.settingsPhoneBookIntegration?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
             appPreferences.isPhoneBookIntegrationEnabled
     }
 
     private fun setupScreenLockSetting() {
         val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
         if (keyguardManager.isKeyguardSecure) {
-            binding.settingsScreenLock.isEnabled = true
-            binding.settingsScreenLockTimeout.isEnabled = true
-            (binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+            binding?.settingsScreenLock?.isEnabled = true
+            binding?.settingsScreenLockTimeout?.isEnabled = true
+            (binding?.settingsScreenLock?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
                 appPreferences.isScreenLocked
-            binding.settingsScreenLockTimeout.isEnabled = appPreferences.isScreenLocked
+            binding?.settingsScreenLockTimeout?.isEnabled = appPreferences.isScreenLocked
             if (appPreferences.isScreenLocked) {
-                binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
+                binding?.settingsScreenLockTimeout?.alpha = ENABLED_ALPHA
             } else {
-                binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
+                binding?.settingsScreenLockTimeout?.alpha = DISABLED_ALPHA
             }
-            binding.settingsScreenLock.alpha = ENABLED_ALPHA
+            binding?.settingsScreenLock?.alpha = ENABLED_ALPHA
         } else {
-            binding.settingsScreenLock.isEnabled = false
-            binding.settingsScreenLockTimeout.isEnabled = false
+            binding?.settingsScreenLock?.isEnabled = false
+            binding?.settingsScreenLockTimeout?.isEnabled = false
             appPreferences.removeScreenLock()
             appPreferences.removeScreenLockTimeout()
-            (binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked = false
-            binding.settingsScreenLock.alpha = DISABLED_ALPHA
-            binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
+            (binding?.settingsScreenLock?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked = false
+            binding?.settingsScreenLock?.alpha = DISABLED_ALPHA
+            binding?.settingsScreenLockTimeout?.alpha = DISABLED_ALPHA
         }
     }
 
@@ -710,40 +720,40 @@ class SettingsController : BaseController(R.layout.controller_settings) {
     }
 
     private fun hideProxySettings() {
-        appPreferences?.removeProxyHost()
-        appPreferences?.removeProxyPort()
-        appPreferences?.removeProxyCredentials()
-        appPreferences?.removeProxyUsername()
-        appPreferences?.removeProxyPassword()
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_host_edit).visibility = View.GONE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_port_edit).visibility = View.GONE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_use_credentials).visibility =
+        appPreferences.removeProxyHost()
+        appPreferences.removeProxyPort()
+        appPreferences.removeProxyCredentials()
+        appPreferences.removeProxyUsername()
+        appPreferences.removeProxyPassword()
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_host_edit)?.visibility = View.GONE
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_port_edit)?.visibility = View.GONE
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_use_credentials)?.visibility =
             View.GONE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_username_edit).visibility = View.GONE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_password_edit).visibility = View.GONE
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_username_edit)?.visibility = View.GONE
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_password_edit)?.visibility = View.GONE
     }
 
     private fun showProxySettings() {
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_host_edit).visibility =
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_host_edit)?.visibility =
             View.VISIBLE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_port_edit).visibility =
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_port_edit)?.visibility =
             View.VISIBLE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_use_credentials).visibility =
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_use_credentials)?.visibility =
             View.VISIBLE
     }
 
     private fun showProxyCredentials() {
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_username_edit).visibility =
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_username_edit)?.visibility =
             View.VISIBLE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_password_edit).visibility =
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_password_edit)?.visibility =
             View.VISIBLE
     }
 
     private fun hideProxyCredentials() {
-        appPreferences?.removeProxyUsername()
-        appPreferences?.removeProxyPassword()
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_username_edit).visibility = View.GONE
-        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_password_edit).visibility = View.GONE
+        appPreferences.removeProxyUsername()
+        appPreferences.removeProxyPassword()
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_username_edit)?.visibility = View.GONE
+        binding?.settingsScreen?.findViewById<View>(R.id.settings_proxy_password_edit)?.visibility = View.GONE
     }
 
     private fun dispose(disposable: Disposable?) {
@@ -784,7 +794,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
             checkForPhoneNumber()
         } else {
             appPreferences.setPhoneBookIntegration(false)
-            (binding.settingsPhoneBookIntegration.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+            (binding?.settingsPhoneBookIntegration?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
                 appPreferences.isPhoneBookIntegrationEnabled
             Toast.makeText(
                 context,
@@ -802,11 +812,11 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
     private inner class ScreenLockListener : OnPreferenceValueChangedListener<Boolean> {
         override fun onChanged(newValue: Boolean) {
-            binding.settingsScreenLockTimeout.isEnabled = newValue
+            binding?.settingsScreenLockTimeout?.isEnabled = newValue
             if (newValue) {
-                binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
+                binding?.settingsScreenLockTimeout?.alpha = ENABLED_ALPHA
             } else {
-                binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
+                binding?.settingsScreenLockTimeout?.alpha = DISABLED_ALPHA
             }
         }
     }
@@ -844,11 +854,11 @@ class SettingsController : BaseController(R.layout.controller_settings) {
                 } else {
                     when (newValue) {
                         "HTTP" ->
-                            binding.settingsProxyPortEdit.value = "3128"
+                            binding?.settingsProxyPortEdit?.value = "3128"
                         "DIRECT" ->
-                            binding.settingsProxyPortEdit.value = "8080"
+                            binding?.settingsProxyPortEdit?.value = "8080"
                         "SOCKS" ->
-                            binding.settingsProxyPortEdit.value = "1080"
+                            binding?.settingsProxyPortEdit?.value = "1080"
                         else -> {
                         }
                     }
@@ -1031,7 +1041,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
 
                     override fun onError(e: Throwable) {
                         appPreferences.setReadPrivacy(!newValue)
-                        (binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                        (binding?.settingsReadPrivacy?.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
                             !newValue
                     }
 

+ 6 - 6
app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt

@@ -63,7 +63,7 @@ class SwitchAccountController(args: Bundle? = null) :
         R.layout.controller_generic_rv,
         args
     ) {
-    private val binding: ControllerGenericRvBinding by viewBinding(ControllerGenericRvBinding::bind)
+    private val binding: ControllerGenericRvBinding? by viewBinding(ControllerGenericRvBinding::bind)
 
     @Inject
     lateinit var userManager: UserManager
@@ -118,7 +118,7 @@ class SwitchAccountController(args: Bundle? = null) :
 
     override fun onViewBound(view: View) {
         super.onViewBound(view)
-        binding.swipeRefreshLayout.isEnabled = false
+        binding?.swipeRefreshLayout?.isEnabled = false
 
         actionBar?.show()
 
@@ -167,10 +167,10 @@ class SwitchAccountController(args: Bundle? = null) :
 
     private fun prepareViews() {
         val layoutManager: LinearLayoutManager = SmoothScrollLinearLayoutManager(activity)
-        binding.recyclerView.layoutManager = layoutManager
-        binding.recyclerView.setHasFixedSize(true)
-        binding.recyclerView.adapter = adapter
-        binding.swipeRefreshLayout.isEnabled = false
+        binding?.recyclerView?.layoutManager = layoutManager
+        binding?.recyclerView?.setHasFixedSize(true)
+        binding?.recyclerView?.adapter = adapter
+        binding?.swipeRefreshLayout?.isEnabled = false
     }
 
     private fun reauthorizeFromImport(account: Account?) {

+ 21 - 21
app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt

@@ -82,7 +82,7 @@ class WebViewLoginController(args: Bundle? = null) : BaseController(
     R.layout.controller_web_view_login,
     args
 ) {
-    private val binding: ControllerWebViewLoginBinding by viewBinding(ControllerWebViewLoginBinding::bind)
+    private val binding: ControllerWebViewLoginBinding? by viewBinding(ControllerWebViewLoginBinding::bind)
 
     @Inject
     lateinit var userManager: UserManager
@@ -137,25 +137,25 @@ class WebViewLoginController(args: Bundle? = null) : BaseController(
         actionBar?.hide()
 
         assembledPrefix = resources!!.getString(R.string.nc_talk_login_scheme) + PROTOCOL_SUFFIX + "login/"
-        binding.webview.settings.allowFileAccess = false
-        binding.webview.settings.allowFileAccessFromFileURLs = false
-        binding.webview.settings.javaScriptEnabled = true
-        binding.webview.settings.javaScriptCanOpenWindowsAutomatically = false
-        binding.webview.settings.domStorageEnabled = true
-        binding.webview.settings.setUserAgentString(webLoginUserAgent)
-        binding.webview.settings.saveFormData = false
-        binding.webview.settings.savePassword = false
-        binding.webview.settings.setRenderPriority(WebSettings.RenderPriority.HIGH)
-        binding.webview.clearCache(true)
-        binding.webview.clearFormData()
-        binding.webview.clearHistory()
+        binding?.webview?.settings?.allowFileAccess = false
+        binding?.webview?.settings?.allowFileAccessFromFileURLs = false
+        binding?.webview?.settings?.javaScriptEnabled = true
+        binding?.webview?.settings?.javaScriptCanOpenWindowsAutomatically = false
+        binding?.webview?.settings?.domStorageEnabled = true
+        binding?.webview?.settings?.setUserAgentString(webLoginUserAgent)
+        binding?.webview?.settings?.saveFormData = false
+        binding?.webview?.settings?.savePassword = false
+        binding?.webview?.settings?.setRenderPriority(WebSettings.RenderPriority.HIGH)
+        binding?.webview?.clearCache(true)
+        binding?.webview?.clearFormData()
+        binding?.webview?.clearHistory()
         WebView.clearClientCertPreferences(null)
-        webViewFidoBridge = WebViewFidoBridge.createInstanceForWebView(activity as AppCompatActivity?, binding.webview)
+        webViewFidoBridge = WebViewFidoBridge.createInstanceForWebView(activity as AppCompatActivity?, binding?.webview)
         CookieSyncManager.createInstance(activity)
         android.webkit.CookieManager.getInstance().removeAllCookies(null)
         val headers: MutableMap<String, String> = HashMap()
         headers.put("OCS-APIRequest", "true")
-        binding.webview.webViewClient = object : WebViewClient() {
+        binding?.webview?.webViewClient = object : WebViewClient() {
             private var basePageLoaded = false
             override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
                 webViewFidoBridge?.delegateShouldInterceptRequest(view, request)
@@ -181,24 +181,24 @@ class WebViewLoginController(args: Bundle? = null) : BaseController(
                 try {
                     loginStep++
                     if (!basePageLoaded) {
-                        binding.progressBar.visibility = View.GONE
-                        binding.webview.visibility = View.VISIBLE
+                        binding?.progressBar?.visibility = View.GONE
+                        binding?.webview?.visibility = View.VISIBLE
 
                         basePageLoaded = true
                     }
                     if (!TextUtils.isEmpty(username)) {
                         if (loginStep == 1) {
-                            binding.webview.loadUrl(
+                            binding?.webview?.loadUrl(
                                 "javascript: {document.getElementsByClassName('login')[0].click(); };"
                             )
                         } else if (!automatedLoginAttempted) {
                             automatedLoginAttempted = true
                             if (TextUtils.isEmpty(password)) {
-                                binding.webview.loadUrl(
+                                binding?.webview?.loadUrl(
                                     "javascript:var justStore = document.getElementById('user').value = '$username';"
                                 )
                             } else {
-                                binding.webview.loadUrl(
+                                binding?.webview?.loadUrl(
                                     "javascript: {" +
                                         "document.getElementById('user').value = '" + username + "';" +
                                         "document.getElementById('password').value = '" + password + "';" +
@@ -308,7 +308,7 @@ class WebViewLoginController(args: Bundle? = null) : BaseController(
                 super.onReceivedError(view, errorCode, description, failingUrl)
             }
         }
-        binding.webview.loadUrl("$baseUrl/index.php/login/flow", headers)
+        binding?.webview?.loadUrl("$baseUrl/index.php/login/flow", headers)
     }
 
     private fun dispose() {

+ 71 - 69
app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.kt

@@ -62,7 +62,7 @@ class EntryMenuController(args: Bundle) :
         R.layout.controller_entry_menu,
         args
     ) {
-    private val binding: ControllerEntryMenuBinding by viewBinding(ControllerEntryMenuBinding::bind)
+    private val binding: ControllerEntryMenuBinding? by viewBinding(ControllerEntryMenuBinding::bind)
 
     @Inject
     lateinit var eventBus: EventBus
@@ -87,11 +87,11 @@ class EntryMenuController(args: Bundle) :
         if (ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG ==
             ApplicationWideMessageHolder.getInstance().messageType
         ) {
-            binding.textInputLayout.error = resources?.getString(R.string.nc_wrong_password)
-            ApplicationWideMessageHolder.getInstance().setMessageType(null)
-            if (binding.okButton.isEnabled) {
-                binding.okButton.isEnabled = false
-                binding.okButton.alpha = OPACITY_BUTTON_DISABLED
+            binding?.textInputLayout?.error = resources?.getString(R.string.nc_wrong_password)
+            ApplicationWideMessageHolder.getInstance().messageType = null
+            if (binding?.okButton?.isEnabled == true) {
+                binding?.okButton?.isEnabled = false
+                binding?.okButton?.alpha = OPACITY_BUTTON_DISABLED
             }
         }
     }
@@ -100,13 +100,13 @@ class EntryMenuController(args: Bundle) :
         super.onViewBound(view)
 
         if (conversation != null && operation === ConversationOperationEnum.OPS_CODE_RENAME_ROOM) {
-            binding.textEdit.setText(conversation!!.name)
+            binding?.textEdit?.setText(conversation!!.name)
         }
 
-        binding.textEdit.setOnEditorActionListener { v, actionId, event ->
+        binding?.textEdit?.setOnEditorActionListener { v, actionId, event ->
             @Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
-            if (actionId === EditorInfo.IME_ACTION_DONE && binding.okButton.isEnabled) {
-                binding.okButton.callOnClick()
+            if (actionId === EditorInfo.IME_ACTION_DONE && binding?.okButton?.isEnabled == true) {
+                binding?.okButton?.callOnClick()
                 return@setOnEditorActionListener true
             }
             false
@@ -118,57 +118,59 @@ class EntryMenuController(args: Bundle) :
         when (operation) {
             ConversationOperationEnum.OPS_CODE_INVITE_USERS, ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> {
                 labelText = resources!!.getString(R.string.nc_call_name)
-                binding.textEdit.inputType = InputType.TYPE_CLASS_TEXT
-                binding.smileyButton.visibility = View.VISIBLE
-                emojiPopup = EmojiPopup(
-                    rootView = view,
-                    editText = binding.textEdit,
-                    onEmojiPopupShownListener = {
-                        viewThemeUtils.platform.colorImageView(binding.smileyButton)
-                    },
-                    onEmojiPopupDismissListener = {
-                        binding.smileyButton.imageTintList = ColorStateList.valueOf(
-                            ResourcesCompat.getColor(resources!!, R.color.medium_emphasis_text, context.theme)
-                        )
-                    },
-                    onEmojiClickListener = {
-                        binding.textEdit.editableText.append(" ")
-                    }
-                )
+                binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT
+                binding?.smileyButton?.visibility = View.VISIBLE
+                emojiPopup = binding?.let {
+                    EmojiPopup(
+                        rootView = view,
+                        editText = it.textEdit,
+                        onEmojiPopupShownListener = {
+                            viewThemeUtils.platform.colorImageView(it.smileyButton)
+                        },
+                        onEmojiPopupDismissListener = {
+                            it.smileyButton.imageTintList = ColorStateList.valueOf(
+                                ResourcesCompat.getColor(resources!!, R.color.medium_emphasis_text, context.theme)
+                            )
+                        },
+                        onEmojiClickListener = {
+                            binding?.textEdit?.editableText?.append(" ")
+                        }
+                    )
+                }
             }
 
             ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> {
                 // 99 is joining a conversation via password
                 labelText = resources!!.getString(R.string.nc_password)
-                binding.textEdit.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
+                binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
             }
 
             ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM -> {
                 labelText = resources!!.getString(R.string.nc_conversation_link)
-                binding.textEdit.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
+                binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
             }
 
             else -> {
             }
         }
         if (PASSWORD_ENTRY_OPERATIONS.contains(operation)) {
-            binding.textInputLayout.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
+            binding?.textInputLayout?.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
         } else {
-            binding.textInputLayout.endIconMode = TextInputLayout.END_ICON_NONE
+            binding?.textInputLayout?.endIconMode = TextInputLayout.END_ICON_NONE
         }
 
-        viewThemeUtils.material.colorTextInputLayout(binding.textInputLayout)
-        viewThemeUtils.material.colorMaterialButtonText(binding.okButton)
+        binding?.textInputLayout?.let { viewThemeUtils.material.colorTextInputLayout(it) }
+        binding?.okButton?.let { viewThemeUtils.material.colorMaterialButtonText(it) }
 
-        binding.textInputLayout.hint = labelText
-        binding.textInputLayout.requestFocus()
+        binding?.textInputLayout?.hint = labelText
+        binding?.textInputLayout?.requestFocus()
 
-        binding.smileyButton.setOnClickListener { onSmileyClick() }
-        binding.okButton.setOnClickListener { onOkButtonClick() }
+        binding?.smileyButton?.setOnClickListener { onSmileyClick() }
+        binding?.okButton?.setOnClickListener { onOkButtonClick() }
     }
 
     private fun textEditAddChangedListener() {
-        binding.textEdit.addTextChangedListener(object : TextWatcher {
+        binding?.textEdit?.addTextChangedListener(object : TextWatcher {
             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                 // unused atm
             }
@@ -181,46 +183,46 @@ class EntryMenuController(args: Bundle) :
                 if (!TextUtils.isEmpty(s)) {
                     if (operation === ConversationOperationEnum.OPS_CODE_RENAME_ROOM) {
                         if (conversation!!.name == null || !conversation!!.name.equals(s.toString())) {
-                            if (!binding.okButton.isEnabled) {
-                                binding.okButton.isEnabled = true
-                                binding.okButton.alpha = OPACITY_ENABLED
+                            if (!binding?.okButton?.isEnabled!!) {
+                                binding?.okButton?.isEnabled = true
+                                binding?.okButton?.alpha = OPACITY_ENABLED
                             }
-                            binding.textInputLayout.isErrorEnabled = false
+                            binding?.textInputLayout?.isErrorEnabled = false
                         } else {
-                            if (binding.okButton.isEnabled) {
-                                binding.okButton.isEnabled = false
-                                binding.okButton.alpha = OPACITY_DISABLED
+                            if (binding?.okButton?.isEnabled == true) {
+                                binding?.okButton?.isEnabled = false
+                                binding?.okButton?.alpha = OPACITY_DISABLED
                             }
-                            binding.textInputLayout.error = resources?.getString(R.string.nc_call_name_is_same)
+                            binding?.textInputLayout?.error = resources?.getString(R.string.nc_call_name_is_same)
                         }
                     } else if (operation !== ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM) {
-                        if (!binding.okButton.isEnabled) {
-                            binding.okButton.isEnabled = true
-                            binding.okButton.alpha = OPACITY_ENABLED
+                        if (!binding?.okButton?.isEnabled!!) {
+                            binding?.okButton?.isEnabled = true
+                            binding?.okButton?.alpha = OPACITY_ENABLED
                         }
-                        binding.textInputLayout.isErrorEnabled = false
+                        binding?.textInputLayout?.isErrorEnabled = false
                     } else if (
-                        UriUtils.hasHttpProtocollPrefixed(binding.textEdit.text.toString()) &&
-                        binding.textEdit.text.toString().contains("/call/")
+                        UriUtils.hasHttpProtocollPrefixed(binding?.textEdit?.text.toString()) &&
+                        binding?.textEdit?.text.toString().contains("/call/")
                     ) {
-                        if (!binding.okButton.isEnabled) {
-                            binding.okButton.isEnabled = true
-                            binding.okButton.alpha = OPACITY_ENABLED
+                        if (!binding?.okButton?.isEnabled!!) {
+                            binding?.okButton?.isEnabled = true
+                            binding?.okButton?.alpha = OPACITY_ENABLED
                         }
-                        binding.textInputLayout.isErrorEnabled = false
+                        binding?.textInputLayout?.isErrorEnabled = false
                     } else {
-                        if (binding.okButton.isEnabled) {
-                            binding.okButton.isEnabled = false
-                            binding.okButton.alpha = OPACITY_DISABLED
+                        if (binding?.okButton?.isEnabled == true) {
+                            binding?.okButton?.isEnabled = false
+                            binding?.okButton?.alpha = OPACITY_DISABLED
                         }
-                        binding.textInputLayout.error = resources?.getString(R.string.nc_wrong_link)
+                        binding?.textInputLayout?.error = resources?.getString(R.string.nc_wrong_link)
                     }
                 } else {
-                    if (binding.okButton.isEnabled) {
-                        binding.okButton.isEnabled = false
-                        binding.okButton.alpha = OPACITY_DISABLED
+                    if (binding?.okButton?.isEnabled == true) {
+                        binding?.okButton?.isEnabled = false
+                        binding?.okButton?.alpha = OPACITY_DISABLED
                     }
-                    binding.textInputLayout.isErrorEnabled = false
+                    binding?.textInputLayout?.isErrorEnabled = false
                 }
             }
         })
@@ -238,7 +240,7 @@ class EntryMenuController(args: Bundle) :
             operation !== ConversationOperationEnum.OPS_CODE_INVITE_USERS
         ) {
             val bundle = Bundle()
-            conversation!!.name = binding.textEdit.text.toString()
+            conversation!!.name = binding?.textEdit?.text.toString()
             bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap<Any>(conversation))
             bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
             router.pushController(
@@ -249,14 +251,14 @@ class EntryMenuController(args: Bundle) :
         } else if (operation !== ConversationOperationEnum.OPS_CODE_INVITE_USERS) {
             val bundle = Bundle()
             bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
-            bundle.putString(BundleKeys.KEY_CALL_URL, binding.textEdit.text.toString())
+            bundle.putString(BundleKeys.KEY_CALL_URL, binding?.textEdit?.text.toString())
             router.pushController(
                 RouterTransaction.with(OperationsMenuController(bundle))
                     .pushChangeHandler(HorizontalChangeHandler())
                     .popChangeHandler(HorizontalChangeHandler())
             )
         } else if (operation === ConversationOperationEnum.OPS_CODE_INVITE_USERS) {
-            originalBundle.putString(BundleKeys.KEY_CONVERSATION_NAME, binding.textEdit.text.toString())
+            originalBundle.putString(BundleKeys.KEY_CONVERSATION_NAME, binding?.textEdit?.text.toString())
             router.pushController(
                 RouterTransaction.with(
                     OperationsMenuController(
@@ -273,12 +275,12 @@ class EntryMenuController(args: Bundle) :
         val bundle = Bundle()
         bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap<Any>(conversation))
         bundle.putString(BundleKeys.KEY_CALL_URL, callUrl)
-        bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, binding.textEdit.text.toString())
+        bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, binding?.textEdit?.text.toString())
         bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
         if (originalBundle.containsKey(BundleKeys.KEY_SERVER_CAPABILITIES)) {
             bundle.putParcelable(
                 BundleKeys.KEY_SERVER_CAPABILITIES,
-                originalBundle.getParcelable<Parcelable>(BundleKeys.KEY_SERVER_CAPABILITIES)
+                originalBundle.getParcelable(BundleKeys.KEY_SERVER_CAPABILITIES)
             )
         }
         router.pushController(

+ 17 - 17
app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt

@@ -83,7 +83,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
     R.layout.controller_operations_menu,
     args
 ) {
-    private val binding: ControllerOperationsMenuBinding by viewBinding(ControllerOperationsMenuBinding::bind)
+    private val binding: ControllerOperationsMenuBinding? by viewBinding(ControllerOperationsMenuBinding::bind)
 
     @Inject
     lateinit var ncApi: NcApi
@@ -117,7 +117,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
         sharedApplication!!.componentApplication.inject(this)
         currentUser = userManager.currentUser.blockingGet()
 
-        viewThemeUtils.platform.colorCircularProgressBar(binding.progressBar)
+        binding?.progressBar?.let { viewThemeUtils.platform.colorCircularProgressBar(it) }
 
         if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) {
             conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1)
@@ -476,10 +476,10 @@ class OperationsMenuController(args: Bundle) : BaseController(
     @Suppress("Detekt.TooGenericExceptionCaught")
     private fun showResultImage(everythingOK: Boolean, isGuestSupportError: Boolean) {
         try {
-            binding.progressBar.visibility = View.GONE
+            binding?.progressBar?.visibility = View.GONE
             if (resources != null) {
                 if (everythingOK) {
-                    binding.resultImageView.setImageDrawable(
+                    binding?.resultImageView?.setImageDrawable(
                         DisplayUtils.getTintedDrawable(
                             resources,
                             R.drawable.ic_check_circle_black_24dp,
@@ -487,7 +487,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
                         )
                     )
                 } else {
-                    binding.resultImageView.setImageDrawable(
+                    binding?.resultImageView?.setImageDrawable(
                         DisplayUtils.getTintedDrawable(
                             resources,
                             R.drawable.ic_cancel_black_24dp,
@@ -496,35 +496,35 @@ class OperationsMenuController(args: Bundle) : BaseController(
                     )
                 }
             }
-            binding.resultImageView.visibility = View.VISIBLE
+            binding?.resultImageView?.visibility = View.VISIBLE
             if (everythingOK) {
-                binding.resultTextView.setText(R.string.nc_all_ok_operation)
+                binding?.resultTextView?.setText(R.string.nc_all_ok_operation)
             } else {
-                binding.resultTextView.setTextColor(resources!!.getColor(R.color.nc_darkRed))
+                binding?.resultTextView?.setTextColor(resources!!.getColor(R.color.nc_darkRed))
                 if (!isGuestSupportError) {
-                    binding.resultTextView.setText(R.string.nc_failed_to_perform_operation)
+                    binding?.resultTextView?.setText(R.string.nc_failed_to_perform_operation)
                 } else {
-                    binding.resultTextView.setText(R.string.nc_failed_signaling_settings)
-                    binding.webButton.setOnClickListener {
+                    binding?.resultTextView?.setText(R.string.nc_failed_signaling_settings)
+                    binding?.webButton?.setOnClickListener {
                         val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(callUrl))
                         startActivity(browserIntent)
                     }
-                    binding.webButton.visibility = View.VISIBLE
+                    binding?.webButton?.visibility = View.VISIBLE
                 }
             }
-            binding.resultTextView.visibility = View.VISIBLE
+            binding?.resultTextView?.visibility = View.VISIBLE
             if (everythingOK) {
-                eventBus!!.post(ConversationsListFetchDataEvent())
+                eventBus.post(ConversationsListFetchDataEvent())
             } else {
-                binding.resultImageView.setImageDrawable(
+                binding?.resultImageView?.setImageDrawable(
                     DisplayUtils.getTintedDrawable(
                         resources,
                         R.drawable.ic_cancel_black_24dp,
                         R.color.nc_darkRed
                     )
                 )
-                binding.okButton.setOnClickListener { v: View? -> eventBus!!.post(ConversationsListFetchDataEvent()) }
-                binding.okButton.visibility = View.VISIBLE
+                binding?.okButton?.setOnClickListener { v: View? -> eventBus.post(ConversationsListFetchDataEvent()) }
+                binding?.okButton?.visibility = View.VISIBLE
             }
         } catch (npe: NullPointerException) {
             Log.i(TAG, "Controller already closed", npe)

+ 5 - 5
app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt

@@ -25,13 +25,13 @@ import com.bluelinelabs.conductor.Controller
 import kotlin.properties.ReadOnlyProperty
 import kotlin.reflect.KProperty
 
-fun <T : ViewBinding> Controller.viewBinding(bindingFactory: (View) -> T) =
+fun <T : ViewBinding> Controller.viewBinding(bindingFactory: (View) -> T?) =
     ControllerViewBindingDelegate(this, bindingFactory)
 
 class ControllerViewBindingDelegate<T : ViewBinding>(
     controller: Controller,
-    private val viewBinder: (View) -> T
-) : ReadOnlyProperty<Controller, T>, LifecycleObserver {
+    private val viewBinder: (View) -> T?
+) : ReadOnlyProperty<Controller, T?>, LifecycleObserver {
 
     private var binding: T? = null
 
@@ -43,7 +43,7 @@ class ControllerViewBindingDelegate<T : ViewBinding>(
         })
     }
 
-    override fun getValue(thisRef: Controller, property: KProperty<*>): T {
-        return binding ?: viewBinder(thisRef.view!!).also { binding = it }
+    override fun getValue(thisRef: Controller, property: KProperty <*>): T? {
+        return binding ?: thisRef.view?.let { viewBinder(it).also { binding = it } }
     }
 }