Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fga/improve room detail start #4065

Merged
merged 6 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4065.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve performances on RoomDetail screen
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
package im.vector.app.features.reactions.data

import im.vector.app.InstrumentedTest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
Expand All @@ -30,64 +34,80 @@ import kotlin.system.measureTimeMillis
@FixMethodOrder(MethodSorters.JVM)
class EmojiDataSourceTest : InstrumentedTest {

private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

@Test
fun checkParsingTime() {
val time = measureTimeMillis {
EmojiDataSource(context().resources)
createEmojiDataSource()
}

assertTrue("Too long to parse", time < 100)
}

@Test
fun checkNumberOfResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertTrue("Wrong number of emojis", emojiDataSource.rawData.emojis.size >= 500)
assertTrue("Wrong number of categories", emojiDataSource.rawData.categories.size >= 8)
val emojiDataSource = createEmojiDataSource()
val rawData = runBlocking {
emojiDataSource.rawData.await()
}
assertTrue("Wrong number of emojis", rawData.emojis.size >= 500)
assertTrue("Wrong number of categories", rawData.categories.size >= 8)
}

@Test
fun searchTestEmptySearch() {
val emojiDataSource = EmojiDataSource(context().resources)

assertTrue("Empty search should return at least 500 results", emojiDataSource.filterWith("").size >= 500)
val emojiDataSource = createEmojiDataSource()
val result = runBlocking {
emojiDataSource.filterWith("")
}
assertTrue("Empty search should return at least 500 results", result.size >= 500)
}

@Test
fun searchTestNoResult() {
val emojiDataSource = EmojiDataSource(context().resources)

assertTrue("Should not have result", emojiDataSource.filterWith("noresult").isEmpty())
val emojiDataSource = createEmojiDataSource()
val result = runBlocking {
emojiDataSource.filterWith("noresult")
}
assertTrue("Should not have result", result.isEmpty())
}

@Test
fun searchTestOneResult() {
val emojiDataSource = EmojiDataSource(context().resources)

assertEquals("Should have 1 result", 1, emojiDataSource.filterWith("france").size)
val emojiDataSource = createEmojiDataSource()
val result = runBlocking {
emojiDataSource.filterWith("france")
}
assertEquals("Should have 1 result", 1, result.size)
}

@Test
fun searchTestManyResult() {
val emojiDataSource = EmojiDataSource(context().resources)

assertTrue("Should have many result", emojiDataSource.filterWith("fra").size > 1)
val emojiDataSource = createEmojiDataSource()
val result = runBlocking {
emojiDataSource.filterWith("fra")
}
assertTrue("Should have many result", result.size > 1)
}

@Test
fun testTada() {
val emojiDataSource = EmojiDataSource(context().resources)

val result = emojiDataSource.filterWith("tada")

val emojiDataSource = createEmojiDataSource()
val result = runBlocking {
emojiDataSource.filterWith("tada")
}
assertEquals("Should find tada emoji", 1, result.size)
assertEquals("Should find tada emoji", "🎉", result[0].emoji)
}

@Test
fun testQuickReactions() {
val emojiDataSource = EmojiDataSource(context().resources)

assertEquals("Should have 8 quick reactions", 8, emojiDataSource.getQuickReactions().size)
val emojiDataSource = createEmojiDataSource()
val result = runBlocking {
emojiDataSource.getQuickReactions()
}
assertEquals("Should have 8 quick reactions", 8, result.size)
}

private fun createEmojiDataSource() = EmojiDataSource(coroutineScope, context().resources)
}
2 changes: 2 additions & 0 deletions vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import im.vector.app.features.usercode.UserCodeActivity
import im.vector.app.features.widgets.WidgetActivity
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import im.vector.app.features.workers.signout.SignOutBottomSheetDialogFragment
import kotlinx.coroutines.CoroutineScope

@Component(
dependencies = [
Expand Down Expand Up @@ -129,6 +130,7 @@ interface ScreenComponent {
fun uiStateRepository(): UiStateRepository
fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
fun autoAcceptInvites(): AutoAcceptInvites
fun appCoroutineScope(): CoroutineScope

/* ==========================================================================================
* Activities
Expand Down
3 changes: 3 additions & 0 deletions vector/src/main/java/im/vector/app/core/di/VectorComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import im.vector.app.features.reactions.data.EmojiDataSource
import im.vector.app.features.session.SessionListener
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.CoroutineScope
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
Expand Down Expand Up @@ -165,6 +166,8 @@ interface VectorComponent {

fun webRtcCallManager(): WebRtcCallManager

fun appCoroutineScope(): CoroutineScope

fun jitsiActiveConferenceHolder(): JitsiActiveConferenceHolder

@Component.Factory
Expand Down
11 changes: 11 additions & 0 deletions vector/src/main/java/im/vector/app/core/di/VectorModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.SharedPrefPinCodeStore
import im.vector.app.features.ui.SharedPreferencesUiStateRepository
import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session
import javax.inject.Singleton

@Module
abstract class VectorModule {
Expand Down Expand Up @@ -94,6 +98,13 @@ abstract class VectorModule {
fun providesHomeServerHistoryService(matrix: Matrix): HomeServerHistoryService {
return matrix.homeServerHistoryService()
}

@Provides
@JvmStatic
@Singleton
fun providesApplicationCoroutineScope(): CoroutineScope {
return CoroutineScope(SupervisorJob() + Dispatchers.Main)
}
}

@Binds
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2021 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.app.core.platform

import androidx.annotation.MainThread
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent

fun <T> LifecycleOwner.lifecycleAwareLazy(initializer: () -> T): Lazy<T> = LifecycleAwareLazy(this, initializer)

private object UninitializedValue

class LifecycleAwareLazy<out T>(
private val owner: LifecycleOwner,
initializer: () -> T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not private val?

) : Lazy<T>, LifecycleObserver {

private var initializer: (() -> T)? = initializer

private var _value: Any? = UninitializedValue

@Suppress("UNCHECKED_CAST")
override val value: T
@MainThread
get() {
if (_value === UninitializedValue) {
_value = initializer!!()
attachToLifecycle()
}
return _value as T
}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun resetValue() {
_value = UninitializedValue
detachFromLifecycle()
}

private fun attachToLifecycle() {
if (getLifecycleOwner().lifecycle.currentState == Lifecycle.State.DESTROYED) {
throw IllegalStateException("Initialization failed because lifecycle has been destroyed!")
}
getLifecycleOwner().lifecycle.addObserver(this)
}

private fun detachFromLifecycle() {
getLifecycleOwner().lifecycle.removeObserver(this)
}

private fun getLifecycleOwner() = when (owner) {
is Fragment -> owner.viewLifecycleOwner
else -> owner
}

override fun isInitialized(): Boolean = _value !== UninitializedValue

override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package im.vector.app.core.ui.views
import android.content.Context
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.databinding.ViewFailedMessagesWarningBinding

Expand Down Expand Up @@ -49,8 +48,4 @@ class FailedMessagesWarningView @JvmOverloads constructor(
views.failedMessagesDeleteAllButton.setOnClickListener { callback?.onDeleteAllClicked() }
views.failedMessagesRetryButton.setOnClickListener { callback?.onRetryClicked() }
}

fun render(hasFailedMessages: Boolean) {
isVisible = hasFailedMessages
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,26 @@ import androidx.recyclerview.widget.RecyclerView
import im.vector.app.features.autocomplete.AutocompleteClickListener
import im.vector.app.features.autocomplete.RecyclerViewPresenter
import im.vector.app.features.reactions.data.EmojiDataSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import javax.inject.Inject

class AutocompleteEmojiPresenter @Inject constructor(context: Context,
private val emojiDataSource: EmojiDataSource,
private val controller: AutocompleteEmojiController) :
RecyclerViewPresenter<String>(context), AutocompleteClickListener<String> {

private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

init {
controller.listener = this
}

fun clear() {
coroutineScope.coroutineContext.cancelChildren()
controller.listener = null
}

Expand All @@ -45,12 +53,14 @@ class AutocompleteEmojiPresenter @Inject constructor(context: Context,
}

override fun onQuery(query: CharSequence?) {
val data = if (query.isNullOrBlank()) {
// Return common emojis
emojiDataSource.getQuickReactions()
} else {
emojiDataSource.filterWith(query.toString())
coroutineScope.launch {
val data = if (query.isNullOrBlank()) {
// Return common emojis
emojiDataSource.getQuickReactions()
} else {
emojiDataSource.filterWith(query.toString())
}
controller.setData(data)
}
controller.setData(data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package im.vector.app.features.autocomplete.member
import android.content.Context
import androidx.recyclerview.widget.RecyclerView
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.features.autocomplete.AutocompleteClickListener
import im.vector.app.features.autocomplete.RecyclerViewPresenter
import org.matrix.android.sdk.api.query.QueryStringValue
Expand All @@ -35,7 +35,7 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
private val controller: AutocompleteMemberController
) : RecyclerViewPresenter<RoomMemberSummary>(context), AutocompleteClickListener<RoomMemberSummary> {

private val room = session.getRoom(roomId)!!
private val room by lazy { session.getRoom(roomId)!! }

init {
controller.listener = this
Expand Down
Loading