VideoAdView
:
動画再生およびトラッキングイベントの送信を行うCustom Viewです。
レイアウトファイルに直接配置することができます。
VideoAdActivity
:
全画面時に表示されるActivityです。
VideoAdView
と同様、動画再生およびトラッキングイベントの送信を行いますが、動画再生の制御機能が増えています。
動画SDKでは全画面表示に対応するためSDKのVideoAdActivity
を使用します。これをAndroidManifest.xml
に登録しておきます。
<activity android:name="jp.fout.rfp.android.sdk.video.VideoAdActivity" />
動画広告の場合、RFPInstreamInfoModel#isVideo()
がtrue
を返します。これを利用して動画広告か否かを判定することができます。
private boolean isAd(int position) {
return getItem(position) instanceof RFPInstreamInfoModel;
}
private boolean isVideoAd(int position) {
if (!isAd(position)) {
return false;
}
RFPInstreamInfoModel item = (RFPInstreamInfoModel) getItem(position);
return item.isVideo();
}
動画広告の詳細情報はRFPInstreamInfoModel
に格納されており、これを元に動画広告を組み立てるためVideoAdView
に処理させる必要があります。
具体的な実装としてはVideoAdView#processAd()
にRFPInstreamInfoModel
のインスタンスに渡します。
static class AdViewHolder {
TextView advertiserName;
TextView adText;
ImageView adImage;
TextView adSponsoredLabel;
VideoAdView adVideo;
AdViewHolder(View convertView) {
advertiserName = (TextView) convertView.findViewById(R.id.custom_instream_advertiser_name);
adText = (TextView) convertView.findViewById(R.id.custom_instream_ad_text);
adImage = (ImageView) convertView.findViewById(R.id.custom_instream_ad_image);
adSponsoredLabel = (TextView) convertView.findViewById(R.id.custom_instream_sponsor_name);
adVideo = (VideoAdView) convertView.findViewById(R.id.custom_instream_video_view);
}
void setData(RFPInstreamInfoModel adData) {
advertiserName.setText(adData.title());
adText.setText(adData.content());
String displayedAdvertiser = adData.displayedAdvertiser();
if (null != displayedAdvertiser && 0 < displayedAdvertiser.length()) {
adSponsoredLabel.setText(displayedAdvertiser);
}
adVideo.processAd(adData);
}
}
動画広告に配置する配置するボタンのテキストは広告の管理画面から設定することができます。
この設定値はRFPInstreamInfoModel#cta_text()
で取得することができます。
static class AdViewHolder {
// ... ...
Button adButton;
AdViewHolder(View convertView) {
// ... ...
adButton = (Button) convertView.findViewById(R.id.custom_instream_action_button);
}
void setData(RFPInstreamInfoModel adData) {
// ... ...
String adButtonText = adData.cta_text();
if (adButtonText != null && adButtonText.length() > 0) {
adButton.setText(adButtonText);
}
adVideo.processAd(adData);
}
}
VideoAdView#processAd()
を呼び出し、動画が再生可能となった時点で自動的に再生が開始されます。
VideoAdView#setAutoStart(false)
とすることで自動再生を抑制することができます。この場合、VideoAdView#play()
をアプリから呼び出し再生を開始する必要があります。また、VideoAdView#pause()
を呼び出すことにより任意の地点で停止することができます。
この場合、動画の再生・停止のタイミングはアプリケーション開発者に任せられますが、ディスプレイ枠などである程度動画の再生・停止を自動化したい場合のため、 RFPInViewNotifier
というユーティリティクラスを用意してあります。
実装手順は以下のようになります:
VideoAdView#setAutoStart(false)
を呼ぶRFPInViewNotifier
のインスタンスを生成する。
RFPInViewNotifier.OnVisibilityChangedListener
を実装し、 onVisible()
で VideoAdView#play()
、 onInVisible()
で VideoAdView#pause()
を呼ぶ。RFPInViewNotifier#addView(View)
で広告枠を監視下に入れる。RFPInViewNotifier#detach()
を呼ぶ。以下に実装例を示します:
private lateinit var adVideoView: VideoAdView
private lateinit var inViewNotifier: RFPInViewNotifier
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_outstream, container, false)
adVideoView = view.findViewById(R.id.ad_video)
adVideoView.setAutoStart(false) // 1
// 2
inViewNotifier = RFPInViewNotifier(requireActivity(), object : RFPInViewNotifier.OnVisibilityChangedListener {
override fun onVisible(v: View?) {
(v as? VideoAdView)?.play()
}
override fun onInvisible(v: View?) {
(v as? VideoAdView)?.pause()
}
})
// 3.
inViewNotifier.addView(adVideoView)
// ... ...
return view
}
override fun onDestroyView() {
super.onDestroyView()
inViewNotifier.detach() // 4
// ... ...
}
動画領域をタップすることで全画面表示となります。
この挙動はデフォルトの動作ですが、全画面モードを終了した際に動画の再生位置を同期させたい場合は追加の実装が必要となります。
全画面モードはActivityであり、再生位置およびトラッキングイベント送信状況の引き継ぎのため、Activity#startActivityForResult()
とActivity#onActivityResult()
で結果の受け渡しを行っています。
VideoAdView
は全画面モード移行時にOnFullscreenRequestListener#onRequestFullscreen()
を呼び出しているのでこれを実装します。引数として対象となるVideoAdView
と引き継ぎ情報の入ったインテントが渡されるのでこれをもとにActivity#startActivityForResult()
を呼びます。ActivityへのリクエストコードにはRFP.getVideoAdFullscreenRequestCode()
を使用してください。
VideoAdView#restore(Intent)
を使用することで再生位置およびトラッキングイベント送信状況を引き継ぐことができます。
ActivityがVideoAdView#restore(Intent)
の対象となるVideoAdView
を知っている必要がありますが、VideoAdView
のフルスクリーン画面呼び出し時のコールバックであるVideoAdView.OnFullscreenRequestListener
を実装することでこれをActivityに通知することができます。
下記の例では、greenrobot/EventBusを用いてActivityに対象となるVideoAdView
を通知し、保存しています。
class VideoAdViewHolder extends RecyclerView.ViewHolder
implements VideoAdView.OnFullscreenRequestListener {
RFPInstreamAdPlacer adPlacer;
VideoAdView adVideo;
VideoAdViewHolder(View itemView, RFPInstreamAdPlacer placer) {
super(itemView);
adPlacer = placer;
// ... ...
adVideo = (VideoAdView) itemView.findViewById(R.id.custom_instream_ad_image);
adVideo.setOnFullscreenRequestListener(this);
}
@Override
public void onRequestFullscreen(VideoAdView view, Intent intent) {
EventBus.getDefault().post(new RequestFullscreenEvent(view, intent));
}
}
VideoAdView mSourceVideoAdView = null;
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRequestFullscreen(RequestFullscreenEvent event) {
mSourceVideoAdView = event.view;
startActivityForResult(event.intent, RFP.getVideoAdFullscreenRequestCode());
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RFP.getVideoAdFullscreenRequestCode()
&& resultCode == Activity.RESULT_OK
&& mSourceVideoAdView != null) {
mSourceVideoAdView.restore(data);
}
}
RFP#getVideoAdFullscreenRequestCode()
のデフォルト値は18416
で、RFP#setVideoAdFullscreenRequestCode(int)
を使用して任意の値に変更できます。
RFP.setVideoAdFullscreenRequestCode(1000);
VideoAdView#setAutoStart()
を用いて自動再生を抑制している場合は、復帰時にVideoAdView#play()
を呼び出す必要があります。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RFP.getVideoAdFullscreenRequestCode()
&& resultCode == Activity.RESULT_OK
&& mSourceVideoAdView != null) {
mSourceVideoAdView.restore(data);
mSourceVideoAdView.play();
}
}
全画面モードへの移行時、アクションボタンによるランディングページの移動時は正しくトラッキングイベントを取得するため停止しておく必要があります。
全画面モード移行時と全画面モード内のアクションボタンについてはSDKで正しくハンドリングされますが、アプリ側の制御となるフィード内のアクションボタンについては動画の停止処理を記述する必要があります。VideoAdView#pause()
をActivity#onStop()
、Activity#onPause()
、アクションボタンのonClick
時などいずれかに記述してください。
VideoAdView adVideo = (VideoAdView) itemView.findViewById(R.id.custom_instream_ad_image);
Button adButton = (Button) itemView.findViewById(R.id.custom_instream_ad_action_button);
adButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
adVideo.pause();
adPlacer.sendClickEvent(adData);
}
});
デフォルトでは動画領域をタップすると全画面モードに移行しますが、VideoAdView#setOnVideoAreaClickListener(OnClickLisner)
にて挙動をカスタマイズできます。
下記の例はタップ時にランディングページに遷移(Call-to-Actionと同様の挙動)となるような実装です。
RFPInstreamAdPlacer adPlacer;
VideoAdViewHolder(View itemView, RFPInstreamAdPlacer placer) {
super(itemView);
adPlacer = placer;
}
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == ITEM_VIEW_TYPE_AD_VIDEO) {
final VideoAdViewHolder h = (VideoAdViewHolder) holder;
final RFPInstreamInfoModel model = (RFPInstreamInfoModel) mModels.get(position);
h.adVideoView.setOnVideoAreaClickListener(new OnClickListener() {
public void onClick(View v) {
h.adVideoView.pause();
adPlacer.sendClickEvent(model);
}
}
} else {
// ... ...
}
}
動画広告読み込み時にエラーが発生した場合、エラー処理を記述して制御することができます。
フィード内ではVideoAdView#OnErrorListener
を実装し、VideoAdView#setOnErrorListener()
にてリスナーを設定してください。
static class AdViewHolder implements VideoAdView.OnErrorListener {
VideoAdView adVideo;
VideoAdViewHolder(View itemView, RFPInstreamAdPlacer placer) {
super(itemView);
// ... ...
adVideo = (VideoAdView) itemView.findViewById(R.id.custom_instream_ad_image);
adVideo.setOnErrorListener(this);
}
@Override
public void onError(VideoAdView view, String message, @Nullable Throwable t) {
// view: エラーの発生したVideoAdView
// message: エラー内容
// t: 例外が発生した場合に該当する例外
Log.d(TAG, message, t);
}
}
フルスクリーン時にエラーが発生した場合、アクティビティは終了し、onActivityResult()
のresultCode
にRESULT_CANCELED
が入ります。これを判定することによりエラー処理を記述することができます。この場合、Intent
にエラー内容が入ります。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RFP.getVideoAdFullscreenRequestCode()) {
if (resultCode == Activity.RESULT_OK) {
// 正常時の処理
} else if (resultCode == Activity.RESULT_CANCELED) {
// エラー時の処理
String message = data.getStringExtra("error_message");
Throwable t = (Throwable) data.getSerializableExtra("error");
Log.d(TAG, message, t);
}
}
}
RFPは動画広告を内部ストレージのキャッシュ領域にある程度保持します。
キャッシュ容量はRFPが推奨する設定値を使用します。この値はアプリ側から変更することが可能です。
int size = RFP.getVideoCacheSize(); // 現在の設定値を返す
RFP.setVideoCacheSize(50); // 50MBをキャッシュ領域として使用する
RFP.setVideoCacheSize(0); // キャッシュを無効にする
動画SDKではウィンドウからデタッチされた動画の再生状態を保持していませんので、再びアタッチされた際に動画が初期状態から再生されます。 この挙動はデフォルトの動作ですが、再びウィンドウ内にアタッチされた際に対象動画を元の再生位置から開始させたい場合は追加の実装が必要となります。
VideoAdView
が画面外に移動時にVideoAdView#OnDetachedListener
を呼び出しているのでこれを実装します。引数として対象となるVideoAdView
の引き継ぎ情報の入ったBundleが渡されるのでこれを格納してください。
対象動画が画面内に移動されたら、先ほど格納したBundleを
VideoAdView#processAd(RFPInstreamInfoModel adModel, @Nullable Bundle savedState)
に渡して再生状態を引き継ぐことができます。
// store ad's information for resume
private SparseArray<Bundle> mResumeVideosArray = new SparseArray<>();
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == ITEM_VIEW_TYPE_AD_VIDEO) {
final VideoAdViewHolder h = (VideoAdViewHolder) holder;
final RFPInstreamInfoModel model = (RFPInstreamInfoModel) mModels.get(position);
// get resume ad's information if exist
Bundle state = mResumeVideosArray.get(position);
// remove ad's old information if exist
mResumeVideosArray.remove(position);
// reset ad's information when it's detached from window
h.adVideoView.setOnDetachedListener(bundle -> mResumeVideosArray.put(position, bundle));
h.setData(adInfo, null, state);
} else {
// ... ...
}
}
static class AdViewHolder {
VideoAdView adVideo;
// ... ...
AdViewHolder(View convertView) {
adVideo = (VideoAdView) itemView.findViewById(R.id.custom_instream_ad_image);
// ... ...
}
void setData(RFPInstreamInfoModel adData, Bundle savedState) {
// ... ...
// restart ad from the position when being detached
adVideo.processAd(adData, savedState);
}