Mostrando entradas con la etiqueta Android. Mostrar todas las entradas
Mostrando entradas con la etiqueta Android. Mostrar todas las entradas

Azure IoT Device Client SDK v 1.2.0 doesn't work with Xamarin Android apps

Several hours lost trying to deal with a connectivity issue between our Android app and a test Azure IoT server, just to find out that the problem was just having update the Client SDK nuget package (https://github.com/Azure/azure-iot-sdk-csharp)

The SendEventAsync seemed to never finish the task when using MQTT, and it threw an exception when AMQP (something related to Amqp constructor could not intialize the Configuration System).

After several hours assuming it was a fault in my code, or in the Azure setup, I checked the SDK release notes ( https://github.com/Azure/azure-iot-sdk-csharp/releases ), to find that last version was released a few days ago, with a major AMQP redesign.

Downgrading to version 1.19 simply did the trick...

World Enduro Rally

I'm happy to share the result of the work I've been doing in the last months: yesterday, World Enduro Rally came to light.



The game is available for XBoxOne, Steam, Android and Windows. You can get all the details at its Facebook page, or download directly here:





Hope you like it!

Manually copying Unity's APK and OBB files to an Android device for testing

When developing an Android game that is meant to be published through the PlayStore, there's a limitation regarding the maximum file size of the APK. It currently can't exceed 100 MB.

The thing is that games usually go beyond that limit. To deal with that, Google offers the so called APK Expansion Files. Every application can include up to 2 of them, with a maximum size of 2 GB each.

If your game goes beyond 100 MB, you can easily instruct Unity to split the game in two by checking the "Split Application Binary" in Edit->Project Settings->Player->Android->Publishing Settings.

Image result for unity split application binary

That will generate two files instead of one:
  • APK: the usual APK file with all the binaries, scripts etc, and the first scene included in the build settings (the scene with build index 0). You need to take care that the first scene is small enough so this APK doesn't go beyond 100 MB. In general, this scene is usually just a loading icon, or a game logo that is displayed while loading. 
  • OBB: A second Expansion File with the OBB, holding everything else. 
Some time ago, it was necessary to use a Unity plugin to deal with OBB loading, but this is no longer necessary. So, first of all, DO NOT USE that plugin. In fact, it hasn't been updated for years, and won't work in current versions of Unity. 

Current versions of Google PlayStore natively support the distribution of OBB files, so you won't need to do anything special besides providing the OBBs along with the APK when you upload your game.

Note: usually, the first time you upload your game to Google Play, it won't ask you for OBB expansion files (I guess that's a bug in the system). If that's your case, simply generate a second version of your game, and re-upload your APK. Then it will ask for Expansion Files properly. 

How to install the game locally, in a test device? 

In normal circumstances, you  just create one big APK, copy it manually to the SD-Card of your phone, and install the game by tapping on the APK file. If you do that now, the game will install fine but it won't be able to find the Expansion OBB files, so the first scene will load fine, but it will probably get stuck there. 

In order to let the game find the OBB files, you need to:

1.- Rename the OBB so it follows the convention:

Unity creates valid OBB files, but their names don't follow the convention Android expects. Your OBB file should have a name like the following: 

main.[VERSION_NUMBER].[PACKAGE_NAME].obb

Where [VERSION_NUMBER] is the highest digit of the version number you can find in the Player Settings:


And [PACKAGE_NAME] is the package name specified in the same screen, right above the version number:


So, in the example depicted in the images, the name of the OBB file would be:

main.1.com.iayucar.TestGame1.obb

2.- Copy the renamed OBB file to an specific location

The game will expect to find that OBB file in a location like:

[INSTALL_LOCATION]\Android\obb\[PACKAGE_NAME]

Where [PACKAGE_NAME] is the same value described above, and INSTALL_LOCATION refers to whether the game is installed in the internal memory or the external SD-Card (this depends on your own settings). 

As our game is configured to be installed in an external SD-Card preferably, the folder where we need to paste the OBB file is:

\\SDCard\Android\obb\com.iayucar.TestGame1\

And that's it. Now you can launch the game, and it will properly find the additional contents included in the OBB Expansion File. 

Make some cash developing for Windows Phone

imageIn this post I’ll try to share my thoughts and experience developing applications for the Windows Phone Marketplace. I’ll share with you why I think it’s a great platform for an indie or pro developer, and I’ll also try tell you what did work in this last years, what didn’t, and a couple of hints that could help you improve a bit your revenue.
First things first: I’m not a professional developer for mobile devices. I am indeed a software developer in other fields, but as I always was interested in this kind of devices, I decided to give them a try. All my interest was to test the investment and resources needed for a development like that, and the return profit one can expect from it.
Although not everything I tried really worked, my applications has been downloaded almost 22.000 times since published (today is January 17th, 2012), with a full version conversion rate of almost a 16%. This is my total and daily download chart since October 2010:
image

A bit of history

Windows BlocksMy first application for the phone, published in October 23rd 2010, was Windows Blocks. It was a very simple Arkanoid-like game, that was first developed as a C# game developing tutorial for a webcast. The title of the webcast was: “Developing a full Windows Phone game in less than 2 hours”. Once it was done for the webcast, I decided to finish the few details still remaining, and to publish it in the marketplace, just to learn how it worked. Of course, it was not a hit, and the reviews weren’t excellent (remember it was done in a few hours), but it was a good first try.
In those days (a couple of weeks after the Marketplace was launched), the publication process was a bit of a pain, I must say. But it has been improved a lot since then, and now it is far easier.
Gravitards
Then, I decided to make something more serious, investing more resources and effort, what lead to my second Windows Phone game: Gravitards. It was a beautiful skill game in which the player had to move a ball through several levels with progressive difficulty. This time, reviews were much better, and sales were better too, but honestly not enough to cover the hours invested in the game.
In the first case, it was not surprising that things didn’t work. After all, I didn’t invested too much efforts in the game, so that was predictable. The second one though was much more detailed and I put much more care into it, so I expected more. However it was, it had a couple of drawbacks users complained about that kept sales down I guess.
Silver NavigatorBut then came my third attempt: Silver Navigator. It was not a game, but a GPS-like application.
One day, I had to drive to Madrid. My ex-wife kept my old GPS, and it was a holiday here, so I couldn’t buy a newer one. I decided to check in the Marketplace if there was something like that in there, but there wasn’t yet. Then, I remembered that I saw once a Silverlight Bing Maps control over there, that had pretty decent features. So I took Visual Studio, created a new Silverlight project, and put one of those controls in there. I found a couple of web services that could calculate routes, and added several features like route painting over the Bing Maps control. In three hours, I had a very primitive version of Silver Navigator, that allowed me to drive through Madrid without a problem.
As there was not anything like that in the marketplace yet, I decided to invest some hours on it, and after two or three weeks adding more features, a User Interface, and some other stuff, I published the first version of Silver Navigator on April, 2011. It wasn’t a full-featured GPS device, as maps and route calculation required an internet connection, but it did a pretty decent job inside cities, where signal always works, and in fact that’s where I most use GPS devices, instead of open roads.
The impact was remarkable. Downloads started to grow, and it stayed above 100 downloads per day for several months. I opened a Facebook page for it, where users could write their opinions, and that was a win with no doubt. People started to ask for new features, like voice guidance and automatic map rotation. As things were doing well, I decided to go further, and to implement those features, releasing a couple of new versions which had a great acceptance by the users.
Now, Silver Navigator has been downloaded almost 18.300 times, with a full version conversion rate of almost a 18%.

The numbers

Up to date (January 17th, 2012), the statistics of my applications are:
  • Windows Blocks: Total Downloads: 620           Conversion Rate: 10.1 %
  • Gravitards:         Total Downloads: 2512         Conversion Rate:  5.6 %
  • Silver Navigator: Total Downloads: 18269        Conversion Rate: 17.5 %
Note: What these numbers mean? All my applications in the Windows Phone Marketplace had a free/demo mode, that users can try first, and then decide to buy them or not. The conversion rate is the percentage of the total downloads that truly became sales.
So, as you can see, it’s been a great result for an indie developer, and even more if you think of the amount of hours and resources invested in the development of these applications. The effort/reward balance has been really positive:
  • Windows Blocks: Aprox. hours invested: 5      Total units sold up to date:  63
  • Gravitards:         Aprox. hours invested: 280   Total units sold up to date:  141
  • Silver Navigator: Aprox. hours invested: 240   Total units sold up to date:  3202

Hints to keep in mind when publishing an application

First, you should care about maximizing the visibility of your application in the Marketplace. This is influenced by many factors, like the number of total applications in there, how attractive it results for the users, etc. There are some things you cannot do anything about, like the number of apps in the marketplace, but you can help out in many others. In general, keep the following things in mind:
  • Try to think of a good name for your application. I failed in that with “Gravitards” (some people talked about the name, saying it reminded of “retard” or “retarded”, haha… :-).
  • Publish it in the appropriate category, as people uses categories to filter searches.
  • Put a lot of attention and detail to graphics and presentation of your application. Icons, pictures, and all media material should be well designed. It should be concise and clear, but also beautiful and attractive when possible. This is more important than you think…
  • Promote your application as much as you can. If you can afford it, pay for ads at Facebook or wherever you want. If you think you have a really good idea, contact your local Microsoft representative and ask for support. They are amazing, and will really help you if they can.
  • Give your users the chance to talk to you. Create a Facebook page for your application, and include a link on your app that takes you directly there.
  • Send the details of your application to most common mobile websites our there, like WMPowerUser, XDA Devs, etc etc. They do a great job reviewing apps.
  • Put a free/demo mode on your app. Give the users the chance to try it for free. Many people won’t pay without trying first.
Secondly, you will probably care about having the highest conversion rate possible. It’s nice to see that your application is being downloaded thousands of times, but it’s even nicer to see that becoming sales :)  !!!. So, to improve your conversion rate, keep in mind:
  • Your free/demo mode should be long enough to allow users trying the application well. But more importantly, it should be long and featured enough to make them want the full version. And this is important. If it’s too short, there might be not enough time for them to appreciate the app, and to get into it. If it’s too long, they might get the feeling that they had enough with the demo. In a game, for example, give them the chance to be playing for 10 or 15 minutes with the demo. I made this mistake with Gravitards, where the demo was only one level long (only 2-3 minutes playing), and people found that way too short.
  • Easy of use: this is crucial. Keep an eye on professional apps or games, and study how they are done. Why options are laid out that way, why menus have that shape. Everything is studied. Try to copy from the people that knows how to do it. Users should find easily what they want to do, with as few clicks as possible.
  • If it’s a game, keep an eye on difficulty level. This is very important, and very difficult to properly tune. If the game is too easy, people will get bored soon, may be even with the demo only, and will not buy the full version. If it’s too hard, it could be frustrating for users, and stop playing. However, if you are about to make this mistake, it’s better to make it a hard game than an easy game (there’s always people out there that takes that as a challenge).
  • Responsiveness of your application. Nowadays, people don’t tolerate slow or unresponsive applications. Easy as that. Everything should be fluid.
  • Localize your app to as many languages as possible, and of course include English as a minimum. Your app will be sold worldwide !!!
  • Consider including publicity in the free/demo version of your application. It’s a great way to make some cash with the free downloads.
  • Think of a reasonable price. It’s sometimes better to sell a lot of copies at a low price, than selling a few, more expensive copies.

Why do I think that Windows Phone is a GREAT platform to develop for?

There are many reasons, but some of them are:
  • Microsoft’s support to developers is great, with direct contact that really helps, continuous contests and prizes for developers, app promotion as “App of the week”, etc.
  • The number of Windows Phone devices in the market is raising amazingly. Specially since the Nokia Lumia came out, Windows Phone is now the hit. Everybody is talking about the Lumia.
  • Technically, there are very few hardware differences between WP7 devices, so you can be pretty sure that your application will work well on every device. This is a BIG problem in other platforms, like Android, were the phones are frustratingly segmented.
  • Microsoft offers development tools for free, like Visual Studio Expresss, XNA Game Studio, etc. The investment needed to start earning money from your developments is almost ZERO. And that doesn’t happen in any other platform.
  • You can re-use your WP developments for the XBox too, what is really great. Very soon, you will also have a Marketplace for Windows, what is going to be an awesome milestone in software development.
  • The technology behind your developments (.Net Framework, XNA Game Studio, Silverlight, etc), has a very affordable learning curve. In fact, it’s the easiest and most efficient platform in the market. And for free !!!
  • The development tools are, simply, the best. And this deserves an specific chapter.

Development tools

Microsoft is not a clear software leader in all fields. But in Development Tools, it is. I think that every software developer opinion is the same. Microsoft Visual Studio has no rival.

And this is no exception. The set of tools that you will use to develop for the Windows Phone is truly amazing (and free). The Windows Phone emulator is a masterpiece, with a mindblowing debugging speed. Those of you that have tried developing for Android, for example, know what I´m talking about.
If you plan to develop a game, the XNA Game Studio is simply awesome. It provides you with a full featured framework for game creation that allows you to develop last-gen games, again with a learning curve like no other.
If you plan to develop and application, Silverlight is again very cool. Last-gen user interfaces, beautifully animated and fluid, all with a .Net Framework basement, with thousands of classes ready for you, that will make your developments a piece of cake.
I have developed for Android for some time now, and believe me, it’s a frustrating experience compared to all of this.

My conclusion

If you are planning a new development for a mobile platform, Windows Phone has become THE way to go. When it first came out, it was soon positioned as a clear candidate and a tough competitor for iOS and Android. But now, in my modest opinion, it has no rival.
No matter if you are a pro developer or an indie one, the design and development period of your life-cycle will be much easier and shorter. Of course, if you have the resources to make a cross-platform development, and you can make your app to work in all iOS, Android and Windows Phone, go for it.
But if you’re a indie developer, or simply don’t need that level of compatibility, your best bet is WP. Currently, it is clearly the platform which requires the lowest initial investment (almost zero), the most efficient and easy (and therefore the one that requires less effort and work), and step by step it’s becoming the one with the highest return rates. You just need to check this blog post.
Cheers !!

XNA/Android cross-platform and strongly-typed access to game contents


In this previous post, we already showed how to use T4 templates for several use cases, with a special focus on Resource Files (ResX). Now we are going to complete that post with another one, focused specifically on Content management.
What we want to achieve is an elegant, cross-platform, and strongly-typed way of accessing contents in our projects.

The XNA approach to contents

XNA identifies contents with Asset Names, in the form of strings, but it doesn’t offer any form of strong-typed access, what is very bug-prone, because if you misspell the name of an asset, you won’t notice until runtime, or you won’t notice ever…

The Android approach to contents

Android already offers strongly-typed access to contents that are placed below the “Resources” special folder. Unfortunately, there are a lot of limitations for the contents inside that folder. One of the most evident (and stupid), is that contents cannot be re-arranged into subfolders, what makes it almost un-usable for medium-big projects. Besides that, the kind of access Android gives to that folder is through INT identifiers, what conflicts with the XNA way of doing this (which uses Asset names).
One of the possible solutions is to move our contents to the “Assets” folder, where things can be arranged arbitrarily, and where assets are identified with a string, very much like in XNA. Too bad that Android doesn’t offer strongly-typed access to that folder…

What we want to achieve

1.- We want to be able to arrange our contents in sub-folders, so in Android, we will have to go to the Assets approach, instead of the Resources one.
2.- That solves also the unification of types when identifying assets. In both cases (XNA and Android), we will be using Asset Names as strings.
3.- In both sides we will need to provide a strongly-typed way of accessing contents.
4.- We want the exact same interface that is finally published outwards, so that every piece of code that uses our strongly-typed classes, write the exact same code no matter which platform we are coding on.

An implementation using again T4 templates

Again, we will write down two different T4 templates, one for XNA and one for Android. Both of them will have to do merely the same, but with some minor differences. Let’s see them:

Example: XNA T4 Template to give strongly-typed access to contents

This template will be placed wherever we want to use it. It can be in the main XNA Game project, or in a library project shared all around. Basically, it will search inside the Visual Studio solution for the Game’s Content Project. Once found, it will iterate recursively through the file and folder structure of the project, generating classes that give strong-typed access to each Asset.
Imagine we have the following structure in our contents project:
image
We want to get an output like the following:
image
As you can see, we will use namespaces to represent the tree-structure of folders in the content project. Once we find a folder with one or more content files, we will create a class named “Keys” that will hold properties to access asset names. We will also create an enumeration with all the assets found at that level. This way, we also allow to navigate through the contents tree, if needed.

The code

The XNA template that generates that, is the following:
<#
//  --------------------------------------------------------------------------------------------------------------
//  Template: Generates C# code to give strongly-typed access to Asset files
//  Author: Inaki Ayucar
//  Website: www.graphicdna.net
//  Based on the work of: http://blog.baltrinic.com
//  -----------------------------------------------------------------------------------------------------------
#>
<#@ template debug="true" hostspecific="true" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#  // ------------------------------------------------------------------------------------------------------
    // Get global variables
    // ------------------------------------------------------------------------------------------------------
    var serviceProvider = Host as IServiceProvider;
    if (serviceProvider != null)
        Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
 
 
    // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
    if (Dte == null)
        throw new Exception("T4MVC can only execute through the Visual Studio host");
 
 
    Project = GetXNAContentsProject(Dte);
    if (Project == null)
    {
        Error("Could not find XNA Content Project.");
        return"XX";
    }   
    Project prjT4 = GetProjectContainingT4File(Dte);
    if (prjT4 == null)
    {
        Error("Could not find Template's project");
        return"XX";
    }   
 
     AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
     RootNamespace = prjT4.Properties.Item("RootNamespace").Value.ToString();
    // --------------------------------------------------------------------------------------------------
#>
// ------------------------------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------------------------------
using System.Threading;
 
 
<#
    try
    {
        // We are storing in a List<ResourceEntry> (declared below) a list with all string entries of
        // all files found matching our search criteria
        AllEntries = new Dictionary<string, List<AssetFileInfo>>();
 
        string projectFileName = Path.GetFileName(Project.FullName);
        string projectFullPath = Project.FullName.Substring(0, Project.FullName.Length - projectFileName.Length);
 
        // Find files on our project that match our search criteria (recursively), and store every string
        // entry on those files
        FindResourceFilesRecursivly(projectFullPath, Project.ProjectItems, "");
 
 
        foreach(string path in AllEntries.Keys)
        {
            if(path == null || path == "")
                continue;
 
            List<string> enumNames = new List<string>();
 
            string aux = path;
 
            // To avoid conflict names with namespaces, the class names will always be "Keys"
            string className = "Keys";
 
            if(aux.EndsWith("\\"))
                aux = aux.Remove(aux.Length - 1, 1);
            string pathNameSpace = aux.Replace("\\", ".");
 
            // Start of namespace
            if(pathNameSpace != "")
                WriteLine(string.Format("namespace {0}.Assets.{1}", RootNamespace, pathNameSpace));
            else WriteLine(string.Format("namespace {0}.Assets", RootNamespace));
            WriteLine("{");
 
            // Start of class
            WriteLine(string.Format("\tpublic class {0}", className));
            WriteLine("\t{");
            foreach(AssetFileInfo info in AllEntries[path])
            {  
                string filenameWithoutExt= Path.GetFileNameWithoutExtension(info.File);
                WriteLine(string.Format("\t\tpublic static string {0}", filenameWithoutExt));
                WriteLine("\t\t{");
                WriteLine(string.Format("\t\t\tget  {{ return \"{0}\"; }}", info.AssetName.Replace(@"\", @"\\") ));
                WriteLine("\t\t}");
 
                enumNames.Add(filenameWithoutExt);
            }
 
            // Start of Enum
            WriteLine("\t\tpublic enum eKeys");
            WriteLine("\t\t{");
            foreach(string enumname in enumNames)
                WriteLine(string.Format("\t\t\t{0},", enumname));
            // Close enum
            WriteLine("\t\t}");
 
 
            // Close class
            WriteLine("\t}");
 
 
 
            // Close namespace
            WriteLine("}");
        }
 
    }
    catch(Exception ex)
    {
        Error(ex.ToString());
    }
#>
 
 
<#+ // --------------------------------------------------------------------------------------------------------
    // Class feature control block:
    // Remarks: Identified by the #+ mark, allows to define variables, methods, etc
    // --------------------------------------------------------------------------------------------------------
    const string Kind_PhysicalFolder = "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}";
    bool AlwaysKeepTemplateDirty = true;
    static DTE Dte;
    static Project Project;
    static string AppRoot;
    static string RootNamespace;
    static Dictionary<string, List<AssetFileInfo>> AllEntries;
    static List<string> SupportedExtensions = new List<string>() {".dds", ".png", ".bmp", ".tga", ".jpg"};
 
    /// <Summary>
    /// FindResourceFilesRecursivly
    /// Remarks: Searches recursively in the files of our project, for those which are in the same folder
    /// that this file, or below, and that have extensions included in the supported extension list
    /// </Summary>
    void FindResourceFilesRecursivly(string pProjectFullPath, ProjectItems items, string path)
    {
        string assetRelativePath = path.TrimStart(new char[1]{'.'});
        assetRelativePath = assetRelativePath.Replace('.', '\\');
        foreach(ProjectItem item in items)
        {       
            if(item.Kind == Kind_PhysicalFolder)
                FindResourceFilesRecursivly(pProjectFullPath, item.ProjectItems, path+"."+item.Name);
            else
            {               
                // check if extension is supported
                string extension = Path.GetExtension(item.Name).ToLowerInvariant();
                if(SupportedExtensions.Contains(extension))
                {
                    string itemFileName = item.FileNames[0];
                    if(itemFileName == null)
                        continue;
 
 
                    AssetFileInfo info = new AssetFileInfo();
 
                    info.AssetName = itemFileName.Remove(0, pProjectFullPath.Length);
 
                    // XNA require que los asset names no tengan extensión
                    info.AssetName = info.AssetName.Substring(0, info.AssetName.Length - extension.Length);
 
                    info.File = item.Name;
                    info.Path = itemFileName.Substring(0, itemFileName.Length - item.Name.Length);
 
                    if(!AllEntries.ContainsKey(assetRelativePath))
                        AllEntries.Add(assetRelativePath, new List<AssetFileInfo>());
 
                    AllEntries[assetRelativePath].Add(info);
                }
            }
        }
    }
    /// <Summary>
    /// GetXNAContentsProject
    /// Remarks: http://www.codeproject.com/KB/macros/EnvDTE.aspx
    /// </Summary>
    Project GetXNAContentsProject(DTE dte)
    {
        foreach(Project prj in dte.Solution.Projects)
        {
            // XNA Content projects define this property. Use it to identify the project
            if(!HasProperty(prj.Properties,
                 "Microsoft.Xna.GameStudio.ContentProject.ContentRootDirectoryExtender.ContentRootDirectory"))
                continue;
 
            return prj;         
        }    
        return null;
    }
     /// <Summary>
    /// GetProjectContainingT4File
    /// Remarks:
    /// </Summary>
    Project GetProjectContainingT4File(DTE dte)
    {
 
        // Find the .tt file's ProjectItem
        ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
 
        // If the .tt file is not opened, open it
        if (projectItem.Document == null)
            projectItem.Open(Constants.vsViewKindCode);
 
        if (AlwaysKeepTemplateDirty) {
            // Mark the .tt file as unsaved. This way it will be saved and update itself next time the
            // project is built. Basically, it keeps marking itself as unsaved to make the next build work.
            // Note: this is certainly hacky, but is the best I could come up with so far.
            projectItem.Document.Saved = false;
        }
 
        return projectItem.ContainingProject;
    }
    /// <Summary>
    /// Struct: ResourceEntry
    /// Remarks: Stores information about an entry in a resource file
    /// </Summary>
    private bool HasProperty(Properties properties, string propertyName)
    {
        if (properties != null)
        {
            foreach (Property item in properties)
            {
                //WriteLine("// " + item.Name);
                if (item != null && item.Name == propertyName)
                    return true;
            }
        }
        return false;
    }
    /// <Summary>
    /// Struct: ResourceEntry
    /// Remarks: Stores information about an entry in a resource file
    /// </Summary>
    struct AssetFileInfo
    {               
        public string AssetName {get;set;}
        public string Path { get; set; }
        public string File { get; set; }
    }  
#>

Usage

One the T4 template is included on your solution, the way of accessing Assets in XNA is like the following:
Content.Load<Texture2D>(GDNA.PencilBurst.Assets.Textures.UI.Keys.circleT);
All absolutely strong-typed, much less bug-prone.

Android version

Once you have that as a start point, developing the Android version is pretty straight-forward. You just need to change a couple of things:
  • Instead of searching for the Contents Project inside your solution, you will need to search for the main project, and look for the “Assets” subfolder.
  • Asset names do include file extensions in Android, so be sure to not remove them (like in XNA)
  • Also, asset names will need to replace ‘\’ for ‘/’, so you can correctly invoke:
stream = mContext.Assets.Open(pAssetName);
And that´s all !!!