Commit 9d398fb3 authored by Arturo Mejia's avatar Arturo Mejia
Browse files

Closes #1382: Add implementation for capture thumbnail of websites

on GeckoEngineView.
parent 041abb84
......@@ -583,7 +583,7 @@ class SystemEngineView @JvmOverloads constructor(
override fun captureThumbnail(onFinish: (Bitmap?) -> Unit) {
val webView = session?.webView
val thumbnails = if (webView == null) {
val thumbnail = if (webView == null) {
null
} else {
webView.buildDrawingCache()
......@@ -591,7 +591,7 @@ class SystemEngineView @JvmOverloads constructor(
webView.destroyDrawingCache()
outBitmap
}
onFinish(thumbnails)
onFinish(thumbnail)
}
private fun resetJSAlertAbuseState() {
......
......@@ -68,7 +68,7 @@ interface EngineView {
fun canScrollVerticallyDown(): Boolean = true
/**
* Take a screenshot of the actual session.
* Request a screenshot of the visible portion of the web page currently being rendered.
* @param onFinish A callback to inform that process of capturing a thumbnail has finished.
*/
fun captureThumbnail(onFinish: (Bitmap?) -> Unit)
......
......@@ -14,6 +14,24 @@ Use Gradle to download the library from [maven.mozilla.org](https://maven.mozill
implementation "org.mozilla.components:feature-session:{latest-version}"
```
### ThumbnailsFeature
Feature implementation for automatically taking thumbnails of sites. The feature will take a screenshot when the page finishes loading, and will add it to the `Session.thumbnail` property.
```kotlin
val feature = ThumbnailsFeature(context, engineView, sessionManager)
lifecycle.addObserver(feature)
```
If the OS is under low memory conditions, the screenshot will be not taken. Ideally, this should be used in conjunction with [SessionManager.onLowMemory](https://github.com/mozilla-mobile/android-components/blob/024e3de456e3b46e9bf6718db9500ecc52da3d29/components/browser/session/src/main/java/mozilla/components/browser/session/SessionManager.kt#L472) to allow free up some `Session.thumbnail` from memory.
```kotlin
// Wherever you implement ComponentCallbacks2
override fun onTrimMemory(level: Int) {
sessionManager.onLowMemory()
}
```
## License
This Source Code Form is subject to the terms of the Mozilla Public
......
......@@ -35,6 +35,7 @@ dependencies {
testImplementation Dependencies.testing_junit
testImplementation Dependencies.testing_robolectric
testImplementation Dependencies.testing_mockito
testImplementation Dependencies.androidx_test_core
testImplementation Dependencies.support_customtabs
}
......
/* 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.feature.session
import android.content.Context
import android.support.annotation.VisibleForTesting
import mozilla.components.browser.session.SelectionAwareSessionObserver
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.EngineView
import mozilla.components.support.base.feature.LifecycleAwareFeature
import mozilla.components.support.ktx.android.content.isOSOnLowMemory
/**
* Feature implementation for automatically taking thumbnails of sites.
* The feature will take a screenshot when the page finishes loading,
* and will add it to the [Session.thumbnail] property.
*
* If the OS is under low memory conditions, the screenshot will be not taken.
* Ideally, this should be used in conjunction with [SessionManager.onLowMemory] to allow
* free up some [Session.thumbnail] from memory.
*/
class ThumbnailsFeature(
private val context: Context,
private val engineView: EngineView,
sessionManager: SessionManager
) : LifecycleAwareFeature {
private val observer = ThumbnailsFeatureRequestObserver(sessionManager)
/**
* Starts observing the selected session to listen for when a session finish loading.
*/
override fun start() {
observer.observeSelected()
}
/**
* Stops observing the selected session.
*/
override fun stop() {
observer.stop()
}
internal inner class ThumbnailsFeatureRequestObserver(
sessionManager: SessionManager
) : SelectionAwareSessionObserver(sessionManager) {
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading) {
requestScreenshot(session)
}
}
}
private fun requestScreenshot(session: Session) {
if (!isLowOnMemory()) {
engineView.captureThumbnail {
session.thumbnail = it
}
} else {
session.thumbnail = null
}
}
@VisibleForTesting
internal var testLowMemory = false
private fun isLowOnMemory() = testLowMemory || context.isOSOnLowMemory()
}
/* 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.feature.session
import android.content.Context
import android.graphics.Bitmap
import androidx.test.core.app.ApplicationProvider
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineView
import mozilla.components.support.test.any
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class ThumbnailsFeatureTest {
private lateinit var mockSessionManager: SessionManager
private lateinit var mockEngineView: EngineView
private lateinit var feature: ThumbnailsFeature
@Before
fun setup() {
val engine = mock(Engine::class.java)
val context = ApplicationProvider.getApplicationContext<Context>()
mockSessionManager = spy(SessionManager(engine))
mockEngineView = mock(EngineView::class.java)
feature = ThumbnailsFeature(context, mockEngineView, mockSessionManager)
}
@Test
fun `when feature is stop must not capture thumbnail when a site finish loading`() {
feature.start()
feature.stop()
val session = getSelectedSession()
session.notifyObservers {
onLoadingStateChanged(session, false)
}
verify(mockEngineView, never()).captureThumbnail(any())
}
@Test
fun `feature must capture thumbnail when a site finish loading`() {
feature.start()
val session = getSelectedSession()
session.notifyObservers {
onLoadingStateChanged(session, false)
}
verify(mockEngineView).captureThumbnail(any())
}
@Test
fun `when a page is loaded and the os is in low memory condition none thumbnail should be captured`() {
feature.start()
val session = getSelectedSession()
session.thumbnail = mock(Bitmap::class.java)
feature.testLowMemory = true
session.notifyObservers {
onLoadingStateChanged(session, false)
}
verify(mockEngineView, never()).captureThumbnail(any())
assertNull(session.thumbnail)
}
private fun getSelectedSession(): Session {
val session = Session("https://www.mozilla.org")
mockSessionManager.add(session)
mockSessionManager.select(session)
return session
}
}
......@@ -35,6 +35,9 @@ permalink: /changelog/
* An instance of a SyncManager is an entry point for interacting with background data synchronization.
* See component's README for usage details.
* **browser-engine-system** and **browser-engine-gecko-nightly**
* ⚠️ **This is a breaking API change**: The [`captureThumbnail`](https://github.com/mozilla-mobile/android-components/blob/1b1600a7e8aa83a7e7d09b30cecd49762f7781f5/components/concept/engine/src/main/java/mozilla/components/concept/engine/EngineSession.kt#L245) function has been moved to [`EngineView`](https://github.com/mozilla-mobile/android-components/blob/1b1600a7e8aa83a7e7d09b30cecd49762f7781f5/components/concept/engine/src/main/java/mozilla/components/concept/engine/EngineView.kt#L15). From now on for taking screenshots automatically you will have to opt-in by using `ThumbnailsFeature`. The decision was made to reduce overhead memory consumption for apps that are not using screenshots. Find more info in [feature-session](https://github.com/mozilla-mobile/android-components/blob/master/components/feature/session/README.md) and a practical example can be found in the [sample-browser project](https://github.com/mozilla-mobile/android-components/blob/master/samples/browser).
# 0.46.0
* [Commits](https://github.com/mozilla-mobile/android-components/compare/v0.45.0...v0.46.0)
......
......@@ -19,6 +19,7 @@ import mozilla.components.feature.downloads.DownloadsFeature
import mozilla.components.feature.prompts.PromptFeature
import mozilla.components.feature.session.CoordinateScrollingFeature
import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.session.ThumbnailsFeature
import mozilla.components.feature.session.WindowFeature
import mozilla.components.feature.sitepermissions.SitePermissionsFeature
import mozilla.components.feature.tabs.toolbar.TabsToolbarFeature
......@@ -39,6 +40,7 @@ class BrowserFragment : Fragment(), BackHandler {
private val promptFeature = ViewBoundFeatureWrapper<PromptFeature>()
private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
private val sitePermissionsFeature = ViewBoundFeatureWrapper<SitePermissionsFeature>()
private val thumbnailsFeature = ViewBoundFeatureWrapper<ThumbnailsFeature>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val layout = inflater.inflate(R.layout.fragment_browser, container, false)
......@@ -152,6 +154,12 @@ class BrowserFragment : Fragment(), BackHandler {
windowFeature
)
thumbnailsFeature.set(
feature = ThumbnailsFeature(requireContext(), layout.engineView, components.sessionManager),
owner = this,
view = layout
)
return layout
}
......
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