Merge pull request #2210 from nextcloud/chore/noid/with-nullable-binding

Add method in NewBaseController to handle NPE catches for viewbinding
This commit is contained in:
Andy Scherzinger 2022-07-12 16:31:55 +02:00 committed by GitHub
commit ea849281e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 35 deletions

View File

@ -384,7 +384,6 @@ class ContactsController(args: Bundle) :
}
}
@Suppress("Detekt.TooGenericExceptionCaught")
private fun fetchData() {
dispose(null)
alreadyFetching = true
@ -440,33 +439,21 @@ class ContactsController(args: Bundle) :
adapter?.filterItems()
}
try {
withNullableControllerViewBinding {
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
} 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")
}
}
override fun onError(e: Throwable) {
try {
withNullableControllerViewBinding {
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
} 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")
}
dispose(contactsQueryDisposable)
}
override fun onComplete() {
try {
withNullableControllerViewBinding {
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
} 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")
}
dispose(contactsQueryDisposable)
alreadyFetching = false
@ -692,7 +679,6 @@ class ContactsController(args: Bundle) :
dispose(null)
}
@Suppress("Detekt.TooGenericExceptionCaught")
override fun onQueryTextChange(newText: String): Boolean {
if (newText != "" && adapter?.hasNewFilter(newText) == true) {
adapter?.setFilter(newText)
@ -702,12 +688,8 @@ class ContactsController(args: Bundle) :
adapter?.updateDataSet(contactItems as List<Nothing>?)
}
try {
withNullableControllerViewBinding {
binding.controllerGenericRv.swipeRefreshLayout.isEnabled = !adapter!!.hasFilter()
} 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")
}
return true
@ -933,9 +915,8 @@ class ContactsController(args: Bundle) :
}
}
@Suppress("Detekt.TooGenericExceptionCaught")
private fun toggleConversationPrivacyLayout(showInitialLayout: Boolean) {
try {
withNullableControllerViewBinding {
if (showInitialLayout) {
binding.conversationPrivacyToggle.initialRelativeLayout.visibility = View.VISIBLE
binding.conversationPrivacyToggle.secondaryRelativeLayout.visibility = View.GONE
@ -943,26 +924,17 @@ class ContactsController(args: Bundle) :
binding.conversationPrivacyToggle.initialRelativeLayout.visibility = View.GONE
binding.conversationPrivacyToggle.secondaryRelativeLayout.visibility = View.VISIBLE
}
} 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")
}
}
@Suppress("Detekt.TooGenericExceptionCaught")
private fun toggleConversationViaLinkVisibility(isPublicCall: Boolean) {
try {
withNullableControllerViewBinding {
if (isPublicCall) {
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.GONE
updateGroupParticipantSelection()
} else {
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.VISIBLE
}
} 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")
}
}

View File

@ -54,10 +54,10 @@ import com.nextcloud.talk.controllers.ServerSelectionController
import com.nextcloud.talk.controllers.SwitchAccountController
import com.nextcloud.talk.controllers.WebViewLoginController
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
import com.nextcloud.talk.controllers.util.ControllerViewBindingDelegate
import com.nextcloud.talk.databinding.ActivityMainBinding
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import java.util.ArrayList
import javax.inject.Inject
import kotlin.jvm.internal.Intrinsics
@ -296,6 +296,27 @@ abstract class NewBaseController(@LayoutRes var layoutRes: Int, args: Bundle? =
}
}
/**
* Mainly intended to be used in async listeners that may be called after the controller has been destroyed.
*
* If you need to use this function to patch a NPE crash, something is wrong in the way that the async calls are
* handled, they should have been cancelled when the controller UI was destroyed (if their only purpose was
* updating UI).
*/
@Suppress("Detekt.TooGenericExceptionCaught")
inline fun withNullableControllerViewBinding(block: () -> Unit) {
try {
block()
} catch (e: NullPointerException) {
// Handle only the exceptions we know about, let everything else pass through
if (e.stackTrace.firstOrNull()?.className == ControllerViewBindingDelegate::class.qualifiedName) {
Log.w("ControllerViewBinding", "Trying to update UI on a null ViewBinding.", e)
} else {
throw e
}
}
}
open val appBarLayoutType: AppBarLayoutType
get() = AppBarLayoutType.TOOLBAR
val searchHint: String