Trial i Windows Store API
Cały czas rozpisuję się o nowościach i możliwościach jakie mają aplikacje metro wszystko fajnie jednak nie zapominajmy o rynku na jakim będą dostępne nasze aplikacje a mianowicie Windows Store. Obecnie na świecie jest ok 500 milionów komputerów PC z Windows 7. Niech nawet tylko 10% z nich przejdzie na Windows 8 to i tak dostęp będziemy mieć do 50 milionów potencjalnych klientów. Nie ma co robi warzenie (przynajmniej na mnie).
Microsoft daje nam narzędzia do łatwiejszego testowania aplikacji w wersji płatnej i próbnej. W przeciwieństwie do Windows Phone 7 tutaj mamy możliwość testowania kupna aplikacji jak również co jest nowością symulowania kupna konkretnych elementów w aplikacji lub grze. Ta druga opcja przyda się szczególnie przy grach.
Tak więc przejdźmy do o kodowania funkcjonalności kupowania. Wszystkie informacje o naszej aplikacji znajdują się w specjalnie przygotowanym pliku którego używamy do symulowania połączenia się ze sklepem. Plik prezentuję poniżej.
<?xml version="1.0" encoding="utf-16" ?> <CurrentApp> <ListingInformation> <App> <AppId>2B14D306-D8F8-4066-A45B-0FB3464C67F2</AppId> <LinkUri>http://apps.microsoft.com/app/2B14D306-D8F8-4066-A45B-0FB3464C67F2</LinkUri> <CurrentMarket>en-US</CurrentMarket> <AgeRating>3</AgeRating> <MarketData xml:lang="en-us"> <Name>Contoso Cookbook</Name> <Description>Metro recipes for Metro developers</Description> <Price>08.99</Price> <CurrencySymbol>$</CurrencySymbol> </MarketData> </App> <Product ProductId="ItalianRecipes"> <MarketData xml:lang="en-us"> <Name>Italian Recipes</Name> <Price>0.99</Price> <CurrencySymbol>$</CurrencySymbol> </MarketData> </Product> </ListingInformation> <LicenseInformation> <App> <IsActive>true</IsActive> <IsTrial>true</IsTrial> <ExpirationDate>2022-12-31T23:59:59.00Z</ExpirationDate> </App> <Product ProductId="ItalianRecipes"> <IsActive>false</IsActive> </Product> </LicenseInformation> </CurrentApp>
Jak widać znajdują się tutaj dwie główne sekcje. Pierwsza <ListingInformation> przechowuje informacje na temat aplikacji i dodatkowych produktów jakie można kupić w ramach aplikacji. Natomiast drugi element <LicenseInformation> zawiera informacje na temat licencjonowania aplikacji i produktów. Normalnie te informacje będą pochodzić ze sklepu. Jednak w naszym przypadku symulacji będą kopiowane do pliku WindowsStoreProxy.xml (nazwa może być dowolna) z którego system z czyta informacje. W pliku App.xaml.cs odnajdujemy metodę OnLaunched do której wstawiamy następujący kawałek kodu.
// Inocjowanie WindowsStoreProxy.xml var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Microsoft\\Windows Store\\ApiData", CreationCollisionOption.OpenIfExists); var file = await Package.Current.InstalledLocation.GetFileAsync("Data\\license.xml"); var newfile = await folder.CreateFileAsync("WindowsStoreProxy.xml", CreationCollisionOption.ReplaceExisting); await file.CopyAndReplaceAsync(newfile);
Te cztery linijki otwierają plik licencji jaki mamy w projekcie potem tworzą plik który będzie używany do symulowania zakupi i kopiują dane z pierwszego do drugiego.
Kolejnym krokiem jest stworzenie klasy która będzie przechowywać informacjie na temat licencji tak samo jak się robiło dla aplikacji WP7. Przykładową implementację prezentuję poniżej.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.ApplicationModel.Store; using Windows.Foundation; namespace ContosoCookbook { class AppLicenseDataSource : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private bool _licensed = false; private string _price; public AppLicenseDataSource() { if (CurrentAppSimulator.LicenseInformation.IsTrial) { CurrentAppSimulator.LicenseInformation.LicenseChanged += OnLicenseChanged; GetListingInformationAsync(); } else _licensed = true; } private async void GetListingInformationAsync() { var listing = await CurrentAppSimulator.LoadListingInformationAsync(); _price = listing.FormattedPrice; } private void OnLicenseChanged() { if (!CurrentAppSimulator.LicenseInformation.IsTrial) { _licensed = true; CurrentAppSimulator.LicenseInformation.LicenseChanged -= OnLicenseChanged; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("IsLicensed")); PropertyChanged(this, new PropertyChangedEventArgs("IsTrial")); PropertyChanged(this, new PropertyChangedEventArgs("LicenseInfo")); } } } public bool IsLicensed { get { return _licensed; } } public bool IsTrial { get { return !_licensed; } } public string LicenseInfo { get { if (!_licensed) return "Trial Version"; else return ("Valid until " + CurrentAppSimulator.LicenseInformation.ExpirationDate.LocalDateTime.ToString("dddd, MMMM d, yyyy")); } } public string FormattedPrice { get { if (!String.IsNullOrEmpty(_price)) return "Upgrade to the Full Version for " + _price; else return "Upgrade to the Full Version"; } } } }
W konstruktorze klasy pobierane są informacje z symulatora zakupu. Między innymi sprawdzana jest wersja w przypadku wersji trial dodawane jest zdarzenie zmiany wersji. Nie będę dokładnie opisywał ponieważ klasa jest prosta. Natomiast kod odpowiedzialny za zakup naszej aplikacji prezentuje się bardzo prosto.
using Windows.ApplicationModel.Store;
CurrentAppSimulator.RequestAppPurchaseAsync();
Potrzebujemy dodać jedną przestrzeń nazw i uruchomić jedną metodę. Zostało napisać wygląd prezentowania tych informacji ale to już zależy do każdej aplikacji indywidualnie. Po napisaniu wyglądu jesteśmy gotowi do uruchomienia. U mnie prezentuje się następująco.
Okno symulujące zakup jak widać mamy kilka scenariuszy do wyboru.
Ostatnim elementem symulacji zakupów o jakim chcę opowiedzieć jest kupowanie pojedynczych produktów które zwiększają funkcjonalność naszej aplikacji lub gry. Odbywa się to bardzo podobnie jak kupno aplikacji. Znów musimy stworzyć klasę która będzie informować o stanie danego produktu. Przykładowa implementacja.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.ApplicationModel.Store; namespace ContosoCookbook { class ProductLicenseDataSource : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private const string _name = "ItalianRecipes"; private bool _licensed = false; private string _price; public string GroupTitle { set { if (value != "Italian") _licensed = true; else if (CurrentAppSimulator.LicenseInformation.ProductLicenses[_name].IsActive) _licensed = true; else { CurrentAppSimulator.LicenseInformation.LicenseChanged += OnLicenseChanged; GetListingInformationAsync(); } } } private async void GetListingInformationAsync() { var listing = await CurrentAppSimulator.LoadListingInformationAsync(); _price = listing.ProductListings[_name].FormattedPrice; } private void OnLicenseChanged() { if (CurrentAppSimulator.LicenseInformation.ProductLicenses[_name].IsActive) { _licensed = true; CurrentAppSimulator.LicenseInformation.LicenseChanged -= OnLicenseChanged; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("IsLicensed")); PropertyChanged(this, new PropertyChangedEventArgs("IsTrial")); } } } public bool IsLicensed { get { return _licensed; } } public bool IsTrial { get { return !_licensed; } } public string FormattedPrice { get { if (!String.IsNullOrEmpty(_price)) return "Purchase Italian Recipes for " + _price; else return "Purchase Italian Recipes"; } } } }
W tej implementacji właściwość GroupTitle steruje tym czy dany element trzeba kupić. Jest tak ponieważ kod pochodzi od aplikacji kucharskiej a kupić można przepisy włoskie a inne są darmowe. Kod odpowiedzialny za uruchomienie kupna tego produktu jest następujący.
using Windows.ApplicationModel.Store; CurrentAppSimulator.RequestProductPurchaseAsync("ItalianRecipes");
Jak widać Microsoft dał nam spory silnik do testowania kupowania aplikacji.