mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 17:35:54 +03:00
Remove memory leak from Autocompleter
This commit is contained in:
parent
d6329a1ab6
commit
917042c48c
10 changed files with 168 additions and 26 deletions
|
@ -16,8 +16,6 @@
|
|||
|
||||
package im.vector.matrix.android.api.auth.data
|
||||
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||
|
||||
// Either a list of supported login types, or an error if the homeserver is outdated
|
||||
sealed class LoginFlowResult {
|
||||
data class Success(
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@ package im.vector.riotx.features.autocomplete.command
|
|||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.riotx.features.command.Command
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -32,8 +32,6 @@ class AutocompleteCommandPresenter @Inject constructor(context: Context,
|
|||
}
|
||||
|
||||
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
|
||||
// Also remove animation
|
||||
recyclerView?.itemAnimator = null
|
||||
return controller.adapter
|
||||
}
|
||||
|
||||
|
@ -51,4 +49,8 @@ class AutocompleteCommandPresenter @Inject constructor(context: Context,
|
|||
}
|
||||
controller.setData(data)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
controller.listener = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ package im.vector.riotx.features.autocomplete.emoji
|
|||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -32,9 +32,11 @@ class AutocompleteEmojiPresenter @Inject constructor(context: Context,
|
|||
controller.listener = this
|
||||
}
|
||||
|
||||
fun clear(){
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
|
||||
// Also remove animation
|
||||
recyclerView?.itemAnimator = null
|
||||
return controller.adapter
|
||||
}
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ package im.vector.riotx.features.autocomplete.group
|
|||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.matrix.android.api.query.QueryStringValue
|
||||
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.model.GroupSummary
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteGroupPresenter @Inject constructor(context: Context,
|
||||
|
@ -35,9 +35,11 @@ class AutocompleteGroupPresenter @Inject constructor(context: Context,
|
|||
controller.listener = this
|
||||
}
|
||||
|
||||
fun clear(){
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
|
||||
// Also remove animation
|
||||
recyclerView?.itemAnimator = null
|
||||
return controller.adapter
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ package im.vector.riotx.features.autocomplete.member
|
|||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
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.RoomMemberSummary
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
|
||||
|
||||
class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
|
||||
@Assisted val roomId: String,
|
||||
|
@ -40,14 +40,16 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
|
|||
controller.listener = this
|
||||
}
|
||||
|
||||
fun clear(){
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(roomId: String): AutocompleteMemberPresenter
|
||||
}
|
||||
|
||||
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
|
||||
// Also remove animation
|
||||
recyclerView?.itemAnimator = null
|
||||
return controller.adapter
|
||||
}
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ package im.vector.riotx.features.autocomplete.room
|
|||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.matrix.android.api.query.QueryStringValue
|
||||
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.roomSummaryQueryParams
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotx.features.autocomplete.RecyclerViewPresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteRoomPresenter @Inject constructor(context: Context,
|
||||
|
@ -36,8 +36,6 @@ class AutocompleteRoomPresenter @Inject constructor(context: Context,
|
|||
}
|
||||
|
||||
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
|
||||
// Also remove animation
|
||||
recyclerView?.itemAnimator = null
|
||||
return controller.adapter
|
||||
}
|
||||
|
||||
|
@ -58,4 +56,8 @@ class AutocompleteRoomPresenter @Inject constructor(context: Context,
|
|||
.sortedBy { it.displayName }
|
||||
controller.setData(rooms.toList())
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
controller.listener = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,13 +36,10 @@ 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.store.PrivateKeysInfo
|
||||
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.VectorViewModel
|
||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import im.vector.riotx.features.widgets.WidgetViewModel
|
||||
import im.vector.riotx.features.widgets.WidgetViewState
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.Function3
|
||||
import timber.log.Timber
|
||||
|
@ -168,5 +165,4 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
|
|||
vectorPreferences.storeUnknownDeviceDismissedList(ignoredDeviceList)
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import im.vector.matrix.android.api.util.toMatrixItem
|
|||
import im.vector.matrix.android.api.util.toRoomAliasMatrixItem
|
||||
import im.vector.riotx.R
|
||||
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.CommandAutocompletePolicy
|
||||
import im.vector.riotx.features.autocomplete.emoji.AutocompleteEmojiPresenter
|
||||
|
@ -56,12 +57,14 @@ class AutoCompleter @AssistedInject constructor(
|
|||
private val autocompleteEmojiPresenter: AutocompleteEmojiPresenter
|
||||
) {
|
||||
|
||||
private lateinit var autocompleteMemberPresenter: AutocompleteMemberPresenter
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(roomId: String): AutoCompleter
|
||||
}
|
||||
|
||||
private lateinit var editText: EditText
|
||||
private var editText: EditText? = null
|
||||
|
||||
fun enterSpecialMode() {
|
||||
commandAutocompletePolicy.enabled = false
|
||||
|
@ -71,12 +74,11 @@ class AutoCompleter @AssistedInject constructor(
|
|||
commandAutocompletePolicy.enabled = true
|
||||
}
|
||||
|
||||
private val glideRequests by lazy {
|
||||
GlideApp.with(editText)
|
||||
}
|
||||
private lateinit var glideRequests: GlideRequests
|
||||
|
||||
fun setup(editText: EditText) {
|
||||
this.editText = editText
|
||||
glideRequests = GlideApp.with(editText)
|
||||
val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(editText.context, R.attr.riotx_background))
|
||||
setupCommands(backgroundDrawable, editText)
|
||||
setupMembers(backgroundDrawable, editText)
|
||||
|
@ -85,6 +87,15 @@ class AutoCompleter @AssistedInject constructor(
|
|||
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) {
|
||||
Autocomplete.on<Command>(editText)
|
||||
.with(commandAutocompletePolicy)
|
||||
|
@ -104,10 +115,11 @@ class AutoCompleter @AssistedInject constructor(
|
|||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
}
|
||||
|
||||
private fun setupMembers(backgroundDrawable: ColorDrawable, editText: EditText) {
|
||||
val autocompleteMemberPresenter = autocompleteMemberPresenterFactory.create(roomId)
|
||||
autocompleteMemberPresenter = autocompleteMemberPresenterFactory.create(roomId)
|
||||
Autocomplete.on<RoomMemberSummary>(editText)
|
||||
.with(CharPolicy('@', true))
|
||||
.with(autocompleteMemberPresenter)
|
||||
|
|
|
@ -370,6 +370,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
timelineEventController.callback = null
|
||||
timelineEventController.removeModelBuildListener(modelBuildListener)
|
||||
modelBuildListener = null
|
||||
autoCompleter.clear()
|
||||
debouncer.cancelAll()
|
||||
recyclerView.cleanup()
|
||||
super.onDestroyView()
|
||||
|
|
Loading…
Reference in a new issue