「Android Integeration with AdMob Mediation」修訂間的差異
| imported>Wikiuser | imported>Wikiuser  | ||
| 行 904: | 行 904: | ||
| :::<p style="font-size: 18px;">藍色為TAmedia SDK原生廣告類別, 黃色為AdMob SDK原生廣告類別</b></p> | :::<p style="font-size: 18px;">藍色為TAmedia SDK原生廣告類別, 黃色為AdMob SDK原生廣告類別</b></p> | ||
| :::<p style="font-size: 18px;">開發者僅需在native_ad.xml中將TAmedia TWMNativeAdView添加至AdMob NativeAdView之上即可</p> | :::<p style="font-size: 18px;">開發者僅需在native_ad.xml中將TAmedia TWMNativeAdView添加至AdMob NativeAdView之上即可</p> | ||
| :::<p style="font-size: 18px;">佈局中如果有發現AdMob SDK MediaView, TAmedia SDK TWMMediaView則會主動覆蓋至上面, 負責顯示影音廣告</p> | |||
| [[ image:AdMob_Mediation_SI.png | 1000px ]] | [[ image:AdMob_Mediation_SI.png | 1000px ]] | ||
於 2022年2月11日 (五) 13:21 的修訂
詳細範例:
- AdMob 20.5.0 Mediation Project (包含橫幅、插頁、原生廣告)。  
新增應用程式
AdMob網站: https://apps.admob.com/
新增廣告單元
新增中介服務群組
- 進入 AdMob 網頁選擇 中介服務 > 新增中介服務群組 
- 設定廣告格式、平台 
- 設定中介群組名稱並點選加入廣告單元 
- 選擇先前建立的廣告單元 (以橫幅廣告為例) 
- 
   點選新增自訂事件,並設定下列相關資訊: 有效千次曝光出價: 越高代表該家聯播網廣告曝光的機會越高 Class Name: 根據您的專案輸入 mediation code 的 package name,如: com.taiwanmobile.pt.adp.mediation.TAMediaBanner (下方實作 Mediation Code章節會說明Mediation Code的實作方式) Parameter: 輸入您的 TAmedia 廣告版位ID 
- 點選完成後,中介服務群組的設置便結束 
 
 
TAmedia廣告版位ID
程式整合
- 1. 請參考AdMob for Android 網站提供的方式整合Google Ads SDK,此步驟會使用到新增應用程式所取得的應用程式ID 
- 2. 下載 TAMedia Android SDK,並參考 開始使用Getting Started 整合TAMedia Android SDK 
- 2. 下載AdMob 20.5.0 Mediation Project,並整合下列Mediation Code進開發者專案中 
Mediation Code
Banner
- 依照下列方式實作AdMob所提供的CustomEventBanner 
package com.taiwanmobile.pt.adp.mediation import android.app.Activity import android.content.Context import android.os.Bundle import android.util.Log import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdSize import com.google.android.gms.ads.mediation.MediationAdRequest import com.google.android.gms.ads.mediation.customevent.CustomEventBanner import com.google.android.gms.ads.mediation.customevent.CustomEventBannerListener import com.taiwanmobile.pt.adp.view.* // 需將本class的完整類別名稱 com.taiwanmobile.pt.adp.mediation.TAMediaBanner註冊於中介服務群組, 如 新增中介服務群組 所述 class TAMediaBanner : CustomEventBanner { private var adView: TWMAdView? = null override fun requestBannerAd( context: Context, customEventBannerListener: CustomEventBannerListener, serverParameter: String?, adSize: AdSize, mediationAdRequest: MediationAdRequest, bundle: Bundle? ) { // serverParameter: AdMob 後台設定的 TAMedia Slot ID, 會藉由此參數獲得 Log.d(TAG, "requestBannerAd($serverParameter) invoked!!") adView = TWMAdView(context as Activity, convertAdSize(adSize), serverParameter) adView?.adListener = object : TWMAdViewListener { override fun onReceiveAd(ad: TWMAd) { customEventBannerListener.onAdLoaded(adView) } override fun onFailedToReceiveAd(ad: TWMAd, errorCode: TWMAdRequest.ErrorCode) { customEventBannerListener.onAdFailedToLoad( convertErrorCode(errorCode) ) } override fun onPresentScreen(ad: TWMAd) { customEventBannerListener.onAdOpened() } override fun onDismissScreen(ad: TWMAd) { customEventBannerListener.onAdClosed() } override fun onLeaveApplication(ad: TWMAd) { customEventBannerListener.onAdClicked() customEventBannerListener.onAdLeftApplication() } } adView?.loadAd(TWMAdRequest()) } override fun onDestroy() { adView?.destroy() } override fun onPause() {} override fun onResume() {} //將AdMob的AdSize轉換為TWMAdSize private fun convertAdSize(adSize: AdSize): TWMAdSize { return when { isAdSizeEqual(adSize, TWMAdSize.BANNER) -> { TWMAdSize.BANNER } isAdSizeEqual(adSize, TWMAdSize.BANNER_1200X627) -> { TWMAdSize.BANNER_1200X627 } isAdSizeEqual(adSize, TWMAdSize.BANNER_300X250) -> { TWMAdSize.BANNER_300X250 } isAdSizeEqual(adSize, TWMAdSize.SMART_BANNER) -> { TWMAdSize.SMART_BANNER } else -> { TWMAdSize.BANNER } } } private fun isAdSizeEqual(adSize: AdSize, twmAdSize: TWMAdSize): Boolean { return (adSize.width == twmAdSize.width && adSize.height == twmAdSize.height) } //將TAMedia ErrorCode轉換為AdMob的ErrorCode private fun convertErrorCode(errorCode: TWMAdRequest.ErrorCode): Int { return when (errorCode) { TWMAdRequest.ErrorCode.INTERNAL_ERROR -> { AdRequest.ERROR_CODE_INTERNAL_ERROR } TWMAdRequest.ErrorCode.INVALID_REQUEST -> { AdRequest.ERROR_CODE_INVALID_REQUEST } TWMAdRequest.ErrorCode.NETWORK_ERROR -> { AdRequest.ERROR_CODE_NETWORK_ERROR } TWMAdRequest.ErrorCode.NO_FILL -> { AdRequest.ERROR_CODE_NO_FILL } else -> AdRequest.ERROR_CODE_NETWORK_ERROR } } companion object { private const val TAG = "TAMediaBanner" } }
Interstitial
- 依照下列方式實作AdMob所提供的CustomEventInterstitial 
package com.taiwanmobile.pt.adp.mediation import android.app.Activity import android.content.Context import android.os.Bundle import android.util.Log import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.mediation.MediationAdRequest import com.google.android.gms.ads.mediation.customevent.CustomEventInterstitial import com.google.android.gms.ads.mediation.customevent.CustomEventInterstitialListener import com.taiwanmobile.pt.adp.view.TWMAd import com.taiwanmobile.pt.adp.view.TWMAdRequest import com.taiwanmobile.pt.adp.view.TWMAdViewListener import com.taiwanmobile.pt.adp.view.TWMInterstitialAd // 需將本class的完整類別名稱 com.taiwanmobile.pt.adp.mediation.TAMediaInterstitial註冊於中介服務群組, 如 新增中介服務群組 所述 class TAMediaInterstitial : CustomEventInterstitial { private var interstitialAd: TWMInterstitialAd? = null override fun requestInterstitialAd( context: Context, customEventInterstitialListener: CustomEventInterstitialListener, serverParameter: String?, mediationAdRequest: MediationAdRequest, bundle: Bundle? ) { // serverParameter: AdMob 後台設定的 TAMedia Slot ID, 會藉由此參數獲得 Log.d(TAG, "requestInterstitialAd($serverParameter) ") interstitialAd = TWMInterstitialAd(context as Activity, serverParameter as String) interstitialAd?.setAdListener(object : TWMAdViewListener { override fun onReceiveAd(ad: TWMAd) { customEventInterstitialListener.onAdLoaded() } override fun onFailedToReceiveAd(ad: TWMAd, errorCode: TWMAdRequest.ErrorCode) { customEventInterstitialListener.onAdFailedToLoad( convertErrorCode(errorCode) ) } override fun onPresentScreen(ad: TWMAd) { customEventInterstitialListener.onAdOpened() } override fun onDismissScreen(ad: TWMAd) { customEventInterstitialListener.onAdClosed() } override fun onLeaveApplication(ad: TWMAd) { customEventInterstitialListener.onAdClicked() customEventInterstitialListener.onAdLeftApplication() } }) interstitialAd?.loadAd(TWMAdRequest()) } override fun showInterstitial() { interstitialAd?.show() } override fun onDestroy() {} override fun onPause() {} override fun onResume() {} // 將TAMedia ErrorCode轉換為AdMob的ErrorCode private fun convertErrorCode(errorCode: TWMAdRequest.ErrorCode): Int { return when (errorCode) { TWMAdRequest.ErrorCode.INTERNAL_ERROR -> { AdRequest.ERROR_CODE_INTERNAL_ERROR } TWMAdRequest.ErrorCode.INVALID_REQUEST -> { AdRequest.ERROR_CODE_INVALID_REQUEST } TWMAdRequest.ErrorCode.NETWORK_ERROR -> { AdRequest.ERROR_CODE_NETWORK_ERROR } TWMAdRequest.ErrorCode.NO_FILL -> { AdRequest.ERROR_CODE_NO_FILL } else -> AdRequest.ERROR_CODE_NETWORK_ERROR } } companion object { private const val TAG = "TAMediaInterstitial" } }
Native
- 依照下列方式實作AdMob所提供的CustomEventNative - 從AdMob廣告設定serverParameter得知TAmedia原生廣告版位, 進而對TAmedia SDK請求原生廣告, 詳情可參考 TAmedia廣告版位ID 
- 下列為附加功能(Optional), 預設關閉, 如需使用再將屬性設為true, 詳情可參考 TWMNativeAdOptions 
- videoStartUnmuted: 影片預設開啟聲音 
- disableImageLoading: 取消圖片下載, 加速廣告請求 
- videoCustomControlRequest: 取消影片內控制項 
- mediaPreferImage: TWMMediaView改呈現圖片 
 
package com.taiwanmobile.pt.adp.mediation import android.content.Context import android.os.Bundle import com.google.android.gms.ads.mediation.NativeMediationAdRequest import com.google.android.gms.ads.mediation.customevent.CustomEventNative import com.google.android.gms.ads.mediation.customevent.CustomEventNativeListener import com.taiwanmobile.pt.adp.mediation.native.TANativeEventForwarder import com.taiwanmobile.pt.adp.nativead.TWMNativeAdOptions import com.taiwanmobile.pt.adp.view.TWMAdRequest import com.taiwanmobile.pt.adp.view.TWMNativeAd // 需將本class的完整類別名稱 com.taiwanmobile.pt.adp.mediation.TAmediaNative註冊於中介服務群組, 如 新增中介服務群組 所述 class TAmediaNative : CustomEventNative { private var twmNativeAd: TWMNativeAd? = null // optional. Set true when the feature below is needed. // http://wiki.tamedia.com.tw/androidDoc/library/com.taiwanmobile.pt.adp.nativead/-t-w-m-native-ad-options/index.html private val videoStartUnmuted = false private val disableImageLoading = false private val videoCustomControlRequest = false private val mediaPreferImage = false override fun onDestroy() { twmNativeAd?.destroy() } override fun onPause() {} override fun onResume() {} override fun requestNativeAd( context: Context, customEventNativeListener: CustomEventNativeListener, serverParameter: String?, nativeMediationAdRequest: NativeMediationAdRequest, extra: Bundle? ) { serverParameter?.let { twmAdUnitId -> twmNativeAd = TWMNativeAd(context, twmAdUnitId).also { nativeAd -> nativeAd.setAdListener( TANativeEventForwarder( customEventNativeListener, nativeMediationAdRequest.nativeAdRequestOptions ) ) }.apply { val request = TWMAdRequest().apply { val nativeAdOptions = mutableListOf<TWMNativeAdOptions>() if (videoStartUnmuted) { nativeAdOptions.add(TWMNativeAdOptions.VIDEO_START_UNMUTED) } if (disableImageLoading) { nativeAdOptions.add(TWMNativeAdOptions.DISABLE_IMAGE_LOADING) } if (videoCustomControlRequest) { nativeAdOptions.add(TWMNativeAdOptions.VIDEO_CUSTOM_CONTROLS_REQUESTED) } if (mediaPreferImage) { nativeAdOptions.add(TWMNativeAdOptions.MEDIA_PREFER_IMAGE) } if (nativeAdOptions.size > 0) { setNativeAdOptions(nativeAdOptions.toTypedArray()) } } loadAd(request) } } } }
- 依照下列方式實作CustomEventNative需用到的TANativeEventForwarder 
package com.taiwanmobile.pt.adp.mediation.native
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.mediation.customevent.CustomEventNativeListener
import com.google.android.gms.ads.nativead.NativeAdOptions
import com.taiwanmobile.pt.adp.view.TWMAd
import com.taiwanmobile.pt.adp.view.TWMAdRequest
import com.taiwanmobile.pt.adp.view.TWMAdViewListener
import com.taiwanmobile.pt.adp.view.TWMNativeAd
@Suppress("DEPRECATION")
class TANativeEventForwarder(private val listener: CustomEventNativeListener,
                             private val options: NativeAdOptions
) : TWMAdViewListener {
    override fun onDismissScreen(ad: TWMAd) {}
    override fun onFailedToReceiveAd(ad: TWMAd, errorCode: TWMAdRequest.ErrorCode) {
        listener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL)
    }
    override fun onLeaveApplication(ad: TWMAd) {
        listener.onAdClicked()
        listener.onAdLeftApplication()
    }
    override fun onPresentScreen(ad: TWMAd) {
        listener.onAdImpression()
    }
    override fun onReceiveAd(ad: TWMAd) {
        when (ad is TWMNativeAd) {
            true -> {
                val mapper = TANativeAdMapper(ad)
                listener.onAdLoaded(mapper)
            }
            false -> {
                listener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL)
            }
        }
    }
}- 依照下列方式實作TANativeEventForwarder需用到的TANativeAdMapper 
package com.taiwanmobile.pt.adp.mediation.native
import android.view.View
import android.widget.FrameLayout
import com.google.android.gms.ads.mediation.UnifiedNativeAdMapper
import com.google.android.gms.ads.nativead.NativeAdAssetNames
import com.taiwanmobile.pt.adp.nativead.TWMMediaView
import com.taiwanmobile.pt.adp.view.TWMNativeAd
import com.taiwanmobile.pt.adp.view.TWMNativeAdView
import com.taiwanmobile.pt.guide.R
class TANativeAdMapper(private val nativeAd: TWMNativeAd) : UnifiedNativeAdMapper() {
    init {
        overrideClickHandling = true
        overrideImpressionRecording = true
        // Mapping TWMNativeAd to AdMob Native AD resource
        // headline
        nativeAd.nativeAdContent.longSubject?.let { headline = it }
        // body
        nativeAd.nativeAdContent.body?.let { body = it }
        // call to action
        nativeAd.nativeAdContent.callToAction?.let { callToAction = it }
        // icon
        nativeAd.nativeAdContent.iconSquare?.let { image ->
            image.getDrawable()?.let { drawable ->
                image.getUri()?.let { uri ->
                    icon = TANativeMappedImage(drawable, uri)
                }
            }
        }
        // mediaView
        nativeAd.nativeAdContent.mediaContent?.let {
            setHasVideoContent(true)
        }
    }
    override fun trackViews(
        containerView: View,
        clickableAssetViews: MutableMap<String, View>,
        nonClickableAssetViews: MutableMap<String, View>
    ) {
        super.trackViews(containerView, clickableAssetViews, nonClickableAssetViews)
        // TWMNativeAdView population
        containerView.findViewById<TWMNativeAdView>(R.id.taNativeAdView)?.apply {
            // headline
            clickableAssetViews[NativeAdAssetNames.ASSET_HEADLINE]?.let { headlineView ->
                setLongSubjectView(headlineView)
            }
            // body
            clickableAssetViews[NativeAdAssetNames.ASSET_BODY]?.let { bodyView ->
                setBodyView(bodyView)
            }
            // call to action
            clickableAssetViews[NativeAdAssetNames.ASSET_CALL_TO_ACTION]?.let { ctaView ->
                setCallToActionView(ctaView)
            }
            // icon
            clickableAssetViews[NativeAdAssetNames.ASSET_ICON]?.let { iconView ->
                setSquareIconView(iconView)
            }
            // mediaView
            if (hasVideoContent()) {
                clickableAssetViews[NativeAdAssetNames.ASSET_MEDIA_VIDEO]?.let { mediaView ->
                    nativeAd.nativeAdContent.mediaContent?.let { mediaContent ->
                        val taMediaView = TWMMediaView(mediaView.context).apply {
                            setMediaContent(mediaContent)
                            layoutParams = mediaView.layoutParams
                        }
                        // append TWMMediaView on AdMob MediaView
                        (mediaView as FrameLayout?)?.addView(taMediaView)
                        // specify TWMMediaView in TWMNativeAdView
                        setMediaView(taMediaView)
                        // (optional) cta text size
                        taMediaView.setCTATextSize(12.0f)
                        // (optional) video count down text size
                        taMediaView.setVideoCountdownTextSize(14.0f)
                        // (optional) volume image size
                        taMediaView.setVolumeImageSize(20)
                    }
                }
            } else {
                clickableAssetViews[NativeAdAssetNames.ASSET_MEDIA_VIDEO]?.let { mediaView ->
                    mediaView.visibility = View.GONE
                }
            }
            // final step of population, specify TWMNativeAd object to TWMNativeView
            setNativeAd(nativeAd)
        }
    }
}- 依照下列方式實作TANativeAdMapper需用到的TANativeMappedImage 
@file:Suppress("DEPRECATION")
package com.taiwanmobile.pt.adp.mediation.native
import android.graphics.drawable.Drawable
import android.net.Uri
import com.google.android.gms.ads.formats.NativeAd
/**
 * A Image class that fits the the [NativeAd.Image] abstract class and can be filled with
 * assets returned by TAmedia SDK.
 */
class TANativeMappedImage(
    private val drawable: Drawable,
    private val uri: Uri
) :
    NativeAd.Image() {
    override fun getDrawable(): Drawable = drawable
    override fun getUri(): Uri = uri
    override fun getScale(): Double = 1.0
}Developer Sample Code
Banner
- 1. 在MainActivity.kt中輸入AdUnit Id 
 
   companion object {
       private const val ADUNIT_ID_BANNER_320x50 = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_BANNER_300x250 = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_SMART_BANNER = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_INTERSTITIAL = "{AdMob Interstitial AdUnit Id}"
       private const val ADUNIT_ID_NATIVE = "{AdMob Native AdUnit Id}"
       const val KEY_ADUNITID = "adUnitId"
       const val KEY_ADSIZE = "adSize"
   }
- 2. 在layout檔案中加入可放置廣告的RelativeLayout(※僅供參考,開發者可依照自訂的layout進行配置使用) 
 
   <RelativeLayout
       android:id="@+id/adContainer"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_centerHorizontal="true"
       android:layout_alignParentBottom="true">
   </RelativeLayout>
- 3. BannerActivity 程式加入Banner的宣告, 設定AdUnitId與AdSize 
 
   class BannerActivity : AppCompatActivity() {
       private var adView: AdView? = null
       override fun onCreate(savedInstanceState: Bundle?) {
           super.onCreate(savedInstanceState)
           setContentView(R.layout.activity_banner)
           val adContainer = findViewById<View>(R.id.adContainer) as RelativeLayout
           val args = intent.extras
           if (args != null) {
               val adUnitId = args.getString(MainActivity.KEY_ADUNITID)
               val adSize: AdSize = when (args.getString(MainActivity.KEY_ADSIZE)) {
                   BANNER_320x50 -> {
                       AdSize.BANNER
                   }
                   BANNER_300x250 -> {
                       AdSize.MEDIUM_RECTANGLE
                   }
                   else -> {
                       val displayMetrics = resources.displayMetrics
                       val dpWidth = (displayMetrics.widthPixels / displayMetrics.density).toInt()
                       AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(
                           this,
                           dpWidth
                       )
                   }
               } 
               // Banner initial
               adUnitId?.let {
                   val request = AdRequest.Builder().build()
                   adView = AdView(this@BannerActivity)
                   adView?.adUnitId = it
                   adView?.adSize = adSize
                   adView?.adListener = AdListenerImpl()
                   adView?.loadAd(request)
                   // add view into layout
                   adContainer.addView(adView)
               }
           }
       }
}
Interstitial
- 1. 在MainActivity.kt中輸入AdUnit Id 
 
   companion object {
       private const val ADUNIT_ID_BANNER_320x50 = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_BANNER_300x250 = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_SMART_BANNER = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_INTERSTITIAL = "{AdMob Interstitial AdUnit Id}"
       private const val ADUNIT_ID_NATIVE = "{AdMob Native AdUnit Id}"
       const val KEY_ADUNITID = "adUnitId"
       const val KEY_ADSIZE = "adSize"
   }
- 2. InterstitialActivity 程式加入Interstitial的宣告, 設定AdUnitId 
 
class InterstitialActivity : AppCompatActivity() {
    private var interstitialAd: InterstitialAd? = null
    private var adUnitId: String? = null
    private lateinit var binding: ActivityInterstitialBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityInterstitialBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
        val args = intent.extras
        if (args != null) {
            adUnitId = args.getString(MainActivity.KEY_ADUNITID)
        }
    }
    // triggered in activity_int.xml
    fun fireAd(v: View?) {
        adUnitId?.let {
            val adRequest = AdRequest.Builder().build()
            InterstitialAd.load(this, it, adRequest, object : InterstitialAdLoadCallback() {
                override fun onAdLoaded(interstitialAd: InterstitialAd) {
                    this@InterstitialActivity.interstitialAd = interstitialAd
                    Toast.makeText(
                        baseContext,
                        "onAdLoaded(interstitial) invoked!!",
                        Toast.LENGTH_SHORT
                    ).show()
                    interstitialAd.show(this@InterstitialActivity)
                }
                override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                    Toast.makeText(
                        baseContext,
                        loadAdError.message,
                        Toast.LENGTH_SHORT
                    ).show()
                    interstitialAd = null
                }
            })
        }
    }
}
Native
- 1. 在MainActivity.kt中輸入AdUnit Id 
 
   companion object {
       private const val ADUNIT_ID_BANNER_320x50 = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_BANNER_300x250 = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_SMART_BANNER = "{AdMob Banner AdUnit Id}"
       private const val ADUNIT_ID_INTERSTITIAL = "{AdMob Interstitial AdUnit Id}"
       private const val ADUNIT_ID_NATIVE = "{AdMob Native AdUnit Id}"
       const val KEY_ADUNITID = "adUnitId"
       const val KEY_ADSIZE = "adSize"
   }
- 2. NativeActivity的layout如下, 在layout檔案中加入可放置原生廣告的nativeContainer(※這裡以FrameLayout為例,開發者可依照自訂的layout進行配置使用) 
 
   <LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:paddingBottom="@dimen/activity_vertical_margin"
       android:paddingLeft="@dimen/activity_horizontal_margin"
       android:paddingRight="@dimen/activity_horizontal_margin"
       android:paddingTop="@dimen/activity_vertical_margin"
       android:orientation="vertical">
   <FrameLayout
           android:id="@+id/native_container"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_marginTop="@dimen/activity_vertical_margin" />
   </LinearLayout>
- 3. 準備廣告佈局native_ad.xml, 在com.google.android.gms.ads.nativead.NativeAdView上添加TWMNativeAdView 
 
   <com.google.android.gms.ads.nativead.NativeAdView
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">
       <com.taiwanmobile.pt.adp.view.TWMNativeAdView
           android:id="@+id/taNativeAdView"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">
           <LinearLayout
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_gravity="center"
               android:background="#FFFFFF"
               android:minHeight="50dp"
               android:orientation="vertical">
               <LinearLayout
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:orientation="vertical"
                   android:paddingLeft="20dp"
                   android:paddingTop="3dp"
                   android:paddingRight="20dp">
                   <LinearLayout
                       android:layout_width="match_parent"
                       android:layout_height="wrap_content"
                       android:orientation="horizontal">
                       <ImageView
                           android:id="@+id/ad_app_icon"
                           android:layout_width="40dp"
                           android:layout_height="40dp"
                           android:adjustViewBounds="true"
                           android:paddingEnd="5dp"
                           android:paddingRight="5dp"
                           android:paddingBottom="5dp" />
                       <LinearLayout
                           android:layout_width="match_parent"
                           android:layout_height="wrap_content"
                           android:orientation="vertical">
                           <TextView
                               android:id="@+id/ad_headline"
                               android:layout_width="match_parent"
                               android:layout_height="wrap_content"
                               android:textColor="#0000FF"
                               android:textSize="16sp"
                               android:textStyle="bold" />
                       </LinearLayout>
                   </LinearLayout>
                   <LinearLayout
                       android:layout_width="match_parent"
                       android:layout_height="wrap_content"
                       android:orientation="vertical">
                   <TextView
                       android:id="@+id/ad_body"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_marginEnd="20dp"
                       android:layout_marginRight="20dp"
                       android:textSize="12sp" />
                   <com.google.android.gms.ads.nativead.MediaView
                       android:id="@+id/ad_media"
                       android:layout_width="250dp"
                       android:layout_height="175dp"
                       android:layout_gravity="center_horizontal"
                       android:layout_marginTop="5dp" />
                   <LinearLayout
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_gravity="end"
                       android:orientation="horizontal"
                       android:paddingTop="10dp"
                           android:paddingBottom="10dp">
                           <Button
                               android:id="@+id/ad_call_to_action"
                               android:layout_width="wrap_content"
                               android:layout_height="wrap_content"
                               android:gravity="center"
                               android:textSize="12sp" />
                       </LinearLayout>
                   </LinearLayout>
               </LinearLayout>
           </LinearLayout>
       </com.taiwanmobile.pt.adp.view.TWMNativeAdView>
   </com.google.android.gms.ads.nativead.NativeAdView>
- 4. 使用AdMob AdLoader載入原生廣告 - 當AdLoader取得原生廣告時, 產出(inflate)原生廣告佈局native_ad.xml並進行NativeAdView物件填充(population), 詳情可參考下方populateNativeAdView() 
- NativeAdView填充下列屬性: headline, body, call to action, icon, mediaView等資訊 
- 填充後將native_ad.xml物件加入nativeContainer 
 
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityNativeBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
        val args = intent.extras
        args?.let {
            val adUnitId = args.getString(MainActivity.KEY_ADUNITID) ?: ""
            val adLoader = AdLoader.Builder(this, adUnitId)
                .forNativeAd { ad: NativeAd ->
                    nativeAd = ad
                    val adLayout = NativeAdBinding.inflate(layoutInflater)
                    populateNativeAdView(ad, adLayout)
                    binding.nativeContainer.removeAllViews()
                    binding.nativeContainer.addView(adLayout.root)
                    // If this callback occurs after the activity is destroyed, you
                    // must call destroy and return or you may get a memory leak.
                    // Note `isDestroyed` is a method on Activity.
                    if (isDestroyed) {
                        ad.destroy()
                        return@forNativeAd
                    }
                }
                .withAdListener(object : AdListener() {
                    override fun onAdFailedToLoad(adError: LoadAdError) {
                        Toast.makeText(this@NativeActivity, "onAdFailedToLoad", Toast.LENGTH_LONG)
                            .show()
                    }
                    override fun onAdClicked() {
                        super.onAdClicked()
                        Toast.makeText(this@NativeActivity, "onAdClicked", Toast.LENGTH_LONG)
                            .show()
                    }
                    override fun onAdImpression() {
                        super.onAdImpression()
                        Toast.makeText(this@NativeActivity, "onAdImpression", Toast.LENGTH_LONG)
                            .show()
                    }
                })
                .withNativeAdOptions(
                    NativeAdOptions.Builder()
                        // Methods in the NativeAdOptions.Builder class can be
                        // used here to specify individual options settings.
                        .build()
                )
                .build()
            adLoader.loadAd(AdRequest.Builder().build())
        }
    }
    /**
     * Populates a [NativeAdView] object with data from a given [NativeAd].
     *
     * @param nativeAd the object containing the ad's assets
     * @param viewBinding the view binding object [NativeAdBinding] contains the [NativeAdView]
     */
    private fun populateNativeAdView(nativeAd: NativeAd, viewBinding: NativeAdBinding) {
        // fetch NativeAdView from NativeAdBinding
        val nativeAdView: NativeAdView = viewBinding.root
        // headline
        if (nativeAd.headline == null) {
            viewBinding.adHeadline.visibility = View.INVISIBLE
        } else {
            viewBinding.adHeadline.visibility = View.VISIBLE
            viewBinding.adHeadline.text = nativeAd.headline
            nativeAdView.headlineView = viewBinding.adHeadline
        }
        // body
        if (nativeAd.body == null) {
            viewBinding.adBody.visibility = View.INVISIBLE
        } else {
            viewBinding.adBody.visibility = View.VISIBLE
            viewBinding.adBody.text = nativeAd.body
            nativeAdView.bodyView = viewBinding.adBody
        }
        // call to action
        if (nativeAd.callToAction == null) {
            viewBinding.adCallToAction.visibility = View.INVISIBLE
        } else {
            viewBinding.adCallToAction.visibility = View.VISIBLE
            viewBinding.adCallToAction.text = nativeAd.callToAction
            nativeAdView.callToActionView = viewBinding.adCallToAction
        }
        // icon
        if (nativeAd.icon == null) {
            viewBinding.adAppIcon.visibility = View.GONE
        } else {
            nativeAd.icon?.drawable?.let {
                viewBinding.adAppIcon.setImageDrawable(it)
                viewBinding.adAppIcon.visibility = View.VISIBLE
                nativeAdView.iconView = viewBinding.adAppIcon
            }
        }
        // mediaView
        nativeAdView.mediaView = viewBinding.adMedia
        nativeAdView.setNativeAd(nativeAd)
    }- 5. 設計架構圖 - 藍色為TAmedia SDK原生廣告類別, 黃色為AdMob SDK原生廣告類別 
- 開發者僅需在native_ad.xml中將TAmedia TWMNativeAdView添加至AdMob NativeAdView之上即可 
- 佈局中如果有發現AdMob SDK MediaView, TAmedia SDK TWMMediaView則會主動覆蓋至上面, 負責顯示影音廣告 
 
 













