diff --git a/.classpath b/.classpath
new file mode 100644
index 00000000..b80adb57
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 00000000..1c521088
--- /dev/null
+++ b/.project
@@ -0,0 +1,33 @@
+
+
+ Demo App V3
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 00000000..442c2d7c
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..6c9bceaf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+Google Ads Android IMA SDK
+--------------------------
+
+This project hosts samples for the [Android IMA SDK](https://developers.google.com/interactive-media-ads/docs/sdks/android/v3/).
+
+### Requirements
+ * Eclipse or Android Studio
+
+### Installation Instructions
+Installation instructions can be found in our [online documentation](https://developers.google.com/interactive-media-ads/docs/sdks/android/v3/quickstart#download_build_and_run_sample_app).
+
+### Downloads
+Check out the [releases section](https://github.com/googleads/googleads-ima-android/releases) for downloadable zips of the source.
+
+### More Info
+For more information, see the documentation at https://developers.google.com/interactive-media-ads/docs/sdks/android/v3/.
+
+### Announcements and Updates
+For API and client library updates and news, please follow our [Google+ Ads Developers page](https://plus.google.com/+GoogleAdsDevelopers/posts) and our [Google Ads Developers blog](http://googleadsdeveloper.blogspot.com/)
+
+
+Copyright 2013 Google Inc. All Rights Reserved.
+You may study, modify, and use this example for any purpose.
+Note that this example is provided "as is", WITHOUT WARRANTY of any kind either expressed or implied.
diff --git a/libs/README b/libs/README
new file mode 100644
index 00000000..a595cd4a
--- /dev/null
+++ b/libs/README
@@ -0,0 +1 @@
+Place the downloaded SDK jar in this folder to build.
diff --git a/project.properties b/project.properties
new file mode 100644
index 00000000..b904742f
--- /dev/null
+++ b/project.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-13
diff --git a/res/drawable-hdpi/icon_v3.png b/res/drawable-hdpi/icon_v3.png
new file mode 100644
index 00000000..d12b54db
Binary files /dev/null and b/res/drawable-hdpi/icon_v3.png differ
diff --git a/res/drawable-ldpi/icon_v3.png b/res/drawable-ldpi/icon_v3.png
new file mode 100644
index 00000000..a77da48c
Binary files /dev/null and b/res/drawable-ldpi/icon_v3.png differ
diff --git a/res/drawable-mdpi/icon_v3.png b/res/drawable-mdpi/icon_v3.png
new file mode 100644
index 00000000..5e7dfa81
Binary files /dev/null and b/res/drawable-mdpi/icon_v3.png differ
diff --git a/res/layout-land/main.xml b/res/layout-land/main.xml
new file mode 100644
index 00000000..6df6a3c8
--- /dev/null
+++ b/res/layout-land/main.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout-large-land/main.xml b/res/layout-large-land/main.xml
new file mode 100644
index 00000000..a61cebac
--- /dev/null
+++ b/res/layout-large-land/main.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout-large/main.xml b/res/layout-large/main.xml
new file mode 100644
index 00000000..9f261c4b
--- /dev/null
+++ b/res/layout-large/main.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/main.xml b/res/layout/main.xml
new file mode 100644
index 00000000..0546bf82
--- /dev/null
+++ b/res/layout/main.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 00000000..9752f471
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,24 @@
+
+
+ Android SDK Demo
+ Play
+ Req. Ad
+ Language
+ Companion Slot
+ Leaderboard Companion Slot
+
+ Instream 1
+ Instream 2
+ AdSense skippable
+ Wrapper
+ Ads List
+ Custom Tag
+
+
+
+
+
+
+
+
+
diff --git a/src/com/google/ads/interactivemedia/v3/samples/demoapp/DroidDemoActivity.java b/src/com/google/ads/interactivemedia/v3/samples/demoapp/DroidDemoActivity.java
new file mode 100644
index 00000000..614fc885
--- /dev/null
+++ b/src/com/google/ads/interactivemedia/v3/samples/demoapp/DroidDemoActivity.java
@@ -0,0 +1,363 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.google.ads.interactivemedia.v3.samples.demoapp;
+
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_VARIATION_URI;
+
+import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
+import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
+import com.google.ads.interactivemedia.v3.api.AdErrorEvent.AdErrorListener;
+import com.google.ads.interactivemedia.v3.api.AdEvent;
+import com.google.ads.interactivemedia.v3.api.AdEvent.AdEventListener;
+import com.google.ads.interactivemedia.v3.api.AdsLoader;
+import com.google.ads.interactivemedia.v3.api.AdsLoader.AdsLoadedListener;
+import com.google.ads.interactivemedia.v3.api.AdsManager;
+import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent;
+import com.google.ads.interactivemedia.v3.api.AdsRequest;
+import com.google.ads.interactivemedia.v3.api.CompanionAdSlot;
+import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
+import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
+import com.google.ads.interactivemedia.v3.samples.demoapp.player.DemoPlayer;
+import com.google.ads.interactivemedia.v3.samples.demoapp.player.TrackingVideoView.CompleteCallback;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A demo application for the Android IMA SDK.
+ */
+public class DroidDemoActivity extends Activity
+ implements
+ AdErrorListener,
+ AdsLoadedListener,
+ AdEventListener,
+ CompleteCallback {
+
+ private static final String CONTENT_URL =
+ "http://rmcdn.2mdn.net/MotifFiles/html/1248596/" + "android_1330378998288.mp4";
+ protected String[] defaultTagUrls;
+ protected FrameLayout videoHolder;
+ protected DemoPlayer videoPlayer;
+ protected ScrollView logScroll;
+ protected TextView log;
+ protected ViewGroup companionView;
+ protected ViewGroup leaderboardCompanionView;
+ protected AdsLoader adsLoader;
+ protected AdsManager adsManager;
+ protected AdDisplayContainer container;
+ protected ImaSdkFactory sdkFactory;
+ protected ImaSdkSettings sdkSettings;
+
+ protected Button languageButton;
+ protected Button requestAdButton;
+ protected Spinner defaultTagSpinner;
+ protected boolean isAdStarted;
+ protected boolean isAdPlaying;
+
+ protected boolean contentStarted = false;
+ protected String tagUrl;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ initUi();
+
+ sdkFactory = ImaSdkFactory.getInstance();
+ createAdsLoader();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // This is a crude way to save the video state on orientation change.
+ if (videoPlayer != null) {
+ videoPlayer.savePosition();
+ videoHolder.removeView(videoPlayer);
+ }
+ super.onConfigurationChanged(newConfig);
+ setContentView(R.layout.main);
+ initUi();
+ videoPlayer.restorePosition();
+ videoPlayer.play();
+ }
+
+ protected void initUi() {
+ videoHolder = (FrameLayout) findViewById(R.id.videoHolder);
+
+ if (videoPlayer == null) {
+ videoPlayer = new DemoPlayer(this);
+ videoPlayer.setCompletionCallback(this);
+ }
+ videoHolder.addView(videoPlayer);
+
+ companionView = (ViewGroup) findViewById(R.id.companionFrame);
+ leaderboardCompanionView = (ViewGroup) findViewById(R.id.leaderboardCompanionFrame);
+ logScroll = (ScrollView) findViewById(R.id.scroll);
+ log = (TextView) findViewById(R.id.log);
+
+ languageButton = (Button) findViewById(R.id.language);
+ requestAdButton = (Button) findViewById(R.id.requestAd);
+ defaultTagSpinner = (Spinner) findViewById(R.id.tagSpinner);
+
+ defaultTagUrls = getResources().getStringArray(R.array.default_ad_tags);
+ setButtonListeners();
+ }
+
+ protected void createAdsLoader() {
+ adsLoader = sdkFactory.createAdsLoader(this, getImaSdkSettings());
+ adsLoader.addAdErrorListener(this);
+ adsLoader.addAdsLoadedListener(this);
+ }
+
+ protected ImaSdkSettings getImaSdkSettings() {
+ if (sdkSettings == null) {
+ sdkSettings = sdkFactory.createImaSdkSettings();
+ }
+ return sdkSettings;
+ }
+
+ protected void playVideo() {
+ log("Playing video");
+ videoPlayer.playContent(CONTENT_URL);
+ contentStarted = true;
+ }
+
+ protected void pauseResumeAd() {
+ if (isAdStarted) {
+ if (isAdPlaying) {
+ log("Pausing video");
+ videoPlayer.pauseAd();
+ } else {
+ log("Resuming video");
+ videoPlayer.resumeAd();
+ }
+ }
+ }
+
+ protected AdsRequest buildAdsRequest() {
+ container = sdkFactory.createAdDisplayContainer();
+ container.setPlayer(videoPlayer);
+ container.setAdContainer(videoPlayer.getUiContainer());
+ log("Requesting ads");
+ AdsRequest request = sdkFactory.createAdsRequest();
+ request.setAdTagUrl(tagUrl);
+
+ ArrayList companionAdSlots = new ArrayList();
+
+ CompanionAdSlot companionAdSlot = sdkFactory.createCompanionAdSlot();
+ companionAdSlot.setContainer(companionView);
+ companionAdSlot.setSize(300, 50);
+ companionAdSlots.add(companionAdSlot);
+
+ if (leaderboardCompanionView != null) {
+ CompanionAdSlot leaderboardCompanionAdSlot = sdkFactory.createCompanionAdSlot();
+ leaderboardCompanionAdSlot.setContainer(leaderboardCompanionView);
+ leaderboardCompanionAdSlot.setSize(728, 90);
+ companionAdSlots.add(leaderboardCompanionAdSlot);
+ }
+
+ container.setCompanionSlots(companionAdSlots);
+ request.setAdDisplayContainer(container);
+ return request;
+ }
+
+ protected void requestAd() {
+ adsLoader.requestAds(buildAdsRequest());
+ }
+
+ @Override
+ public void onAdError(AdErrorEvent event) {
+ log(event.getError().getMessage() + "\n");
+ }
+
+ @Override
+ public void onAdsManagerLoaded(AdsManagerLoadedEvent event) {
+ log("Ads loaded!");
+ adsManager = event.getAdsManager();
+ adsManager.addAdErrorListener(this);
+ adsManager.addAdEventListener(this);
+ log("Calling init.");
+ adsManager.init();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (videoPlayer != null) {
+ videoPlayer.savePosition();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (videoPlayer != null) {
+ videoPlayer.restorePosition();
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ if (videoPlayer.isContentPlaying()) {
+ adsLoader.contentComplete();
+ }
+ }
+
+ @Override
+ public void onAdEvent(AdEvent event) {
+ log("Event:" + event.getType());
+
+ switch (event.getType()) {
+ case LOADED:
+ log("Calling start.");
+ adsManager.start();
+ break;
+ case CONTENT_PAUSE_REQUESTED:
+ if (contentStarted) {
+ videoPlayer.pauseContent();
+ }
+ break;
+ case CONTENT_RESUME_REQUESTED:
+ if (contentStarted) {
+ videoPlayer.resumeContent();
+ } else {
+ playVideo();
+ }
+ break;
+ case STARTED:
+ isAdStarted = true;
+ isAdPlaying = true;
+ break;
+ case COMPLETED:
+ isAdStarted = false;
+ isAdPlaying = false;
+ break;
+ case ALL_ADS_COMPLETED:
+ isAdStarted = false;
+ isAdPlaying = false;
+ adsManager.destroy();
+ break;
+ case PAUSED:
+ isAdPlaying = false;
+ break;
+ case RESUMED:
+ isAdPlaying = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ protected void log(String message) {
+ log.append(message + "\n");
+ logScroll.post(new Runnable() {
+ @Override
+ public void run() {
+ logScroll.fullScroll(View.FOCUS_DOWN);
+ }
+ });
+ }
+
+ protected void promptTagUrl() {
+ AlertDialog.Builder prompt = new AlertDialog.Builder(this);
+ prompt.setTitle("Set ad tag URL");
+ prompt.setMessage("Enter the ad tag URL to use:");
+
+ final EditText tagInput = new EditText(this);
+ tagInput.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_URI);
+ tagInput.setSingleLine(true);
+ prompt.setView(tagInput);
+
+ prompt.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ tagUrl = tagInput.getText().toString();
+ }
+ });
+ prompt.setNegativeButton("Cancel", null);
+
+ prompt.show();
+ }
+
+ protected void promptLanguage() {
+ AlertDialog.Builder prompt = new AlertDialog.Builder(this);
+ prompt.setTitle("Set ad UI language code");
+ prompt.setMessage("Enter 2-letter language code ('en','ru',etc.):");
+
+ final EditText tagInput = new EditText(this);
+ tagInput.setInputType(TYPE_CLASS_TEXT);
+ tagInput.setSingleLine(true);
+ prompt.setView(tagInput);
+
+ prompt.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ sdkSettings.setLanguage(tagInput.getText().toString());
+ if (adsManager != null) {
+ adsManager.destroy();
+ }
+ createAdsLoader();
+ }
+ });
+ prompt.setNegativeButton("Cancel", null);
+ prompt.show();
+ }
+
+ protected void setButtonListeners() {
+ languageButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ promptLanguage();
+ }
+ });
+
+ requestAdButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ requestAd();
+ }
+ });
+
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(
+ this, R.array.default_ad_tag_labels, android.R.layout.simple_spinner_item);
+ // Specify the layout to use when the list of choices appears
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ // Apply the adapter to the spinner
+ defaultTagSpinner.setAdapter(adapter);
+ defaultTagSpinner.setOnItemSelectedListener(createTagSelectionListener());
+ }
+
+ protected OnItemSelectedListener createTagSelectionListener() {
+ return new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
+ if (pos == defaultTagUrls.length) {
+ promptTagUrl();
+ } else if (pos < defaultTagUrls.length) {
+ tagUrl = defaultTagUrls[pos];
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {}
+ };
+ }
+}
diff --git a/src/com/google/ads/interactivemedia/v3/samples/demoapp/player/DemoPlayer.java b/src/com/google/ads/interactivemedia/v3/samples/demoapp/player/DemoPlayer.java
new file mode 100644
index 00000000..5cfd760d
--- /dev/null
+++ b/src/com/google/ads/interactivemedia/v3/samples/demoapp/player/DemoPlayer.java
@@ -0,0 +1,174 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.google.ads.interactivemedia.v3.samples.demoapp.player;
+
+import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
+import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
+import com.google.ads.interactivemedia.v3.samples.demoapp.player.TrackingVideoView.CompleteCallback;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.MediaController;
+import android.widget.RelativeLayout;
+
+/**
+ * An example video player that implements VideoAdPlayer.
+ */
+public class DemoPlayer extends RelativeLayout implements VideoAdPlayer {
+ private TrackingVideoView video;
+ private FrameLayout adUiContainer;
+ private MediaController mediaController;
+
+ private String savedContentUrl;
+ private int savedContentPosition = 0;
+ private int savedPosition = 0;
+ private boolean contentPlaying;
+
+ public DemoPlayer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ public DemoPlayer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public DemoPlayer(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ mediaController = new MediaController(getContext());
+ mediaController.setAnchorView(this);
+
+ // Center the video in the parent layout (when video ratio doesn't match the
+ // layout size it will by default position to the left).
+ LayoutParams videoLayouyParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT);
+ videoLayouyParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
+ videoLayouyParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+ video = new TrackingVideoView(getContext());
+ // Adding the touch listener to allow pause/resume during an ad. This is necessary because we
+ // remove the MediaController during ad playback to prevent seeking.
+ video.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (!contentPlaying) {
+ // Only applies when ad is playing
+ video.togglePlayback();
+ }
+ return false;
+ }
+ });
+
+ addView(video, videoLayouyParams);
+ adUiContainer = new FrameLayout(getContext());
+ addView(adUiContainer, LayoutParams.FILL_PARENT);
+ }
+
+ public ViewGroup getUiContainer() {
+ return adUiContainer;
+ }
+
+ public void setCompletionCallback(CompleteCallback callback) {
+ video.setCompleteCallback(callback);
+ }
+
+ /**
+ * Play whatever is already in the video view.
+ */
+ public void play() {
+ video.start();
+ }
+
+ public void playContent(String contentUrl) {
+ contentPlaying = true;
+ savedContentUrl = contentUrl;
+ video.setVideoPath(contentUrl);
+ video.setMediaController(mediaController);
+ play();
+ }
+
+ public void pauseContent() {
+ savedContentPosition = video.getCurrentPosition();
+ video.stopPlayback();
+ video.setMediaController(null); // Disables seeking during ad playback.
+ }
+
+ public void resumeContent() {
+ contentPlaying = true;
+ video.setVideoPath(savedContentUrl);
+ video.seekTo(savedContentPosition);
+ video.setMediaController(mediaController);
+ play();
+ }
+
+ public boolean isContentPlaying() {
+ return contentPlaying;
+ }
+
+ public void savePosition() {
+ savedPosition = video.getCurrentPosition();
+ }
+
+ public void restorePosition() {
+ video.seekTo(savedPosition);
+ }
+
+ // Methods implementing VideoAdPlayer interface.
+
+ @Override
+ public void playAd() {
+ contentPlaying = false;
+ video.start();
+ }
+
+ @Override
+ public void stopAd() {
+ video.stopPlayback();
+ }
+
+ @Override
+ public void loadAd(String url) {
+ video.setVideoPath(url);
+ }
+
+ @Override
+ public void pauseAd() {
+ video.pause();
+ }
+
+ @Override
+ public void resumeAd() {
+ video.start();
+ }
+
+ @Override
+ public void addCallback(VideoAdPlayerCallback callback) {
+ video.addCallback(callback);
+ }
+
+ @Override
+ public void removeCallback(VideoAdPlayerCallback callback) {
+ video.removeCallback(callback);
+ }
+
+ @Override
+ public VideoProgressUpdate getProgress() {
+ int durationMs = video.getDuration();
+
+ if (durationMs <= 0) {
+ return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
+ }
+ VideoProgressUpdate vpu = new VideoProgressUpdate(video.getCurrentPosition(), durationMs);
+ Log.i("PLAYER", vpu.toString());
+ return vpu;
+ }
+}
diff --git a/src/com/google/ads/interactivemedia/v3/samples/demoapp/player/TrackingVideoView.java b/src/com/google/ads/interactivemedia/v3/samples/demoapp/player/TrackingVideoView.java
new file mode 100644
index 00000000..cfa55fbc
--- /dev/null
+++ b/src/com/google/ads/interactivemedia/v3/samples/demoapp/player/TrackingVideoView.java
@@ -0,0 +1,134 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.google.ads.interactivemedia.v3.samples.demoapp.player;
+
+import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer.VideoAdPlayerCallback;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.widget.VideoView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A VideoView that intercepts various methods and reports them back to a set of
+ * VideoAdPlayerCallbacks.
+ */
+public class TrackingVideoView extends VideoView implements OnCompletionListener, OnErrorListener {
+ /** Interface for alerting caller of video completion. */
+ public interface CompleteCallback {
+ public void onComplete();
+ }
+
+ private enum PlaybackState {
+ STOPPED, PAUSED, PLAYING
+ }
+
+ private final List adCallbacks = new ArrayList(1);
+ private CompleteCallback completeCallback;
+ private PlaybackState state = PlaybackState.STOPPED;
+
+ public TrackingVideoView(Context context) {
+ super(context);
+ super.setOnCompletionListener(this);
+ super.setOnErrorListener(this);
+ }
+
+ public void setCompleteCallback(CompleteCallback callback) {
+ this.completeCallback = callback;
+ }
+
+ public void togglePlayback() {
+ switch(state) {
+ case STOPPED:
+ case PAUSED:
+ start();
+ break;
+ case PLAYING:
+ pause();
+ break;
+ }
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ PlaybackState oldState = state;
+ state = PlaybackState.PLAYING;
+
+ switch (oldState) {
+ case STOPPED:
+ for (VideoAdPlayerCallback callback : adCallbacks) {
+ callback.onPlay();
+ }
+ break;
+ case PAUSED:
+ for (VideoAdPlayerCallback callback : adCallbacks) {
+ callback.onResume();
+ }
+ break;
+ default:
+ // Already playing; do nothing.
+ }
+ }
+
+ @Override
+ public void pause() {
+ super.pause();
+ state = PlaybackState.PAUSED;
+
+ for (VideoAdPlayerCallback callback : adCallbacks) {
+ callback.onPause();
+ }
+ }
+
+ @Override
+ public void stopPlayback() {
+ super.stopPlayback();
+ onStop();
+ }
+
+ private void onStop() {
+ if (state == PlaybackState.STOPPED) {
+ return; // Already stopped; do nothing.
+ }
+
+ state = PlaybackState.STOPPED;
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ onStop();
+ for (VideoAdPlayerCallback callback : adCallbacks) {
+ callback.onEnded();
+ }
+ completeCallback.onComplete();
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ for (VideoAdPlayerCallback callback : adCallbacks) {
+ callback.onError();
+ }
+ onStop();
+ // Returning true signals to MediaPlayer that we handled the error. This will prevent the
+ // completion handler from being called.
+ return true;
+ }
+
+ public void addCallback(VideoAdPlayerCallback callback) {
+ adCallbacks.add(callback);
+ }
+
+ public void removeCallback(VideoAdPlayerCallback callback) {
+ adCallbacks.remove(callback);
+ }
+
+ @Override
+ public void setOnCompletionListener(OnCompletionListener l) {
+ throw new UnsupportedOperationException();
+ }
+}