Richard Pospesel pushed to branch android-components-102.0.14-12.0-1 at The Tor Project / Applications / android-components
Commits: b65622fa by Arturo Mejia at 2022-08-30T21:22:54+00:00 Improve the media delegate
- - - - -
4 changed files:
- components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt - + components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/media/GeckoMediaDelegate.kt - components/browser/engine-gecko/src/test/java/mozilla/components/browser/engine/gecko/GeckoEngineSessionTest.kt - + components/browser/engine-gecko/src/test/java/mozilla/components/browser/engine/gecko/media/GeckoMediaDelegateTest.kt
Changes:
===================================== components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngineSession.kt ===================================== @@ -16,6 +16,7 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import mozilla.components.browser.engine.gecko.ext.isExcludedForTrackingProtection import mozilla.components.browser.engine.gecko.fetch.toResponse +import mozilla.components.browser.engine.gecko.media.GeckoMediaDelegate import mozilla.components.browser.engine.gecko.mediasession.GeckoMediaSessionDelegate import mozilla.components.browser.engine.gecko.permission.GeckoPermissionRequest import mozilla.components.browser.engine.gecko.prompt.GeckoPromptDelegate @@ -1098,6 +1099,7 @@ class GeckoEngineSession( geckoSession.contentBlockingDelegate = createContentBlockingDelegate() geckoSession.permissionDelegate = createPermissionDelegate() geckoSession.promptDelegate = GeckoPromptDelegate(this) + geckoSession.mediaDelegate = GeckoMediaDelegate(this) geckoSession.historyDelegate = createHistoryDelegate() geckoSession.mediaSessionDelegate = GeckoMediaSessionDelegate(this) geckoSession.scrollDelegate = createScrollDelegate()
===================================== components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/media/GeckoMediaDelegate.kt ===================================== @@ -0,0 +1,53 @@ +/* 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.engine.gecko.media + +import androidx.annotation.VisibleForTesting +import mozilla.components.browser.engine.gecko.GeckoEngineSession +import mozilla.components.concept.engine.media.RecordingDevice +import org.mozilla.geckoview.GeckoSession +import java.security.InvalidParameterException +import org.mozilla.geckoview.GeckoSession.MediaDelegate.RecordingDevice as GeckoRecordingDevice + +/** + * Gecko-based GeckoMediaDelegate implementation. + */ +internal class GeckoMediaDelegate(private val geckoEngineSession: GeckoEngineSession) : + GeckoSession.MediaDelegate { + + override fun onRecordingStatusChanged( + session: GeckoSession, + geckoDevices: Array<out GeckoRecordingDevice> + ) { + val devices = geckoDevices.map { geckoRecording -> + val type = geckoRecording.toType() + val status = geckoRecording.toStatus() + RecordingDevice(type, status) + } + geckoEngineSession.notifyObservers { onRecordingStateChanged(devices) } + } +} + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +internal fun GeckoRecordingDevice.toType(): RecordingDevice.Type { + return when (type) { + GeckoRecordingDevice.Type.CAMERA -> RecordingDevice.Type.CAMERA + GeckoRecordingDevice.Type.MICROPHONE -> RecordingDevice.Type.MICROPHONE + else -> { + throw InvalidParameterException("Unexpected Gecko Media type $type status $status") + } + } +} + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +internal fun GeckoRecordingDevice.toStatus(): RecordingDevice.Status { + return when (status) { + GeckoRecordingDevice.Status.RECORDING -> RecordingDevice.Status.RECORDING + GeckoRecordingDevice.Status.INACTIVE -> RecordingDevice.Status.INACTIVE + else -> { + throw InvalidParameterException("Unexpected Gecko Media type $type status $status") + } + } +}
===================================== components/browser/engine-gecko/src/test/java/mozilla/components/browser/engine/gecko/GeckoEngineSessionTest.kt ===================================== @@ -113,6 +113,7 @@ class GeckoEngineSessionTest {
private lateinit var navigationDelegate: ArgumentCaptor<GeckoSession.NavigationDelegate> private lateinit var progressDelegate: ArgumentCaptor<GeckoSession.ProgressDelegate> + private lateinit var mediaDelegate: ArgumentCaptor<GeckoSession.MediaDelegate> private lateinit var contentDelegate: ArgumentCaptor<GeckoSession.ContentDelegate> private lateinit var permissionDelegate: ArgumentCaptor<GeckoSession.PermissionDelegate> private lateinit var contentBlockingDelegate: ArgumentCaptor<ContentBlocking.Delegate> @@ -140,6 +141,7 @@ class GeckoEngineSessionTest { whenever(runtime.settings).thenReturn(mock()) navigationDelegate = ArgumentCaptor.forClass(GeckoSession.NavigationDelegate::class.java) progressDelegate = ArgumentCaptor.forClass(GeckoSession.ProgressDelegate::class.java) + mediaDelegate = ArgumentCaptor.forClass(GeckoSession.MediaDelegate::class.java) contentDelegate = ArgumentCaptor.forClass(GeckoSession.ContentDelegate::class.java) permissionDelegate = ArgumentCaptor.forClass(GeckoSession.PermissionDelegate::class.java) contentBlockingDelegate = ArgumentCaptor.forClass(ContentBlocking.Delegate::class.java) @@ -156,6 +158,7 @@ class GeckoEngineSessionTest { verify(geckoSession).permissionDelegate = permissionDelegate.capture() verify(geckoSession).contentBlockingDelegate = contentBlockingDelegate.capture() verify(geckoSession).historyDelegate = historyDelegate.capture() + verify(geckoSession).mediaDelegate = mediaDelegate.capture() }
@Test
===================================== components/browser/engine-gecko/src/test/java/mozilla/components/browser/engine/gecko/media/GeckoMediaDelegateTest.kt ===================================== @@ -0,0 +1,113 @@ +/* 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.engine.gecko.media + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue +import junit.framework.TestCase.fail +import mozilla.components.browser.engine.gecko.GeckoEngineSession +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.media.RecordingDevice +import mozilla.components.support.test.mock +import mozilla.components.support.test.whenever +import mozilla.components.test.ReflectionUtils +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.geckoview.GeckoRuntime +import java.security.InvalidParameterException +import org.mozilla.geckoview.GeckoSession.MediaDelegate.RecordingDevice as GeckoRecordingDevice + +@RunWith(AndroidJUnit4::class) +class GeckoMediaDelegateTest { + private lateinit var runtime: GeckoRuntime + + @Before + fun setup() { + runtime = mock() + whenever(runtime.settings).thenReturn(mock()) + } + + @Test + fun `WHEN onRecordingStatusChanged is called THEN notify onRecordingStateChanged`() { + val mockSession = GeckoEngineSession(runtime) + var onRecordingWasCalled = false + val geckoRecordingDevice = createGeckoRecordingDevice( + status = GeckoRecordingDevice.Status.RECORDING, type = GeckoRecordingDevice.Type.CAMERA + ) + val gecko = GeckoMediaDelegate(mockSession) + + mockSession.register(object : EngineSession.Observer { + override fun onRecordingStateChanged(devices: List<RecordingDevice>) { + onRecordingWasCalled = true + } + }) + + gecko.onRecordingStatusChanged(mock(), arrayOf(geckoRecordingDevice)) + + assertTrue(onRecordingWasCalled) + } + + @Test + fun `GIVEN a GeckoRecordingDevice status WHEN calling toStatus THEN covert to the RecordingDevice status`() { + val geckoRecordingDevice = createGeckoRecordingDevice( + status = GeckoRecordingDevice.Status.RECORDING + ) + val geckoInactiveDevice = createGeckoRecordingDevice( + status = GeckoRecordingDevice.Status.INACTIVE + ) + + assertEquals(RecordingDevice.Status.RECORDING, geckoRecordingDevice.toStatus()) + assertEquals(RecordingDevice.Status.INACTIVE, geckoInactiveDevice.toStatus()) + } + + @Test + fun `GIVEN an invalid GeckoRecordingDevice status WHEN calling toStatus THEN throw an exception`() { + val geckoInvalidDevice = createGeckoRecordingDevice( + status = 12 + ) + try { + geckoInvalidDevice.toStatus() + fail() + } catch (_: InvalidParameterException) { + } + } + + @Test + fun `GIVEN a GeckoRecordingDevice type WHEN calling toType THEN covert to the RecordingDevice type`() { + val geckoCameraDevice = createGeckoRecordingDevice( + type = GeckoRecordingDevice.Type.CAMERA + ) + val geckoMicDevice = createGeckoRecordingDevice( + type = GeckoRecordingDevice.Type.MICROPHONE + ) + + assertEquals(RecordingDevice.Type.CAMERA, geckoCameraDevice.toType()) + assertEquals(RecordingDevice.Type.MICROPHONE, geckoMicDevice.toType()) + } + + @Test + fun `GIVEN an invalid GeckoRecordingDevice type WHEN calling toType THEN throw an exception`() { + val geckoInvalidDevice = createGeckoRecordingDevice( + type = 12 + ) + try { + geckoInvalidDevice.toType() + fail() + } catch (_: InvalidParameterException) { + } + } + + private fun createGeckoRecordingDevice( + status: Long = GeckoRecordingDevice.Status.RECORDING, + type: Long = GeckoRecordingDevice.Type.CAMERA + ): GeckoRecordingDevice { + val device: GeckoRecordingDevice = mock() + ReflectionUtils.setField(device, "status", status) + ReflectionUtils.setField(device, "type", type) + return device + } +}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/android-components/-/commit/b...