diff --git a/ChangeLog.txt b/ChangeLog.txt
index 209abbd01..918fb38d5 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,5 +1,11 @@
Google Mobile Ads Unity Plugin Change Log
+**************
+Version Next
+**************
+
+- Added Picture-in-Picture ad support.
+
**************
Version 11.4.0
**************
diff --git a/source/plugin/Assets/GoogleMobileAds/Api/Core/PictureInPictureAdPosition.cs b/source/plugin/Assets/GoogleMobileAds/Api/Core/PictureInPictureAdPosition.cs
new file mode 100644
index 000000000..9f9b5fb16
--- /dev/null
+++ b/source/plugin/Assets/GoogleMobileAds/Api/Core/PictureInPictureAdPosition.cs
@@ -0,0 +1,25 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace GoogleMobileAds.Api
+{
+ public enum PictureInPictureAdPosition
+ {
+ Default = 0,
+ BottomRight = 1,
+ BottomLeft = 2,
+ TopLeft = 3,
+ TopRight = 4
+ }
+}
diff --git a/source/plugin/Assets/GoogleMobileAds/Api/PictureInPictureAd.cs b/source/plugin/Assets/GoogleMobileAds/Api/PictureInPictureAd.cs
new file mode 100644
index 000000000..d12ea96fb
--- /dev/null
+++ b/source/plugin/Assets/GoogleMobileAds/Api/PictureInPictureAd.cs
@@ -0,0 +1,241 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using UnityEngine;
+using GoogleMobileAds.Common;
+
+namespace GoogleMobileAds.Api
+{
+ ///
+ /// A Picture-in-Picture (PiP) ad that displays in a floating window across screens.
+ ///
+ public class PictureInPictureAd
+ {
+ ///
+ /// Raised when the ad is estimated to have earned money.
+ ///
+ public event Action OnAdPaid;
+
+ ///
+ /// Raised when an ad is clicked.
+ ///
+ public event Action OnAdClicked;
+
+ ///
+ /// Raised when an impression is recorded for an ad.
+ ///
+ public event Action OnAdImpressionRecorded;
+
+ ///
+ /// Raised when the PiP ad is displayed on the screen.
+ ///
+ public event Action OnAdShown;
+
+ ///
+ /// Raised when the PiP ad is hidden from the screen.
+ ///
+ public event Action OnAdHidden;
+
+ ///
+ /// Raised when an ad opened full-screen content.
+ ///
+ public event Action OnAdFullScreenContentOpened;
+
+ ///
+ /// Raised when the ad closed full-screen content.
+ ///
+ public event Action OnAdFullScreenContentClosed;
+
+ protected internal IPictureInPictureAdClient _client;
+
+ protected internal PictureInPictureAd() {}
+
+ internal PictureInPictureAd(IPictureInPictureAdClient client)
+ {
+ _client = client;
+ RegisterAdEvents();
+ }
+
+ ///
+ /// Loads a Picture-in-Picture ad.
+ ///
+ public static void Load(string adUnitId,
+ AdRequest request,
+ Action adLoadCallback)
+ {
+ if (adLoadCallback == null)
+ {
+ Debug.LogError("adLoadCallback is null. No ad was loaded.");
+ return;
+ }
+
+ var client = MobileAds.GetClientFactory().BuildPictureInPictureAdClient();
+ client.CreatePictureInPictureAd();
+ client.OnAdLoaded += (sender, args) =>
+ {
+ var pipAd = new PictureInPictureAd(client);
+ MobileAds.RaiseAction(() =>
+ {
+ adLoadCallback(pipAd, null);
+ });
+ };
+ client.OnAdFailedToLoad += (sender, error) =>
+ {
+ var loadAdError = (error != null && error.LoadAdErrorClient != null)
+ ? new LoadAdError(error.LoadAdErrorClient)
+ : null;
+ MobileAds.RaiseAction(() =>
+ {
+ adLoadCallback(null, loadAdError);
+ });
+ };
+ client.LoadAd(adUnitId, request);
+ }
+
+ ///
+ /// Shows the Picture-in-Picture ad on the screen at the specified corner position.
+ ///
+ public void Show(PictureInPictureAdPosition position = PictureInPictureAdPosition.Default)
+ {
+ if (_client != null)
+ {
+ _client.Show(position);
+ }
+ }
+
+ ///
+ /// Hides the Picture-in-Picture ad from the screen.
+ ///
+ public void Hide()
+ {
+ if (_client != null)
+ {
+ _client.Hide();
+ }
+ }
+
+ ///
+ /// Destroys the Picture-in-Picture ad and cleans up native resources.
+ ///
+ public void Destroy()
+ {
+ if (_client != null)
+ {
+ _client.Destroy();
+ }
+ }
+
+ ///
+ /// Returns the ResponseInfo for the loaded ad, or null if unavailable.
+ ///
+ public ResponseInfo GetResponseInfo()
+ {
+ return _client != null ? new ResponseInfo(_client.GetResponseInfoClient()) : null;
+ }
+
+ ///
+ /// Returns the current or last known position of the Picture-in-Picture ad.
+ ///
+ public PictureInPictureAdPosition GetAdPosition()
+ {
+ return _client != null ? _client.GetAdPosition() : PictureInPictureAdPosition.Default;
+ }
+
+ private void RegisterAdEvents()
+ {
+ if (_client == null)
+ {
+ return;
+ }
+
+ _client.OnAdShown += (sender, args) =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdShown != null)
+ {
+ OnAdShown();
+ }
+ });
+ };
+
+ _client.OnAdHidden += (sender, args) =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdHidden != null)
+ {
+ OnAdHidden();
+ }
+ });
+ };
+
+ _client.OnAdClicked += () =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdClicked != null)
+ {
+ OnAdClicked();
+ }
+ });
+ };
+
+ _client.OnAdDidRecordImpression += (sender, args) =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdImpressionRecorded != null)
+ {
+ OnAdImpressionRecorded();
+ }
+ });
+ };
+
+ _client.OnAdDidPresentFullScreenContent += (sender, args) =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdFullScreenContentOpened != null)
+ {
+ OnAdFullScreenContentOpened();
+ }
+ });
+ };
+
+ _client.OnAdDidDismissFullScreenContent += (sender, args) =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdFullScreenContentClosed != null)
+ {
+ OnAdFullScreenContentClosed();
+ }
+ });
+ };
+
+ _client.OnPaidEvent += (adValue) =>
+ {
+ MobileAds.RaiseAction(() =>
+ {
+ if (OnAdPaid != null)
+ {
+ OnAdPaid(adValue);
+ }
+ });
+ };
+ }
+ }
+}
diff --git a/source/plugin/Assets/GoogleMobileAds/Common/IClientFactory.cs b/source/plugin/Assets/GoogleMobileAds/Common/IClientFactory.cs
index 37c2e0f7d..df2a08b86 100644
--- a/source/plugin/Assets/GoogleMobileAds/Common/IClientFactory.cs
+++ b/source/plugin/Assets/GoogleMobileAds/Common/IClientFactory.cs
@@ -38,6 +38,8 @@ public interface IClientFactory
INativeOverlayAdClient BuildNativeOverlayAdClient();
+ IPictureInPictureAdClient BuildPictureInPictureAdClient();
+
IApplicationPreferencesClient ApplicationPreferencesInstance();
IMobileAdsClient MobileAdsInstance();
diff --git a/source/plugin/Assets/GoogleMobileAds/Common/IPictureInPictureAdClient.cs b/source/plugin/Assets/GoogleMobileAds/Common/IPictureInPictureAdClient.cs
new file mode 100644
index 000000000..b6a8fdb22
--- /dev/null
+++ b/source/plugin/Assets/GoogleMobileAds/Common/IPictureInPictureAdClient.cs
@@ -0,0 +1,62 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using GoogleMobileAds.Api;
+
+namespace GoogleMobileAds.Common
+{
+ public interface IPictureInPictureAdClient
+ {
+ // Ad event fired when the picture in picture ad has loaded.
+ event EventHandler OnAdLoaded;
+ // Ad event fired when the picture in picture ad has failed to load.
+ event EventHandler OnAdFailedToLoad;
+ // Ad event fired when the picture in picture ad is displayed on screen.
+ event EventHandler OnAdShown;
+ // Ad event fired when the picture in picture ad is hidden from screen.
+ event EventHandler OnAdHidden;
+ // Ad event fired when an ad impression has been recorded.
+ event EventHandler OnAdDidRecordImpression;
+ // Ad event fired when an ad has been clicked.
+ event Action OnAdClicked;
+ // Ad event fired when the ad opens an overlay covering the screen.
+ event EventHandler OnAdDidPresentFullScreenContent;
+ // Ad event fired when the ad overlay is dismissed.
+ event EventHandler OnAdDidDismissFullScreenContent;
+ // Ad event fired when the picture in picture ad is estimated to have earned money.
+ event Action OnPaidEvent;
+
+ // Creates a picture in picture ad wrapper.
+ void CreatePictureInPictureAd();
+
+ // Loads a picture in picture ad.
+ void LoadAd(string adUnitId, AdRequest request);
+
+ // Shows the picture in picture ad on screen.
+ void Show(PictureInPictureAdPosition position);
+
+ // Hides the picture in picture ad.
+ void Hide();
+
+ // Destroys the picture in picture ad.
+ void Destroy();
+
+ // Returns the current or last known position of the picture in picture ad.
+ PictureInPictureAdPosition GetAdPosition();
+
+ // Returns the response info for the loaded ad.
+ IResponseInfoClient GetResponseInfoClient();
+ }
+}
diff --git a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/GoogleMobileAdsClientFactory.cs b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/GoogleMobileAdsClientFactory.cs
index b51a39967..f0d243169 100644
--- a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/GoogleMobileAdsClientFactory.cs
+++ b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/GoogleMobileAdsClientFactory.cs
@@ -146,6 +146,16 @@ public INativeOverlayAdClient BuildNativeOverlayAdClient()
" on non-Android runtime");
}
+ public IPictureInPictureAdClient BuildPictureInPictureAdClient()
+ {
+ if (Application.platform == RuntimePlatform.Android)
+ {
+ return new GoogleMobileAds.Android.PictureInPictureAdClient();
+ }
+ throw new InvalidOperationException(@"Called " + MethodBase.GetCurrentMethod().Name +
+ " on non-Android runtime");
+ }
+
public IApplicationPreferencesClient ApplicationPreferencesInstance() {
if (Application.platform == RuntimePlatform.Android) {
return new GoogleMobileAds.Android.ApplicationPreferencesClient();
@@ -180,7 +190,7 @@ private bool IsNextGenEnabled()
new AndroidJavaClass(GoogleMobileAds.Android.NextGenUtils.MobileAdsClassName);
_nextGenEnabled = true;
}
- catch (AndroidJavaException)
+ catch (Exception)
{
_nextGenEnabled = false;
}
diff --git a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/NextGenUtils.cs b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/NextGenUtils.cs
index 42305e04f..3f062a486 100644
--- a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/NextGenUtils.cs
+++ b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/NextGenUtils.cs
@@ -120,6 +120,11 @@ internal class NextGenUtils {
public const string UnityBannerAdCallbackClassName =
"com.google.unity.ads.nextgen.UnityBannerAdCallback";
+ public const string UnityPictureInPictureAdClassName =
+ "com.google.unity.ads.nextgen.UnityPictureInPictureAd";
+ public const string UnityPictureInPictureAdCallbackClassName =
+ "com.google.unity.ads.nextgen.UnityPictureInPictureAdCallback";
+
public const string UnityInterstitialAdClassName =
"com.google.unity.ads.nextgen.UnityInterstitialAd";
public const string UnityInterstitialAdCallbackClassName =
diff --git a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/PictureInPictureAdClient.cs b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/PictureInPictureAdClient.cs
new file mode 100644
index 000000000..03398fcdf
--- /dev/null
+++ b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/PictureInPictureAdClient.cs
@@ -0,0 +1,166 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using GoogleMobileAds.Api;
+using GoogleMobileAds.Common;
+using UnityEngine;
+
+namespace GoogleMobileAds.Android
+{
+ public class PictureInPictureAdClient : AndroidJavaProxy, IPictureInPictureAdClient
+ {
+ internal AndroidJavaObject androidPictureInPictureAd;
+
+ public PictureInPictureAdClient() : base(NextGenUtils.UnityPictureInPictureAdCallbackClassName)
+ {
+ AndroidJavaClass playerClass = new AndroidJavaClass(Utils.UnityActivityClassName);
+ AndroidJavaObject activity = playerClass.GetStatic("currentActivity");
+ this.androidPictureInPictureAd = new AndroidJavaObject(
+ NextGenUtils.UnityPictureInPictureAdClassName, activity, this);
+ }
+
+ public event EventHandler OnAdLoaded;
+ public event EventHandler OnAdFailedToLoad;
+ public event EventHandler OnAdShown;
+ public event EventHandler OnAdHidden;
+ public event EventHandler OnAdDidRecordImpression;
+ public event Action OnAdClicked;
+ public event EventHandler OnAdDidPresentFullScreenContent;
+ public event EventHandler OnAdDidDismissFullScreenContent;
+ public event Action OnPaidEvent;
+
+ public void CreatePictureInPictureAd()
+ {
+ // No-op for Next-Gen.
+ }
+
+ public void LoadAd(string adUnitId, AdRequest request)
+ {
+ this.androidPictureInPictureAd.Call("load", adUnitId, NextGenUtils.GetAdRequestJavaObject(request, adUnitId));
+ }
+
+ public void Show(PictureInPictureAdPosition position)
+ {
+ this.androidPictureInPictureAd.Call("show", (int)position);
+ }
+
+ public void Hide()
+ {
+ this.androidPictureInPictureAd.Call("hide");
+ }
+
+ public void Destroy()
+ {
+ this.androidPictureInPictureAd.Call("destroy");
+ }
+
+ public PictureInPictureAdPosition GetAdPosition()
+ {
+ int ordinal = this.androidPictureInPictureAd.Call("getAdPosition");
+ return (PictureInPictureAdPosition)ordinal;
+ }
+
+ public IResponseInfoClient GetResponseInfoClient()
+ {
+ return new ResponseInfoClient(ResponseInfoClientType.AdLoaded, this.androidPictureInPictureAd);
+ }
+
+ #region Callback methods called from Java via AndroidJavaProxy
+
+ public void onAdLoaded()
+ {
+ if (this.OnAdLoaded != null)
+ {
+ this.OnAdLoaded(this, EventArgs.Empty);
+ }
+ }
+
+ public void onAdFailedToLoad(AndroidJavaObject error)
+ {
+ if (this.OnAdFailedToLoad != null)
+ {
+ LoadAdErrorClientEventArgs args = new LoadAdErrorClientEventArgs
+ {
+ LoadAdErrorClient = new NextGenLoadAdErrorClient(error)
+ };
+ this.OnAdFailedToLoad(this, args);
+ }
+ }
+
+ public void onAdShown()
+ {
+ if (this.OnAdShown != null)
+ {
+ this.OnAdShown(this, EventArgs.Empty);
+ }
+ }
+
+ public void onAdHidden()
+ {
+ if (this.OnAdHidden != null)
+ {
+ this.OnAdHidden(this, EventArgs.Empty);
+ }
+ }
+
+ public void onAdClicked()
+ {
+ if (this.OnAdClicked != null)
+ {
+ this.OnAdClicked();
+ }
+ }
+
+ public void onAdImpression()
+ {
+ if (this.OnAdDidRecordImpression != null)
+ {
+ this.OnAdDidRecordImpression(this, EventArgs.Empty);
+ }
+ }
+
+ public void onAdShowedFullScreenContent()
+ {
+ if (this.OnAdDidPresentFullScreenContent != null)
+ {
+ this.OnAdDidPresentFullScreenContent(this, EventArgs.Empty);
+ }
+ }
+
+ public void onAdDismissedFullScreenContent()
+ {
+ if (this.OnAdDidDismissFullScreenContent != null)
+ {
+ this.OnAdDidDismissFullScreenContent(this, EventArgs.Empty);
+ }
+ }
+
+ public void onPaidEvent(int precision, long valueMicros, string currencyCode)
+ {
+ if (this.OnPaidEvent != null)
+ {
+ AdValue adValue = new AdValue
+ {
+ Precision = (AdValue.PrecisionType)precision,
+ Value = valueMicros,
+ CurrencyCode = currencyCode
+ };
+ this.OnPaidEvent(adValue);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/GoogleMobileAdsClientFactory.cs b/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/GoogleMobileAdsClientFactory.cs
index aab163f16..c0379d60c 100644
--- a/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/GoogleMobileAdsClientFactory.cs
+++ b/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/GoogleMobileAdsClientFactory.cs
@@ -78,6 +78,11 @@ public IMobileAdsClient MobileAdsInstance()
return new GoogleMobileAds.Unity.MobileAdsClient();
}
+ public IPictureInPictureAdClient BuildPictureInPictureAdClient()
+ {
+ return new GoogleMobileAds.Unity.PictureInPictureAdClient();
+ }
+
#if GMA_PREVIEW_FEATURES
public IAppOpenAdPreloaderClient BuildAppOpenAdPreloaderClient() {
diff --git a/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/PictureInPictureAdClient.cs b/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/PictureInPictureAdClient.cs
new file mode 100644
index 000000000..5194aab01
--- /dev/null
+++ b/source/plugin/Assets/GoogleMobileAds/Platforms/Unity/PictureInPictureAdClient.cs
@@ -0,0 +1,779 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityEngine.UI;
+using GoogleMobileAds.Api;
+using GoogleMobileAds.Common;
+
+namespace GoogleMobileAds.Unity
+{
+ public class PictureInPictureAdClient : IPictureInPictureAdClient
+ {
+ public event EventHandler OnAdLoaded;
+ public event EventHandler OnAdFailedToLoad;
+ public event EventHandler OnAdShown;
+ public event EventHandler OnAdHidden;
+ public event EventHandler OnAdDidRecordImpression;
+ public event Action OnAdClicked;
+ public event EventHandler OnAdDidPresentFullScreenContent;
+ public event EventHandler OnAdDidDismissFullScreenContent;
+ public event Action OnPaidEvent;
+
+ private GameObject _gameObject;
+ private RectTransform _cardRect;
+ private PipSimulatorComponent _simulatorComponent;
+ private string _adUnitId;
+ private PictureInPictureAdPosition _currentPosition = PictureInPictureAdPosition.Default;
+ private bool _isLoaded = false;
+ private bool _didImpress = false;
+
+ public void CreatePictureInPictureAd()
+ {
+ // No-op for Unity Editor simulator.
+ }
+
+ public void LoadAd(string adUnitId, AdRequest request)
+ {
+ _adUnitId = adUnitId;
+ if (string.IsNullOrEmpty(adUnitId) || request == null)
+ {
+ if (OnAdFailedToLoad != null)
+ {
+ OnAdFailedToLoad.Invoke(this, new LoadAdErrorClientEventArgs
+ {
+ LoadAdErrorClient = new LoadAdErrorClient()
+ });
+ }
+ return;
+ }
+
+ GameObject prefab = Resources.Load("PlaceholderAds/Banners/MEDIUM_RECTANGLE") as GameObject;
+ if (prefab == null)
+ {
+ prefab = Resources.Load("PlaceholderAds/Banners/BANNER") as GameObject;
+ }
+ if (prefab == null)
+ {
+ Debug.LogError("No PiP placeholder prefab found in Resources.");
+ if (OnAdFailedToLoad != null)
+ {
+ OnAdFailedToLoad.Invoke(this, new LoadAdErrorClientEventArgs
+ {
+ LoadAdErrorClient = new LoadAdErrorClient()
+ });
+ }
+ return;
+ }
+
+ _gameObject = GameObject.Instantiate(prefab);
+ Canvas canvas = _gameObject.GetComponent