Merge pull request #1479 from vector-im/feature/memory_leaks

Feature/memory leaks
This commit is contained in:
Benoit Marty 2020-06-15 14:47:16 +02:00 committed by GitHub
commit ade1f1b911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 221 additions and 42 deletions

View File

@ -396,6 +396,9 @@ dependencies {
// Plant Timber tree for test // Plant Timber tree for test
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
// Activate when you want to check for leaks, from time to time.
//debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
androidTestImplementation 'androidx.test:core:1.2.0' androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0'

View File

@ -25,12 +25,21 @@ import java.util.concurrent.atomic.AtomicReference
private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE" private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE"
class LayoutManagerStateRestorer(private val layoutManager: RecyclerView.LayoutManager) : Restorable, DefaultListUpdateCallback { class LayoutManagerStateRestorer(layoutManager: RecyclerView.LayoutManager) : Restorable, DefaultListUpdateCallback {
private var layoutManager: RecyclerView.LayoutManager? = null
private var layoutManagerState = AtomicReference<Parcelable?>() private var layoutManagerState = AtomicReference<Parcelable?>()
init {
this.layoutManager = layoutManager
}
fun clear() {
layoutManager = null
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
val layoutManagerState = layoutManager.onSaveInstanceState() val layoutManagerState = layoutManager?.onSaveInstanceState()
outState.putParcelable(LAYOUT_MANAGER_STATE, layoutManagerState) outState.putParcelable(LAYOUT_MANAGER_STATE, layoutManagerState)
} }
@ -41,7 +50,7 @@ class LayoutManagerStateRestorer(private val layoutManager: RecyclerView.LayoutM
override fun onInserted(position: Int, count: Int) { override fun onInserted(position: Int, count: Int) {
layoutManagerState.getAndSet(null)?.also { layoutManagerState.getAndSet(null)?.also {
layoutManager.onRestoreInstanceState(it) layoutManager?.onRestoreInstanceState(it)
} }
} }
} }

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.autocomplete
import android.content.Context
import android.database.DataSetObserver
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.AutocompletePresenter
abstract class RecyclerViewPresenter<T>(context: Context?) : AutocompletePresenter<T>(context) {
private var recyclerView: RecyclerView? = null
private var clicks: ClickProvider<T>? = null
private var observer: RecyclerView.AdapterDataObserver? = null
override fun registerClickProvider(provider: ClickProvider<T>) {
clicks = provider
}
override fun registerDataSetObserver(observer: DataSetObserver) {
this.observer = Observer(observer)
}
@CallSuper
override fun getView(): ViewGroup {
val adapter = instantiateAdapter()
observer?.also {
adapter.registerAdapterDataObserver(it)
}
return RecyclerView(context).apply {
this.adapter = adapter
this.layoutManager = instantiateLayoutManager()
this.itemAnimator = null
}
}
override fun onViewShown() {}
@CallSuper
override fun onViewHidden() {
observer?.also {
recyclerView?.adapter?.unregisterAdapterDataObserver(it)
}
recyclerView = null
observer = null
}
/**
* Dispatch click event to Autocomplete.Callback.
* Should be called when items are clicked.
*
* @param item the clicked item.
*/
protected fun dispatchClick(item: T) {
if (clicks != null) clicks?.click(item)
}
/**
* Request that the popup should recompute its dimensions based on a recent change in
* the view being displayed.
*
* This is already managed internally for [RecyclerView] events.
* Only use it for changes in other views that you have added to the popup,
* and only if one of the dimensions for the popup is WRAP_CONTENT .
*/
protected fun dispatchLayoutChange() {
if (observer != null) observer!!.onChanged()
}
/**
* Provide an adapter for the recycler.
* This should be a fresh instance every time this is called.
*
* @return a new adapter.
*/
protected abstract fun instantiateAdapter(): RecyclerView.Adapter<*>
/**
* Provides a layout manager for the recycler.
* This should be a fresh instance every time this is called.
* Defaults to a vertical LinearLayoutManager, which is guaranteed to work well.
*
* @return a new layout manager.
*/
protected fun instantiateLayoutManager(): RecyclerView.LayoutManager {
return LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
}
private class Observer internal constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
root.onChanged()
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
root.onChanged()
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
root.onChanged()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
root.onChanged()
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
root.onChanged()
}
}
}

View File

@ -18,8 +18,8 @@ package im.vector.riotx.features.autocomplete.command
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
import im.vector.riotx.features.command.Command import im.vector.riotx.features.command.Command
import javax.inject.Inject import javax.inject.Inject
@ -32,8 +32,6 @@ class AutocompleteCommandPresenter @Inject constructor(context: Context,
} }
override fun instantiateAdapter(): RecyclerView.Adapter<*> { override fun instantiateAdapter(): RecyclerView.Adapter<*> {
// Also remove animation
recyclerView?.itemAnimator = null
return controller.adapter return controller.adapter
} }
@ -51,4 +49,8 @@ class AutocompleteCommandPresenter @Inject constructor(context: Context,
} }
controller.setData(data) controller.setData(data)
} }
fun clear() {
controller.listener = null
}
} }

View File

@ -18,8 +18,8 @@ package im.vector.riotx.features.autocomplete.emoji
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
import im.vector.riotx.features.reactions.data.EmojiDataSource import im.vector.riotx.features.reactions.data.EmojiDataSource
import javax.inject.Inject import javax.inject.Inject
@ -32,9 +32,11 @@ class AutocompleteEmojiPresenter @Inject constructor(context: Context,
controller.listener = this controller.listener = this
} }
fun clear() {
controller.listener = null
}
override fun instantiateAdapter(): RecyclerView.Adapter<*> { override fun instantiateAdapter(): RecyclerView.Adapter<*> {
// Also remove animation
recyclerView?.itemAnimator = null
return controller.adapter return controller.adapter
} }

View File

@ -18,12 +18,12 @@ package im.vector.riotx.features.autocomplete.group
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.groupSummaryQueryParams import im.vector.matrix.android.api.session.group.groupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
import javax.inject.Inject import javax.inject.Inject
class AutocompleteGroupPresenter @Inject constructor(context: Context, class AutocompleteGroupPresenter @Inject constructor(context: Context,
@ -35,9 +35,11 @@ class AutocompleteGroupPresenter @Inject constructor(context: Context,
controller.listener = this controller.listener = this
} }
fun clear() {
controller.listener = null
}
override fun instantiateAdapter(): RecyclerView.Adapter<*> { override fun instantiateAdapter(): RecyclerView.Adapter<*> {
// Also remove animation
recyclerView?.itemAnimator = null
return controller.adapter return controller.adapter
} }

View File

@ -18,7 +18,6 @@ package im.vector.riotx.features.autocomplete.member
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.query.QueryStringValue
@ -27,6 +26,7 @@ import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
@Assisted val roomId: String, @Assisted val roomId: String,
@ -40,14 +40,16 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
controller.listener = this controller.listener = this
} }
fun clear() {
controller.listener = null
}
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
fun create(roomId: String): AutocompleteMemberPresenter fun create(roomId: String): AutocompleteMemberPresenter
} }
override fun instantiateAdapter(): RecyclerView.Adapter<*> { override fun instantiateAdapter(): RecyclerView.Adapter<*> {
// Also remove animation
recyclerView?.itemAnimator = null
return controller.adapter return controller.adapter
} }

View File

@ -18,12 +18,12 @@ package im.vector.riotx.features.autocomplete.room
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
import javax.inject.Inject import javax.inject.Inject
class AutocompleteRoomPresenter @Inject constructor(context: Context, class AutocompleteRoomPresenter @Inject constructor(context: Context,
@ -36,8 +36,6 @@ class AutocompleteRoomPresenter @Inject constructor(context: Context,
} }
override fun instantiateAdapter(): RecyclerView.Adapter<*> { override fun instantiateAdapter(): RecyclerView.Adapter<*> {
// Also remove animation
recyclerView?.itemAnimator = null
return controller.adapter return controller.adapter
} }
@ -58,4 +56,8 @@ class AutocompleteRoomPresenter @Inject constructor(context: Context,
.sortedBy { it.displayName } .sortedBy { it.displayName }
controller.setData(rooms.toList()) controller.setData(rooms.toList())
} }
fun clear() {
controller.listener = null
}
} }

View File

@ -55,7 +55,7 @@ import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
@ -66,6 +66,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
@Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var popupAlertManager: PopupAlertManager
@Inject lateinit var shortcutsHandler: ShortcutsHandler @Inject lateinit var shortcutsHandler: ShortcutsHandler
@Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
override fun onDrawerStateChanged(newState: Int) { override fun onDrawerStateChanged(newState: Int) {
@ -79,6 +80,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
injector.inject(this) injector.inject(this)
} }
override fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel {
return unknownDeviceViewModelFactory.create(initialState)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice()) FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice())

View File

@ -16,12 +16,16 @@
package im.vector.riotx.features.home package im.vector.riotx.features.home
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.NoOpMatrixCallback import im.vector.matrix.android.api.NoOpMatrixCallback
import im.vector.matrix.android.api.extensions.orFalse import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
@ -32,7 +36,6 @@ import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.EmptyViewEvents import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction import im.vector.riotx.core.platform.VectorViewModelAction
@ -53,16 +56,32 @@ data class DeviceDetectionInfo(
val currentSessionTrust: Boolean val currentSessionTrust: Boolean
) )
class UnknownDeviceDetectorSharedViewModel( class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState,
session: Session, session: Session,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences)
initialState: UnknownDevicesState)
: VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) { : VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
sealed class Action : VectorViewModelAction { sealed class Action : VectorViewModelAction {
data class IgnoreDevice(val deviceIds: List<String>) : Action() data class IgnoreDevice(val deviceIds: List<String>) : Action()
} }
@AssistedInject.Factory
interface Factory {
fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel
}
companion object : MvRxViewModelFactory<UnknownDeviceDetectorSharedViewModel, UnknownDevicesState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? {
val factory = when (viewModelContext) {
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
is ActivityViewModelContext -> viewModelContext.activity as? Factory
}
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
}
}
private val ignoredDeviceList = ArrayList<String>() private val ignoredDeviceList = ArrayList<String>()
init { init {
@ -146,12 +165,4 @@ class UnknownDeviceDetectorSharedViewModel(
vectorPreferences.storeUnknownDeviceDismissedList(ignoredDeviceList) vectorPreferences.storeUnknownDeviceDismissedList(ignoredDeviceList)
super.onCleared() super.onCleared()
} }
companion object : MvRxViewModelFactory<UnknownDeviceDetectorSharedViewModel, UnknownDevicesState> {
override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? {
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
return UnknownDeviceDetectorSharedViewModel(session, VectorPreferences(viewModelContext.activity()), state)
}
}
} }

View File

@ -34,6 +34,7 @@ import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.matrix.android.api.util.toRoomAliasMatrixItem import im.vector.matrix.android.api.util.toRoomAliasMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideApp
import im.vector.riotx.core.glide.GlideRequests
import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
import im.vector.riotx.features.autocomplete.emoji.AutocompleteEmojiPresenter import im.vector.riotx.features.autocomplete.emoji.AutocompleteEmojiPresenter
@ -56,12 +57,14 @@ class AutoCompleter @AssistedInject constructor(
private val autocompleteEmojiPresenter: AutocompleteEmojiPresenter private val autocompleteEmojiPresenter: AutocompleteEmojiPresenter
) { ) {
private lateinit var autocompleteMemberPresenter: AutocompleteMemberPresenter
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
fun create(roomId: String): AutoCompleter fun create(roomId: String): AutoCompleter
} }
private lateinit var editText: EditText private var editText: EditText? = null
fun enterSpecialMode() { fun enterSpecialMode() {
commandAutocompletePolicy.enabled = false commandAutocompletePolicy.enabled = false
@ -71,12 +74,11 @@ class AutoCompleter @AssistedInject constructor(
commandAutocompletePolicy.enabled = true commandAutocompletePolicy.enabled = true
} }
private val glideRequests by lazy { private lateinit var glideRequests: GlideRequests
GlideApp.with(editText)
}
fun setup(editText: EditText) { fun setup(editText: EditText) {
this.editText = editText this.editText = editText
glideRequests = GlideApp.with(editText)
val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(editText.context, R.attr.riotx_background)) val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(editText.context, R.attr.riotx_background))
setupCommands(backgroundDrawable, editText) setupCommands(backgroundDrawable, editText)
setupMembers(backgroundDrawable, editText) setupMembers(backgroundDrawable, editText)
@ -85,6 +87,15 @@ class AutoCompleter @AssistedInject constructor(
setupRooms(backgroundDrawable, editText) setupRooms(backgroundDrawable, editText)
} }
fun clear() {
this.editText = null
autocompleteEmojiPresenter.clear()
autocompleteGroupPresenter.clear()
autocompleteRoomPresenter.clear()
autocompleteCommandPresenter.clear()
autocompleteMemberPresenter.clear()
}
private fun setupCommands(backgroundDrawable: Drawable, editText: EditText) { private fun setupCommands(backgroundDrawable: Drawable, editText: EditText) {
Autocomplete.on<Command>(editText) Autocomplete.on<Command>(editText)
.with(commandAutocompletePolicy) .with(commandAutocompletePolicy)
@ -107,7 +118,7 @@ class AutoCompleter @AssistedInject constructor(
} }
private fun setupMembers(backgroundDrawable: ColorDrawable, editText: EditText) { private fun setupMembers(backgroundDrawable: ColorDrawable, editText: EditText) {
val autocompleteMemberPresenter = autocompleteMemberPresenterFactory.create(roomId) autocompleteMemberPresenter = autocompleteMemberPresenterFactory.create(roomId)
Autocomplete.on<RoomMemberSummary>(editText) Autocomplete.on<RoomMemberSummary>(editText)
.with(CharPolicy('@', true)) .with(CharPolicy('@', true))
.with(autocompleteMemberPresenter) .with(autocompleteMemberPresenter)

View File

@ -372,6 +372,7 @@ class RoomDetailFragment @Inject constructor(
timelineEventController.callback = null timelineEventController.callback = null
timelineEventController.removeModelBuildListener(modelBuildListener) timelineEventController.removeModelBuildListener(modelBuildListener)
modelBuildListener = null modelBuildListener = null
autoCompleter.clear()
debouncer.cancelAll() debouncer.cancelAll()
recyclerView.cleanup() recyclerView.cleanup()

View File

@ -72,6 +72,7 @@ class RoomListFragment @Inject constructor(
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
private val roomListParams: RoomListParams by args() private val roomListParams: RoomListParams by args()
private val roomListViewModel: RoomListViewModel by fragmentViewModel() private val roomListViewModel: RoomListViewModel by fragmentViewModel()
private lateinit var stateRestorer: LayoutManagerStateRestorer
override fun getLayoutResId() = R.layout.fragment_room_list override fun getLayoutResId() = R.layout.fragment_room_list
@ -126,6 +127,7 @@ class RoomListFragment @Inject constructor(
modelBuildListener = null modelBuildListener = null
roomListView.cleanup() roomListView.cleanup()
roomController.listener = null roomController.listener = null
stateRestorer.clear()
createChatFabMenu.listener = null createChatFabMenu.listener = null
super.onDestroyView() super.onDestroyView()
} }
@ -190,7 +192,7 @@ class RoomListFragment @Inject constructor(
private fun setupRecyclerView() { private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context) val layoutManager = LinearLayoutManager(context)
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register() stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
roomListView.layoutManager = layoutManager roomListView.layoutManager = layoutManager
roomListView.itemAnimator = RoomListAnimator() roomListView.itemAnimator = RoomListAnimator()
roomListView.setRecycledViewPool(sharedViewPool) roomListView.setRecycledViewPool(sharedViewPool)

View File

@ -66,7 +66,7 @@ class RoomMemberProfileFragment @Inject constructor(
private val fragmentArgs: RoomMemberProfileArgs by args() private val fragmentArgs: RoomMemberProfileArgs by args()
private val viewModel: RoomMemberProfileViewModel by fragmentViewModel() private val viewModel: RoomMemberProfileViewModel by fragmentViewModel()
private lateinit var appBarStateChangeListener: AppBarStateChangeListener private var appBarStateChangeListener: AppBarStateChangeListener? = null
override fun getLayoutResId() = R.layout.fragment_matrix_profile override fun getLayoutResId() = R.layout.fragment_matrix_profile
@ -156,6 +156,7 @@ class RoomMemberProfileFragment @Inject constructor(
override fun onDestroyView() { override fun onDestroyView() {
matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener) matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener)
roomMemberProfileController.callback = null roomMemberProfileController.callback = null
appBarStateChangeListener = null
matrixProfileRecyclerView.cleanup() matrixProfileRecyclerView.cleanup()
super.onDestroyView() super.onDestroyView()
} }

View File

@ -67,7 +67,7 @@ class RoomProfileFragment @Inject constructor(
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel()
private lateinit var appBarStateChangeListener: AppBarStateChangeListener private var appBarStateChangeListener: AppBarStateChangeListener? = null
override fun getLayoutResId() = R.layout.fragment_matrix_profile override fun getLayoutResId() = R.layout.fragment_matrix_profile
@ -147,6 +147,7 @@ class RoomProfileFragment @Inject constructor(
super.onDestroyView() super.onDestroyView()
matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener) matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener)
matrixProfileRecyclerView.cleanup() matrixProfileRecyclerView.cleanup()
appBarStateChangeListener = null
} }
override fun invalidate() = withState(roomProfileViewModel) { state -> override fun invalidate() = withState(roomProfileViewModel) { state ->