Skip to content

Commit df07929

Browse files
Mobile Ads Developer Relationscopybara-github
authored andcommitted
Add Picture-in-Picture ad support to the Google Mobile Ads Unity plugin on C# layer
PiperOrigin-RevId: 968016162
1 parent fa80d8e commit df07929

12 files changed

Lines changed: 1116 additions & 1 deletion

File tree

ChangeLog.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
Google Mobile Ads Unity Plugin Change Log
22

3+
**************
4+
Version Next
5+
**************
6+
7+
- Added Picture-in-Picture ad support.
8+
39
**************
410
Version 11.4.0
511
**************
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace GoogleMobileAds.Api
16+
{
17+
public enum PictureInPictureAdPosition
18+
{
19+
Default = 0,
20+
BottomRight = 1,
21+
BottomLeft = 2,
22+
TopLeft = 3,
23+
TopRight = 4
24+
}
25+
}
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using UnityEngine;
17+
using GoogleMobileAds.Common;
18+
19+
namespace GoogleMobileAds.Api
20+
{
21+
/// <summary>
22+
/// A Picture-in-Picture (PiP) ad that displays in a floating window across screens.
23+
/// </summary>
24+
public class PictureInPictureAd
25+
{
26+
/// <summary>
27+
/// Raised when the ad is estimated to have earned money.
28+
/// </summary>
29+
public event Action<AdValue> OnAdPaid;
30+
31+
/// <summary>
32+
/// Raised when an ad is clicked.
33+
/// </summary>
34+
public event Action OnAdClicked;
35+
36+
/// <summary>
37+
/// Raised when an impression is recorded for an ad.
38+
/// </summary>
39+
public event Action OnAdImpressionRecorded;
40+
41+
/// <summary>
42+
/// Raised when the PiP ad is displayed on the screen.
43+
/// </summary>
44+
public event Action OnAdShown;
45+
46+
/// <summary>
47+
/// Raised when the PiP ad is hidden from the screen.
48+
/// </summary>
49+
public event Action OnAdHidden;
50+
51+
/// <summary>
52+
/// Raised when an ad opened full-screen content.
53+
/// </summary>
54+
public event Action OnAdFullScreenContentOpened;
55+
56+
/// <summary>
57+
/// Raised when the ad closed full-screen content.
58+
/// </summary>
59+
public event Action OnAdFullScreenContentClosed;
60+
61+
protected internal IPictureInPictureAdClient _client;
62+
63+
protected internal PictureInPictureAd() {}
64+
65+
internal PictureInPictureAd(IPictureInPictureAdClient client)
66+
{
67+
_client = client;
68+
RegisterAdEvents();
69+
}
70+
71+
/// <summary>
72+
/// Loads a Picture-in-Picture ad.
73+
/// </summary>
74+
public static void Load(string adUnitId,
75+
AdRequest request,
76+
Action<PictureInPictureAd, LoadAdError> adLoadCallback)
77+
{
78+
if (adLoadCallback == null)
79+
{
80+
Debug.LogError("adLoadCallback is null. No ad was loaded.");
81+
return;
82+
}
83+
84+
var client = MobileAds.GetClientFactory().BuildPictureInPictureAdClient();
85+
client.CreatePictureInPictureAd();
86+
client.OnAdLoaded += (sender, args) =>
87+
{
88+
var pipAd = new PictureInPictureAd(client);
89+
MobileAds.RaiseAction(() =>
90+
{
91+
adLoadCallback(pipAd, null);
92+
});
93+
};
94+
client.OnAdFailedToLoad += (sender, error) =>
95+
{
96+
var loadAdError = (error != null && error.LoadAdErrorClient != null)
97+
? new LoadAdError(error.LoadAdErrorClient)
98+
: null;
99+
MobileAds.RaiseAction(() =>
100+
{
101+
adLoadCallback(null, loadAdError);
102+
});
103+
};
104+
client.LoadAd(adUnitId, request);
105+
}
106+
107+
/// <summary>
108+
/// Shows the Picture-in-Picture ad on the screen at the specified corner position.
109+
/// </summary>
110+
public void Show(PictureInPictureAdPosition position = PictureInPictureAdPosition.Default)
111+
{
112+
if (_client != null)
113+
{
114+
_client.Show(position);
115+
}
116+
}
117+
118+
/// <summary>
119+
/// Hides the Picture-in-Picture ad from the screen.
120+
/// </summary>
121+
public void Hide()
122+
{
123+
if (_client != null)
124+
{
125+
_client.Hide();
126+
}
127+
}
128+
129+
/// <summary>
130+
/// Destroys the Picture-in-Picture ad and cleans up native resources.
131+
/// </summary>
132+
public void Destroy()
133+
{
134+
if (_client != null)
135+
{
136+
_client.Destroy();
137+
}
138+
}
139+
140+
/// <summary>
141+
/// Returns the ResponseInfo for the loaded ad, or null if unavailable.
142+
/// </summary>
143+
public ResponseInfo GetResponseInfo()
144+
{
145+
return _client != null ? new ResponseInfo(_client.GetResponseInfoClient()) : null;
146+
}
147+
148+
/// <summary>
149+
/// Returns the current or last known position of the Picture-in-Picture ad.
150+
/// </summary>
151+
public PictureInPictureAdPosition GetAdPosition()
152+
{
153+
return _client != null ? _client.GetAdPosition() : PictureInPictureAdPosition.Default;
154+
}
155+
156+
private void RegisterAdEvents()
157+
{
158+
if (_client == null)
159+
{
160+
return;
161+
}
162+
163+
_client.OnAdShown += (sender, args) =>
164+
{
165+
MobileAds.RaiseAction(() =>
166+
{
167+
if (OnAdShown != null)
168+
{
169+
OnAdShown();
170+
}
171+
});
172+
};
173+
174+
_client.OnAdHidden += (sender, args) =>
175+
{
176+
MobileAds.RaiseAction(() =>
177+
{
178+
if (OnAdHidden != null)
179+
{
180+
OnAdHidden();
181+
}
182+
});
183+
};
184+
185+
_client.OnAdClicked += () =>
186+
{
187+
MobileAds.RaiseAction(() =>
188+
{
189+
if (OnAdClicked != null)
190+
{
191+
OnAdClicked();
192+
}
193+
});
194+
};
195+
196+
_client.OnAdDidRecordImpression += (sender, args) =>
197+
{
198+
MobileAds.RaiseAction(() =>
199+
{
200+
if (OnAdImpressionRecorded != null)
201+
{
202+
OnAdImpressionRecorded();
203+
}
204+
});
205+
};
206+
207+
_client.OnAdDidPresentFullScreenContent += (sender, args) =>
208+
{
209+
MobileAds.RaiseAction(() =>
210+
{
211+
if (OnAdFullScreenContentOpened != null)
212+
{
213+
OnAdFullScreenContentOpened();
214+
}
215+
});
216+
};
217+
218+
_client.OnAdDidDismissFullScreenContent += (sender, args) =>
219+
{
220+
MobileAds.RaiseAction(() =>
221+
{
222+
if (OnAdFullScreenContentClosed != null)
223+
{
224+
OnAdFullScreenContentClosed();
225+
}
226+
});
227+
};
228+
229+
_client.OnPaidEvent += (adValue) =>
230+
{
231+
MobileAds.RaiseAction(() =>
232+
{
233+
if (OnAdPaid != null)
234+
{
235+
OnAdPaid(adValue);
236+
}
237+
});
238+
};
239+
}
240+
}
241+
}

source/plugin/Assets/GoogleMobileAds/Common/IClientFactory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public interface IClientFactory
3838

3939
INativeOverlayAdClient BuildNativeOverlayAdClient();
4040

41+
IPictureInPictureAdClient BuildPictureInPictureAdClient();
42+
4143
IApplicationPreferencesClient ApplicationPreferencesInstance();
4244

4345
IMobileAdsClient MobileAdsInstance();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using GoogleMobileAds.Api;
17+
18+
namespace GoogleMobileAds.Common
19+
{
20+
public interface IPictureInPictureAdClient
21+
{
22+
// Ad event fired when the picture in picture ad has loaded.
23+
event EventHandler<EventArgs> OnAdLoaded;
24+
// Ad event fired when the picture in picture ad has failed to load.
25+
event EventHandler<LoadAdErrorClientEventArgs> OnAdFailedToLoad;
26+
// Ad event fired when the picture in picture ad is displayed on screen.
27+
event EventHandler<EventArgs> OnAdShown;
28+
// Ad event fired when the picture in picture ad is hidden from screen.
29+
event EventHandler<EventArgs> OnAdHidden;
30+
// Ad event fired when an ad impression has been recorded.
31+
event EventHandler<EventArgs> OnAdDidRecordImpression;
32+
// Ad event fired when an ad has been clicked.
33+
event Action OnAdClicked;
34+
// Ad event fired when the ad opens an overlay covering the screen.
35+
event EventHandler<EventArgs> OnAdDidPresentFullScreenContent;
36+
// Ad event fired when the ad overlay is dismissed.
37+
event EventHandler<EventArgs> OnAdDidDismissFullScreenContent;
38+
// Ad event fired when the picture in picture ad is estimated to have earned money.
39+
event Action<AdValue> OnPaidEvent;
40+
41+
// Creates a picture in picture ad wrapper.
42+
void CreatePictureInPictureAd();
43+
44+
// Loads a picture in picture ad.
45+
void LoadAd(string adUnitId, AdRequest request);
46+
47+
// Shows the picture in picture ad on screen.
48+
void Show(PictureInPictureAdPosition position);
49+
50+
// Hides the picture in picture ad.
51+
void Hide();
52+
53+
// Destroys the picture in picture ad.
54+
void Destroy();
55+
56+
// Returns the current or last known position of the picture in picture ad.
57+
PictureInPictureAdPosition GetAdPosition();
58+
59+
// Returns the response info for the loaded ad.
60+
IResponseInfoClient GetResponseInfoClient();
61+
}
62+
}

source/plugin/Assets/GoogleMobileAds/Platforms/Android/GoogleMobileAdsClientFactory.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ public INativeOverlayAdClient BuildNativeOverlayAdClient()
146146
" on non-Android runtime");
147147
}
148148

149+
public IPictureInPictureAdClient BuildPictureInPictureAdClient()
150+
{
151+
if (Application.platform == RuntimePlatform.Android)
152+
{
153+
return new GoogleMobileAds.Android.PictureInPictureAdClient();
154+
}
155+
throw new InvalidOperationException(@"Called " + MethodBase.GetCurrentMethod().Name +
156+
" on non-Android runtime");
157+
}
158+
149159
public IApplicationPreferencesClient ApplicationPreferencesInstance() {
150160
if (Application.platform == RuntimePlatform.Android) {
151161
return new GoogleMobileAds.Android.ApplicationPreferencesClient();
@@ -180,7 +190,7 @@ private bool IsNextGenEnabled()
180190
new AndroidJavaClass(GoogleMobileAds.Android.NextGenUtils.MobileAdsClassName);
181191
_nextGenEnabled = true;
182192
}
183-
catch (AndroidJavaException)
193+
catch (Exception)
184194
{
185195
_nextGenEnabled = false;
186196
}

source/plugin/Assets/GoogleMobileAds/Platforms/Android/NextGenUtils.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ internal class NextGenUtils {
120120
public const string UnityBannerAdCallbackClassName =
121121
"com.google.unity.ads.nextgen.UnityBannerAdCallback";
122122

123+
public const string UnityPictureInPictureAdClassName =
124+
"com.google.unity.ads.nextgen.UnityPictureInPictureAd";
125+
public const string UnityPictureInPictureAdCallbackClassName =
126+
"com.google.unity.ads.nextgen.UnityPictureInPictureAdCallback";
127+
123128
public const string UnityInterstitialAdClassName =
124129
"com.google.unity.ads.nextgen.UnityInterstitialAd";
125130
public const string UnityInterstitialAdCallbackClassName =

0 commit comments

Comments
 (0)