Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d02fcc3
handle preloading ads
bparrishMines Aug 27, 2025
f16ba4c
Merge branch 'main' of github.com:flutter/packages into ima_preload_fix
bparrishMines Aug 27, 2025
2f278c4
update example to test multiple ad tags
bparrishMines Aug 27, 2025
8e58c2a
add other test ad tag urls
bparrishMines Aug 27, 2025
ac3fd8f
version bump
bparrishMines Aug 27, 2025
f344f4b
formatting
bparrishMines Aug 27, 2025
e270e55
Merge branch 'main' of github.com:flutter/packages into ima_preload_fix
bparrishMines Aug 27, 2025
752a6fb
also call onPlay and onPause
bparrishMines Aug 27, 2025
85675a3
call resume if resuming
bparrishMines Aug 27, 2025
a717682
Merge branch 'main' of github.com:flutter/packages into ima_preload_fix
bparrishMines Aug 30, 2025
e75b84a
add audio focus request support
bparrishMines Aug 30, 2025
f992ce8
change onprepared to async and actually make preloading possible
bparrishMines Aug 30, 2025
b73e953
create adplayer to catch when app is sent to background
bparrishMines Aug 31, 2025
941b6bb
include title in test app screen
bparrishMines Aug 31, 2025
5a53c06
update mocks and only remove if not empty
bparrishMines Aug 31, 2025
e6b4bef
fix unit tests
bparrishMines Aug 31, 2025
18f3f5b
better changelog
bparrishMines Aug 31, 2025
25eddcb
improve code by creating helper methods
bparrishMines Aug 31, 2025
f99ed4e
move set state to in is completed
bparrishMines Aug 31, 2025
b1a1604
only clear media player
bparrishMines Aug 31, 2025
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
4 changes: 4 additions & 0 deletions packages/interactive_media_ads/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.6+7

* Updates Android `PlatformAdDisplayContainer` implementation to support preloading ads.

## 0.2.6+6

* Bumps com.android.tools.build:gradle to 8.12.1 and kotlin_version to 2.2.10.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
*
* This must match the version in pubspec.yaml.
*/
const val pluginVersion = "0.2.6+6"
const val pluginVersion = "0.2.6+7"
}

override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ private class InteractiveMediaAdsLibraryPigeonProxyApiBaseCodec(
value is AdErrorType ||
value is AdEventType ||
value is UiElement ||
value is AudioManagerAudioFocus ||
value == null) {
super.writeValue(stream, value)
return
Expand Down Expand Up @@ -956,6 +957,42 @@ enum class UiElement(val raw: Int) {
}
}

/**
* Used to indicate the type of audio focus for a view.
*
* See https://developer.android.com/reference/android/media/AudioManager#AUDIOFOCUS_GAIN.
*/
enum class AudioManagerAudioFocus(val raw: Int) {
/** Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration. */
GAIN(0),
/**
* Used to indicate a temporary gain or request of audio focus, anticipated to last a short amount
* of time.
*
* Examples of temporary changes are the playback of driving directions, or an event notification.
*/
GAIN_TRANSIENT(1),
/**
* Used to indicate a temporary request of audio focus, anticipated to last a short amount of
* time, during which no other applications, or system components, should play anything.
*/
GAIN_TRANSIENT_EXCLUSIVE(2),
/**
* Used to indicate a temporary request of audio focus, anticipated to last a short amount of
* time, and where it is acceptable for other audio applications to keep playing after having
* lowered their output level (also referred to as "ducking").
*/
GAIN_TRANSIENT_MAY_DUCK(3),
/** Used to indicate no audio focus has been gained or lost, or requested. */
NONE(4);

companion object {
fun ofRaw(raw: Int): AudioManagerAudioFocus? {
return values().firstOrNull { it.raw == raw }
}
}
}

private open class InteractiveMediaAdsLibraryPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
Expand All @@ -971,6 +1008,9 @@ private open class InteractiveMediaAdsLibraryPigeonCodec : StandardMessageCodec(
132.toByte() -> {
return (readValue(buffer) as Long?)?.let { UiElement.ofRaw(it.toInt()) }
}
133.toByte() -> {
return (readValue(buffer) as Long?)?.let { AudioManagerAudioFocus.ofRaw(it.toInt()) }
}
else -> super.readValueOfType(type, buffer)
}
}
Expand All @@ -993,6 +1033,10 @@ private open class InteractiveMediaAdsLibraryPigeonCodec : StandardMessageCodec(
stream.write(132)
writeValue(stream, value.raw)
}
is AudioManagerAudioFocus -> {
stream.write(133)
writeValue(stream, value.raw)
}
else -> super.writeValue(stream, value)
}
}
Expand Down Expand Up @@ -3709,6 +3753,17 @@ abstract class PigeonApiVideoView(
*/
abstract fun getCurrentPosition(pigeon_instance: android.widget.VideoView): Long

/**
* Sets which type of audio focus will be requested during the playback, or configures playback to
* not request audio focus.
*
* Only available on Android API 26+. Noop on lower versions.
*/
abstract fun setAudioFocusRequest(
pigeon_instance: android.widget.VideoView,
focusGain: AudioManagerAudioFocus
)

companion object {
@Suppress("LocalVariableName")
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiVideoView?) {
Expand Down Expand Up @@ -3783,6 +3838,30 @@ abstract class PigeonApiVideoView(
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.interactive_media_ads.VideoView.setAudioFocusRequest",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val pigeon_instanceArg = args[0] as android.widget.VideoView
val focusGainArg = args[1] as AudioManagerAudioFocus
val wrapped: List<Any?> =
try {
api.setAudioFocusRequest(pigeon_instanceArg, focusGainArg)
listOf(null)
} catch (exception: Throwable) {
InteractiveMediaAdsLibraryPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package dev.flutter.packages.interactive_media_ads

import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.Looper
import androidx.annotation.ChecksSdkIntAtLeast
import io.flutter.plugin.common.BinaryMessenger

/**
Expand All @@ -22,6 +24,12 @@ open class ProxyApiRegistrar(binaryMessenger: BinaryMessenger, var context: Cont
Handler(Looper.getMainLooper()).post { callback.run() }
}

// Interface for an injectable SDK version checker.
@ChecksSdkIntAtLeast(parameter = 0)
open fun sdkIsAtLeast(version: Int): Boolean {
return Build.VERSION.SDK_INT >= version
}

override fun getPigeonApiBaseDisplayContainer(): PigeonApiBaseDisplayContainer {
return BaseDisplayContainerProxyApi(this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

package dev.flutter.packages.interactive_media_ads

import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Build
import android.widget.VideoView
import androidx.core.net.toUri

Expand Down Expand Up @@ -35,4 +37,19 @@ class VideoViewProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
override fun getCurrentPosition(pigeon_instance: VideoView): Long {
return pigeon_instance.currentPosition.toLong()
}

override fun setAudioFocusRequest(pigeon_instance: VideoView, focusGain: AudioManagerAudioFocus) {
if (pigeonRegistrar.sdkIsAtLeast(Build.VERSION_CODES.O)) {
pigeon_instance.setAudioFocusRequest(
when (focusGain) {
AudioManagerAudioFocus.GAIN -> AudioManager.AUDIOFOCUS_GAIN
AudioManagerAudioFocus.GAIN_TRANSIENT -> AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
AudioManagerAudioFocus.GAIN_TRANSIENT_EXCLUSIVE ->
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
AudioManagerAudioFocus.GAIN_TRANSIENT_MAY_DUCK ->
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
AudioManagerAudioFocus.NONE -> AudioManager.AUDIOFOCUS_NONE
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ class TestProxyApiRegistrar : ProxyApiRegistrar(mock(), mock()) {
override fun runOnMainThread(callback: Runnable) {
callback.run()
}

override fun sdkIsAtLeast(version: Int): Boolean {
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package dev.flutter.packages.interactive_media_ads

import android.media.AudioManager
import android.net.Uri
import android.widget.VideoView
import kotlin.test.Test
Expand Down Expand Up @@ -35,4 +36,14 @@ class VideoViewProxyApiTest {

assertEquals(0, api.getCurrentPosition(instance))
}

@Test
fun setAudioFocusRequest() {
val api = TestProxyApiRegistrar().getPigeonApiVideoView()

val instance = mock<VideoView>()
api.setAudioFocusRequest(instance, AudioManagerAudioFocus.GAIN)

verify(instance).setAudioFocusRequest(AudioManager.AUDIOFOCUS_GAIN)
}
}
Loading