Today I read that the Windows Phone team had added some new samples. I was excited to take a look at the Free App With Paid Products sample to see if they had done anything different than I had in Disney Expedition. When looking at the sample I was even more excited to see this in MainPage.xaml.cs
// Each in-app product is identified by a string that is unique for the owning app. This value
// is an example identifier for a product that causes the display of ads to be removed from the app.
private const string PRODUCTID = "AdRemovalProductIdentifier";
This little snippet was exactly what I was doing! Using the In App Purchase(IAP) model to remove ads from my app. I continued to look at the sample only to become disappointed that the sample did not really show how to remove ads with an In App Purchase. Okay, I wasn’t that disappointed. Removing an ad on a single page once the purchase is made is easy enough to do if you follow the sample. But there are many unanswered questions
- What if you have multiple pages with ads?
- How do you give the option (button or menu item) to remove ads across many pages?
- How do you remove/hide the ad for a new page if the purchase has been made?
When using IAP to remove ads you should give the user the ability to remove the ads on every page that has ads.
Note: Another option is to have a central location like settings to remove ads. I don’t like this option because often users don’t go to the settings.
The option you give users could be either a button on the ApplicationBar or a MenuItem. While a menu item is not as visible as a button you can be guaranteed that you will have room in the menu to put an option. The menu item should be visible if the IAP has not been made and be hidden if the purchase has been made. Often times you show/hide items with a BooleanToVisibilityValueConverter. Menu items do not have this ability so you’ll want to do this in code. A good place to do this is within the Loaded event of the PhoneApplicationPage. To make this code reusable it will be placed in a class called AdPhoneApplicationPage which inherits from PhoneApplicationPage. This allows you to make any page in your app an AdPhoneApplicationPage rather than a PhoneApplicationPage.
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
bool adsRemoved = App.AdsRemoved;
// If there is not an ApplicationBar on this page we will need to add one
if ((ApplicationBar == null) && (adsRemoved == false))
{
ApplicationBar = new ApplicationBar
{
// Minimize the bar as it will only have a menu item
Mode = ApplicationBarMode.Minimized
//TODO: Set Background/Foreground color to match your app theme
};
}
// ApplicationBar is null and ads have been removed. Nothing to do!
else if (ApplicationBar == null) return;
ApplicationBarMenuItem removeAdMenuItem = null;
if (ApplicationBar.MenuItems != null)
{
foreach (ApplicationBarMenuItem menuItem in ApplicationBar.MenuItems.OfType<ApplicationBarMenuItem>())
{
if (menuItem != null)
{
if (menuItem.Text == "remove ads")
{
removeAdMenuItem = menuItem;
break;
}
}
}
}
if ((removeAdMenuItem != null) && adsRemoved)
{
// Purchase has been made. Remove the option
removeAdMenuItem.Click -= RemoveAdMenuItemOnClick;
ApplicationBar.MenuItems.Remove(removeAdMenuItem);
// TODO: Remove the ad
}
else if ((removeAdMenuItem == null) && (adsRemoved == false))
{
// Purchase has not been made, give them to option
removeAdMenuItem = new ApplicationBarMenuItem("remove ads");
removeAdMenuItem.Click += RemoveAdMenuItemOnClick;
ApplicationBar.MenuItems.Add(removeAdMenuItem);
}
}
The Loaded event will either add or remove the menu item to the ApplicationBar. If the page does not have an ApplicationBar one will be created to place the menu item in. If the purchase has been made the menu item will be removed. The AdsRemoved property is very similar to the one in the sample except that it caches the value of the purchase.
private static bool? _adsRemoved;
private bool AdsRemoved
{
get
{
if (_adsRemoved == null)
{
_adsRemoved = CurrentApp.LicenseInformation.ProductLicenses["RemoveAdsProductID"].IsActive;
}
return _adsRemoved.Value;
}
}
When the menu item is clicked, we’ll want to perform the IAP and if the user did complete the purchase, remove the menu item.
private async void RemoveAdMenuItemOnClick(object sender, EventArgs eventArgs)
{
try
{
// prompt the user to purchase
await CurrentApp.RequestProductPurchaseAsync("RemoveAdsProductID", false);
if (CurrentApp.LicenseInformation.ProductLicenses["RemoveAdsProductID"].IsActive)
{
// we're rich!
_adsRemoved = true;
// Remove the menu item
var menuItem = (ApplicationBarMenuItem)sender;
ApplicationBar.MenuItems.Remove(menuItem);
// TODO: Remove ad
}
}
catch (Exception)
{
}
}
Notice I had a TODO in there to remove the ad; after all, this is what we are intending to do. One option is to create a custom control that wraps the AdControl and hides it on load if the purchase has been made (I’ll blog about this awesomeness later). Another option is to traverse the child controls of the page to find the AdControl and hide it.
private bool RemoveAdControl(DependencyObject d)
{
var adControl = d as Microsoft.Advertising.Mobile.UI.AdControl;
if (adControl != null)
{
adControl.Visibility = Visibility.Collapsed;
return true;
}
var childCount = VisualTreeHelper.GetChildrenCount(d);
// start at the end of the children as the AdControl is likely to be at the bottom
for (int i = childCount - 1; i >= 0; i--)
{
bool adRemoved = RemoveAdControl(VisualTreeHelper.GetChild(d, i));
if (adRemoved) return true;
}
return false;
}
This simple method uses recursion to find the AdControl and hide it. Replace the TODO statements will one line of code.
You can download the entire AdPhoneApplicationPage here.
In case you do not know how to change the type of PhoneApplicationPage for your pages, here’s a little snippet
<localControls:AdPhoneApplicationPage
x:Class="VisuallyLocated.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VisuallyLocated.Controls">
<!-- content -->
</localControls:AdPhoneApplicationPage>
Make sure that your code behind then inherits from AdPhoneApplicationPage instead of PhoneApplicationPage.