Commit 2f456636 authored by Sawyer Blatz's avatar Sawyer Blatz Committed by Sebastian Kaspari
Browse files

For #4653: Improves performance

parent a7ce9918
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.components.browser.awesomebar.layout
import android.graphics.Bitmap
import android.widget.ImageView
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import mozilla.components.concept.awesomebar.AwesomeBar
import mozilla.components.support.test.mock
import mozilla.utils.setupTestCoroutinesDispatcher
import mozilla.utils.unsetTestCoroutinesDispatcher
import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class IconLoaderTest {
@Before
fun setUp() {
setupTestCoroutinesDispatcher()
}
@After
fun tearDown() {
unsetTestCoroutinesDispatcher()
}
@Test
fun `loads image and sets it on ImageView`() {
runBlocking {
val bitmap: Bitmap = mock()
val suggestion = AwesomeBar.Suggestion(
provider = mock(),
icon = { _, _ -> bitmap })
val view: ImageView = mock()
val loader = IconLoader(view)
loader.load(suggestion)
assertNotNull(loader.iconJob)
loader.iconJob!!.join()
verify(view).setImageBitmap(bitmap)
}
}
@Test
fun `Load task is cancelled`() {
runBlocking {
@Suppress("UNREACHABLE_CODE")
val suggestion = AwesomeBar.Suggestion(
provider = mock(),
icon = { _, _ ->
while (true) { delay(10) }
null
})
val view: ImageView = mock()
val loader = IconLoader(view)
delay(100)
loader.load(suggestion)
loader.cancel()
withTimeout(5000) {
loader.iconJob!!.join()
}
assertTrue(loader.iconJob!!.isCancelled)
}
}
}
......@@ -108,7 +108,7 @@ class AwesomeBarFeature(
context: Context,
loadUrlUseCase: SessionUseCases.LoadUrlUseCase
): AwesomeBarFeature {
awesomeBar.addProviders(ClipboardSuggestionProvider(context, loadUrlUseCase, icons = icons))
awesomeBar.addProviders(ClipboardSuggestionProvider(context, loadUrlUseCase))
return this
}
......
......@@ -4,7 +4,6 @@
package mozilla.components.feature.awesomebar.provider
import kotlinx.coroutines.runBlocking
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.concept.awesomebar.AwesomeBar
......@@ -40,16 +39,17 @@ class BookmarksStorageSuggestionProvider(
/**
* Expects list of BookmarkNode to be specifically of bookmarks (e.g. nodes with a url).
*/
private fun List<BookmarkNode>.into(): List<AwesomeBar.Suggestion> {
return this.map {
private suspend fun List<BookmarkNode>.into(): List<AwesomeBar.Suggestion> {
val iconRequests = this.map { icons?.loadIcon(IconRequest(it.url!!)) }
return this.zip(iconRequests) { result, icon ->
AwesomeBar.Suggestion(
provider = this@BookmarksStorageSuggestionProvider,
id = it.guid,
// We can runBlocking here to get the icon since we are already on an IO thread
icon = runBlocking { icons?.loadIcon(IconRequest(it.url!!))?.await()?.bitmap },
title = it.title,
description = it.url,
onSuggestionClicked = { loadUrlUseCase.invoke(it.url!!) }
id = result.guid,
icon = icon?.await()?.bitmap,
title = result.title,
description = result.url,
onSuggestionClicked = { loadUrlUseCase.invoke(result.url!!) }
)
}
}
......
......@@ -8,10 +8,9 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.Bitmap
import kotlinx.coroutines.runBlocking
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import androidx.core.graphics.drawable.toBitmap
import mozilla.components.concept.awesomebar.AwesomeBar
import mozilla.components.feature.awesomebar.R
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.utils.WebURLFinder
import java.util.UUID
......@@ -23,11 +22,10 @@ private const val MIME_TYPE_TEXT_PLAIN = "text/plain"
* any).
*/
class ClipboardSuggestionProvider(
context: Context,
private val context: Context,
private val loadUrlUseCase: SessionUseCases.LoadUrlUseCase,
private val icon: Bitmap? = null,
private val title: String? = null,
private val icons: BrowserIcons? = null,
private val requireEmptyText: Boolean = true
) : AwesomeBar.SuggestionProvider {
override val id: String = UUID.randomUUID().toString()
......@@ -49,8 +47,7 @@ class ClipboardSuggestionProvider(
id = url,
description = url,
flags = setOf(AwesomeBar.Suggestion.Flag.CLIPBOARD),
// We can runBlocking here to get the icon since we are already on an IO thread
icon = icon ?: runBlocking { icons?.loadIcon(IconRequest(url))?.await()?.bitmap },
icon = icon ?: context.getDrawable(R.drawable.mozac_ic_search)?.toBitmap(),
title = title,
onSuggestionClicked = {
loadUrlUseCase.invoke(url)
......
......@@ -4,7 +4,6 @@
package mozilla.components.feature.awesomebar.provider
import kotlinx.coroutines.runBlocking
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.concept.awesomebar.AwesomeBar
......@@ -42,17 +41,17 @@ class HistoryStorageSuggestionProvider(
// We do not want the suggestion of this provider to disappear and re-appear when text changes.
get() = false
private fun Iterable<SearchResult>.into(): List<AwesomeBar.Suggestion> {
return this.map {
private suspend fun Iterable<SearchResult>.into(): List<AwesomeBar.Suggestion> {
val iconRequests = this.map { icons?.loadIcon(IconRequest(it.url)) }
return this.zip(iconRequests) { result, icon ->
AwesomeBar.Suggestion(
provider = this@HistoryStorageSuggestionProvider,
id = it.id,
// We can runBlocking here to get the icon since we are already on an IO thread
icon = runBlocking { icons?.loadIcon(IconRequest(it.url))?.await()?.bitmap },
title = it.title,
description = it.url,
score = it.score,
onSuggestionClicked = { loadUrlUseCase.invoke(it.url) }
id = result.id,
icon = icon?.await()?.bitmap,
title = result.title,
description = result.url,
score = result.score,
onSuggestionClicked = { loadUrlUseCase.invoke(result.url) }
)
}
}
......
......@@ -4,8 +4,9 @@
package mozilla.components.feature.awesomebar.provider
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.Deferred
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.Icon
import mozilla.components.browser.icons.IconRequest
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
......@@ -31,29 +32,28 @@ class SessionSuggestionProvider(
}
val suggestions = mutableListOf<AwesomeBar.Suggestion>()
val iconRequests: List<Deferred<Icon>?> = sessionManager.sessions.map { icons?.loadIcon(IconRequest(it.url)) }
sessionManager.sessions.forEach { session ->
if (session.contains(text) && !session.private && shouldIncludeSelectedSession(session)
sessionManager.sessions.zip(iconRequests) { result, icon ->
if (result.contains(text) && !result.private && shouldIncludeSelectedSession(result)
) {
suggestions.add(
AwesomeBar.Suggestion(
provider = this,
id = session.id,
title = session.title,
description = session.url,
// We can runBlocking here to get the icon since we are already on an IO thread
icon = runBlocking { icons?.loadIcon(IconRequest(session.url))?.await()?.bitmap },
onSuggestionClicked = { selectTabUseCase(session) }
)
AwesomeBar.Suggestion(
provider = this,
id = result.id,
title = result.title,
description = result.url,
icon = icon?.await()?.bitmap,
onSuggestionClicked = { selectTabUseCase(result) }
)
)
}
}
return suggestions
}
private fun Session.contains(text: String) =
(url.contains(text, ignoreCase = true) || title.contains(text, ignoreCase = true))
(url.contains(text, ignoreCase = true) || title.contains(text, ignoreCase = true))
private fun shouldIncludeSelectedSession(session: Session): Boolean {
return if (excludeSelectedSession) {
......
......@@ -136,7 +136,7 @@ class ClipboardSuggestionProviderTest {
}
runBlocking {
assertEquals(bitmap, suggestion?.icon?.invoke(2, 2))
assertEquals(bitmap, suggestion?.icon)
assertEquals("My test title", suggestion?.title)
}
}
......
......@@ -266,7 +266,7 @@ class SearchSuggestionProviderTest {
try {
val suggestions = provider.onInputChanged("fire")
assertEquals(1, suggestions.size)
assertTrue(suggestions[0].icon?.invoke(20, 20)?.sameAs(engineIcon)!!)
assertTrue(suggestions[0].icon?.sameAs(engineIcon)!!)
} finally {
server.shutdown()
}
......@@ -296,7 +296,7 @@ class SearchSuggestionProviderTest {
try {
val suggestions = provider.onInputChanged("fire")
assertEquals(1, suggestions.size)
assertTrue(suggestions[0].icon?.invoke(20, 20)?.sameAs(paramIcon)!!)
assertTrue(suggestions[0].icon?.sameAs(paramIcon)!!)
} finally {
server.shutdown()
}
......
......@@ -12,6 +12,9 @@ permalink: /changelog/
* [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt)
* [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Config.kt)
* **feature-awesomebar**
* ⚠️ **This is a breaking change**: `AwesomeBar.Suggestion` now directly takes a Bitmap for the icon param rather than a Unit.
* **feature-pwa**
* ⚠️ **This is a breaking change**: Intent sent from the `WebAppShortcutManager` now require the consumption of the `SHORTCUT_CATEGORY` in your manifest
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment