Saturday, August 28, 2010

MoAds - Custom Win Phone 7 Ad Control with AdMob support

Update: Codeplex is Garbage, I've been waiting 3 hours for the source control server to let me upload. In the meantime, download the source or the Binaries from my dropbox account or you can download the source from the project page at BitBucket.

Update 2: Microsoft has released a much better ad serving solution than AdMob, this was just a stop gap in case nothing official was released.  This control is old and busted, check out the Microsoft Ad SDK for the new hotness.


After seeing a project on CodePlex that allowed you to display AdMob ads in a Win Phone 7 application, I decided it would be a good project for another custom control.


Custom Styling, Animation and Templates


The main problem I saw with the existing control was that it didn't take advantage of the styling and "Blendability" of the Win Phone 7 platform. Basically, the control used a web browser control to display an HTML template that was updated with ad information.




Here is an example of three different styles for the same template, the top is the default template, the middle is a text only style, and the bottom shows a fading animation when the ads are refreshing. The animations are helped by utilizing the three states for the AdDisplay control; Loading, Normal and Error.


Custom Ad Providers


Another issue I saw was that it relied solely on AdMob, with very little flexibility for hooking up another Ad service (like a rumored Bing Mobile Ad Service), so I added an adapter pattern to the control.


Here is an example of using the provided AdMob Adapter with the control (notice this can all be done in XAML, no code behind).


<!-- Default template example -->
<moad:AdDisplay
    Height="70"
    VerticalAlignment="Top"
    Foreground="White"
    RefreshSeconds="30">
    <moad:AdDisplay.AdAdapter>
        <moad:AdMobAdapter
            PublisherId="YourPublisherID"
            CookieString="YourCookieString"
            AdKeywords="Boston"
            UseTestMode="True"/>
    </moad:AdDisplay.AdAdapter>
</moad:AdDisplay>


I'm hoping to put up another post to talk about creating a custom "In-House" ad provider.


You can download the source from my DropBox account, or at the created project on Codeplex called MoAds.


Now Playing - Goodie Mob - Get Rich to This

24 comments:

  1. What's the cookieString needed for and what value would I put?

    thanks,
    Sam

    ReplyDelete
  2. The cookie string is a way to uniquely identify a "client". It's used for analytics. It should really be generated from some unique device ID, but in the beta I haven't figured out how we can get a unique id for the phone.

    More information about the AdMob request parameters are at: http://developer.admob.com/wiki/Requests. But for the most part, the AdMob Documentation is pretty useless.

    The "o" parameter is what you're looking for.

    ReplyDelete
  3. Hi

    I'm having trouble getting it to work. This is my code:







    It is loading, but I dont see any add. What is cookiestring and AdKeywords for?

    Thanks

    ReplyDelete
  4. ups, cannot post xaml code here...
    here is the code I am using:

    http://dl.dropbox.com/u/3784027/moads.txt

    ReplyDelete
  5. @anon
    Check the previous comments;
    "The cookie string is a way to uniquely identify a "client". It's used for analytics. It should really be generated from some unique device ID, but in the beta I haven't figured out how we can get a unique id for the phone."

    As for ads not showing up, AdMob is sketchy about showing ads for people with little or no traffic. If you don't get enough traffic/impressions they won't show you any ads. If it is working in test mode, then more than likely admob is just not serving you ads. I'd recommend the MS Ad Control from here; http://advertising.microsoft.com/mobile-apps

    ReplyDelete
  6. Thanks for the great job with the Control Jacob. For people like me living outside the US it is currently the only solution for Ad Supported Trial Versions on WP7.

    ReplyDelete
  7. Thanks, be sure to check out the follow up post if you are interested in creating your own ad providers:

    http://jacob4u2.blogspot.com/2010/08/show-custom-in-house-ads-in-win-phone-7.html

    ReplyDelete
  8. How did you get the publisher id from admob? It requires you to add a site or an app type and WP7 is not an option. How did you get around this?

    ReplyDelete
  9. I think I used either the IPhone or Mobile website choice.

    ReplyDelete
  10. Jacob,

    Thanks for publishing this. Although the MS Pubcenter ad control makes this somewhat obsolete in the US, for outside the US, other ad providers are still necessary, and I would like to make this work.

    I have it hooked up and displaying ads, but when I click on the ad, it goes to the wrong tab in the browser and displays a blank screen. Any ideas?

    - Rick

    ReplyDelete
  11. I've noticed that happening on other web browser tasks, not just for the ad mob ones. Especially in the emulator. The code that handles the click and starts the web browser task can be found in AdDisplay.cs in a method called HandleAdTouch()

    Once I hand off to the web browser task, I really have no control over what happens. But it would be good to check that there is actually an ad URL being parsed and set correctly.

    ReplyDelete
  12. I think there is a memory leak in the control. I have an ad in the second page of my application. Each time I go to the second page and then back to the first page (by closing the 2nd page) I see an increase in the total memory used in my application (between 2mb and 4mb). I noticed that threads created by the control for fetching ads are not terminated when I close the page. So each time I go to the 2nd page I have +1 thread that throws a WebException when I have no connection. So after 10 visits I can see 10 WebExceptions.
    Can you confirm this? And is there some way that it can be fixed?

    ReplyDelete
  13. @Bill: That seems entirely plausible. I don't stop the timer that refreshes the ads when you navigate to a new page. You can fix this by adding a method called StopRefresh in the AdDisplayControl class. This method should just call adReloadTimer.Stop(). You can either call this before you navigate to the next page, or go ahead and implement the IDisposable interface and call that method on dispose.

    ReplyDelete
  14. @Bill: Here is some example code you can paste into AdDisplayControl.cs

    public void StopRefreshing()
    {
    adReloadTimer.Stop();
    }

    public void Dispose()
    {
    this.StopRefreshing();
    }

    ReplyDelete
  15. yes that did the job thanx :)

    ReplyDelete
  16. Hi Jacob, it's bill again :)
    I found another thing in your control. It's difficult to reproduce it but it needs to be fixed.
    There is a webClient exception about not supported concurrent I/O operations. This exception is raised when the 3G signal is very poor but there is connection. I think the problem is that you try to reuse the same webclient without checking if the previous is completed.

    ReplyDelete
  17. @Bill It looks like you're right about the webclient sharing being the problem. This is actually fixed on the latest codeplex source, but you should be able to copy the changes if you feel up to it. Here is what to do;

    1. Remove the static client property that is shared
    2. Remove the constructor stuff dealing with the client property.
    3. Replace the last StartService (The main one that does all the work) with this:

    public WebHeaderCollection Headers { get; private set; }
    /// summary>
    /// Starts the service call.
    /// </summary>
    /// <param name="serviceUri">The service URI.</param>
    /// <param name="method">The method.</param>
    /// <param name="postData">The post data.</param>
    protected virtual void StartServiceCall(Uri serviceUri, HttpMethod method, ContentType type, string postData)
    {
    var client = new WebClient();

    client.Headers = this.Headers;

    if (method == HttpMethod.GET)
    {
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);

    // Remove the content type header, in case it messes with our get.
    if (client.Headers["Content-Type"] != null)
    client.Headers["Content-Type"] = null;

    client.DownloadStringAsync(serviceUri);
    }
    else
    {
    client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted);

    switch (type)
    {
    case ContentType.UrlEncoded:
    client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
    break;
    case ContentType.JSON:
    client.Headers["Content-Type"] = "application/json";
    break;
    default:
    client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
    break;
    }
    client.UploadStringAsync(serviceUri, "POST", postData);
    }
    }

    That should do the trick. Thanks for the input.

    ReplyDelete
  18. @Bill: sorry that was in the BaseJsonService.cs class in the JSonHelpers\Service4u2Lib\Json directory. Around line 229.

    ReplyDelete
  19. @Bill: Then in the AdMobService.cs you'll get an error about the client not being there, replace the line with this:

    base.Headers["User-Agent"] = userAgent;

    ReplyDelete
  20. What kind of application did you create at admob? You can check this screenshot. Feel free to make comments there: https://docs0.google.com/drawings/edit?id=1ajmRPp8BImu2sZLxkzwNTD8TqW_K7nHGuerIte8IRn4&authkey=CO6rrvAB


    (I'm outside of US. So, I'm gonna use with your solution)

    ReplyDelete
  21. I used the SmartPhone web. I used my web services site url, but I don't know that it really matters.

    ReplyDelete
  22. So, you just used your personal homepage as URL? However, this URL won't interact with admob at any time later?

    ReplyDelete
  23. Hi Jacob,

    does this control work always ? I get 'connection error' on both the emulator and the device (windows phone 7).

    thanks in advance,
    Domi
    Belgium

    ReplyDelete
  24. Not sure, it's been over 3 years since I've used it.

    ReplyDelete