Sunday, November 14, 2010

The server could not process your apk. Try again...

Today, I wanted to submit a new version of my word game 'Word Prospector'.

Everything was okay, until I uploaded the ask on the market !
There I had this nice message :
"The server could not process your apk. Try again"

Hum...ok...
As I'm a kind of well educated one, and despite what my rational-software-engineer mind told me, I follow the note, and try again...
I tried 3 times, and always get the same message !

Unfortunately, this message is quite cryptic, and is not that helpful !
Google was not my friend here...
Luckily, Google, the search engine, did a better job than Google, the market creator, and I find here the solution :

The manifest should not have an empty 'uses-configuration' tag !

So I just get rid of the "<uses-configuration>" I had in my manifest file, and everything was fine again !

Thanks for the people finding the solution in the first time !!!

Sunday, October 31, 2010

Mobile screens : the next step ?



Have you ever action play games with buttons on your smartphone ?
One of biggest grief I have against these games is that you don't _feel_ the buttons. So it's very easy to move your fingers out of the button position, and to loose some control. And the experience then become incredibly frustrating !

So I really think some tactile feedback for our mobile will be an tremeduous boost in the user experience !

Monday, October 18, 2010

Sooooo Cute !!




The korean ads for Android were really nice, and now the mascot is available !

(Found on Romain Guy's twitter...)

Saturday, October 16, 2010

How to improve your application - the ultimate crash reporter : ACRA

I've already introduce some way to create a crash reporter ( here and here ).
But now, I've found what I think is the ultimate crash reporter for Android !

Remember : 
My solution was, when a crash occured, to collect the callstack, some potentially interesting other var, some custom data for the application, and to form a email with all these informations, give them to gmail, so the user could send this email with some data on the current crash !
That was a nice first step, but a lot of people just didn't wanted to send the mail, fearing that it would be used for spams.
A better solution would have been to have my own web server with a special page to receive and treat bug reports, but I didn't have time to make the server part, so I was still using my mail solution !

Here's come Nivek !
Then, 6 month ago, on a french Android developer forum ( FrAndroid ), someone asked how people were dealing with crashes. So I presented my solution. Some people found it interesting, and then Nivek has THE idea ! Nivek ( whose real name is Kevin Gaudin and who is the creator of Email Album ) is a smart guy, that was quite active on the forum, and whose interventions are always interesting ( and it's quite rare on developers forums... ).
He proposed to send the crash report to  a google doc form !
Then he took my code, added this possibility, and make a nice open Source project from it.
ACRA was born !

The advantages of ACRA are :
* You don't have to develop your own server side solution, it's all done by google. You don't even need a web server. A google login is enough !
* All crash reports are sent. Nivek made it modular so you can choose how you inform the user that a crash was send ( no feedback, toast feedback, feedback as a notification )
* it's soooo easy to use !
* It's working with every Android version
* You still can add your own custom informations
* You can use it to send information even when there is no crash ( for instance if your code is in a strange state, but you manage to catch the problem, and put it back in a stable state. You may still be interested by what bring this strange state in the first place ).
* You can receive a mail each time the google doc has changed ( so each time a new bug arrived ), or in a daily digest form (that's what I'm using ).
* Lastly, and this is something I didn't have time to investigate, but I'm sure is _really_ powerful, you can use all the power of Google doc to use the information and present it in any useful way you want.

I really think the last point is very important !
If you have your own web server solution, you can get the crash data and insert them in a database. But then you still need some tools ( ie some developments ) to interpret the data.
Here, Google doc can really help you. For instance, creating graphics is a easy task.
As I said, I didn't take time to investigate, but I think it would be really easy to have :
* A graph with the number of daily bugs
* A graph with the number of daily bugs for each different versions
* A top of the most seens bug in the last X days.
* A link between the crashes and the phone model
...

If you find some interesting way to use google Doc to present the crash information, please inform the community !!

Are there any cons ?
Not really.
-You obvioulsy need the internet permission to use it.
-With the new license server introduced by google to fight agains piracy, it is highly recommanded to obfuscate your code. Obfuscated call stacks will be hard to understand in the google doc !
- it won't catch Application Not Responding issues.

And that's all I can see...
To be honest, if you can leave with the internet permission, I just see no reason not to use it.

Here is once again the link to the google code project.
Make a better code, use ACRA !!!

Sunday, October 10, 2010

Spy My Apps : an application for developers to improve the Android Market

Hi all,

The Android market developer page is nice, but I always feel like some important tools were missing !

I wanted in peculiar to have some graphs with the download, or the ratings of your application, so I can see whether things were getting better. Seing what web tools google can do like analytics, or adsense, it's strange so it is not (already) in the developer page !
For a long time, I also wanted to have the comments : it was really strange that you couldn't read the comments on your own application ! Google finally fixed that and comments are now included in the developer page !

So I developed my owned application to create these graphs : Spy My Apps !

* Launch the application everyday, log in and get the data. It really launch the developer web page, and get the data from it.
* Then you can see the graph of
  => your downloads and active downloads
  => The application rating
  => The daily rating mean
* You can also have the comments on your applications

Some screens :



 The graphs :





















I used this application for several months now  (or at least was was to be this application ), and it's really nice to see all this graphs days after days...












Note : the application is not completely finished !
In particular, the error dealing is not done, so it should be easy to have it crash.
But still, it's very useful !
:)


Here the QR :

Sunday, October 3, 2010

50 000 Downloads reached !!!

Youpee !!!

'Word Prospector' has reached this week 50 000 donwloads !

The first version of the game was released in April 2009, and it's been a long way from the first version to this one !

The first version was done entirely on the emulator ( no real phone existed in France at this time ), and was far from perfect. The controls were not easy, its was way bigger than it should have been, and there were bugs.
So the first rating were quite low...

I constantly improved the version, and now the rating are quite good  ( a 3.84 mean rating, still suffering from the start low ratings ! ), so improving a version really has an effect on the ratings and on the downloads !!


Now the next step will be 250 000 downloads ? It looks a little big at this point, but who knows ?

Sunday, September 12, 2010

id software is coming to Android !

After showing some impressive engine work on iPhone, id software is coming to Android !

Their job page shows an announce for an Android programmer !
This is a really good news for Android, as it shows the general move from the Game Industry toward our beloved platform !
Till now, the iPhone was the preferred mobile platform, but the very strong growth of Android changed their state of mind :)

John Carmack has a very large range of knowledge on game programming, as he started long before things like FPU, 3D graphic cards, or the web even existed. This is important for high performance mobile game programming, as we need old school programming tricks, understanding of how modern graphic cards work, or how to create an architecture to deal with big projects.

Definitively a great news, can't wait to see what will come out of this ( although knowing id software, we should prepare for some wait...  ).
I'm sure Epic will also follow this move !

Job page :
http://jobs.gamasutra.com/jobseekerx/viewjobrss.asp?cjid=23463&accountno=73661

( This news came from Romain Guy Twitter )

Thursday, September 9, 2010

Adventures with Android Library Projects

It's been a long time, although I have continued my life as a hobbyist Android developer !

Today, I will share my wonderful adventures with Library Projects !

Library Projects are a new feature in Froyo, that I was really excited about (see here) !
Library Projects allow you to share some whole parts of your applications, resources included. The main and immediate use is to create several versions of the same application.
For instance, if you have a payed version and an ad supported free version, you need to have 2 differents applications, but with very few differences.
In my case, I have two different games "Word Prospector" and 'Chasseur de mots", that are, basically, the same game, but with different data ( the dictionary : it is a english and a french version of the same game ). As the dictionary is 50% of the apk weight, I didn't want to have only one application with two dictionaries.
But, let's face it : until now, I dealed with those two versions as badly as I could : when I made a development in one of the game, I had then to report it in the other game.
I could not even copy / paste all the files, as some divergence appeared. For instance, some key values for middleware ( Flurry for instance ).

So I was really impatient to try this new Android feature !

The start :
Reading the Google doc, it seems really easy to use : make a library project, click "Is Library" in the project properties, then in the application projects, just select this library as "used library".

First step, first success...
The first step was creating the library. I saw in the documentation that you can convert an existing project into a library project, with just one click ! But this arises a question : is this library still considered as a legit application ?
So I tried to launch this library version... And it worked perfectly !
It wouldn't last :)

Second step, first fail !
Once I had my library, in just a matter of seconds, I tried to use it in my french version of the game.
I started to copy my whole project (just in case thing would be funkier than expected ! ).
Then I removed one activity from this game, expecting Eclipse to find it in the library.
So I deleted this activity file, and in the manifest, I change its declaration, by adding the path to the library activity.
So I was changing :
 <activity android:name="ScorePage"
                  android:label="@string/app_name"
                  android:theme="@android:style/Theme.NoTitleBar"
                  >
into :
 <activity android:name="com.alocaly.EnglishVersionLibrary.ScorePage"
                  android:label="@string/app_name"
                  android:theme="@android:style/Theme.NoTitleBar"
                  >


Looks quite simple, for at this moment, I really enjoyed it !
Then I try to compile it, and Eclipse started complaining : it didn't know about this activity...
Gasp... After different tries, I search on the Internet to find the solution :
You have to close and re-start Eclipse !
Ok, it just worked perfectly.
...
But, for some reasons, my confidence started to vanish...

I continued with all my activities until I had a very simple project : an application, and some data files.
All the activities were coming from the library...
The manifest was OK with this config, but I still had some compilation issue.

Next Step : dealing with Attr
As I'm using AdMob in my game, I have a custom AdMob view, and some custom attributes for this view, defined in an attr.xml file.
Trouble is : the game using the library couldn't find the csutom attributes.
Adding another attr.xlm file in the game using the library didn't help : the compiler complaining that some attributes being defined twice (aarggghhh.. )
Searching on the Internet, I found this discussion here where Xavier Ducrochet, Android SDK Tech Lead, said "At this time, the only solution is to remove the layout from the library and move it into the apps and edit the namespace to match the application package".
Ok, so I tried to do that, but couldn't manage to have a compiling solution.

So I took Admob off, while waiting for a good idea...
( my goal at this point was to have something working as soon as possible, if necessary with less features. )

Getting the application from Activities
As I said somewhere in the intro, the code for the two games is the same, but for some keys values for middlewares ( Flurry and Scoreloop ).
So I decided to put the keys in the application ( as each game already has its own application ), and let the activities get them from the application.
So I had some code like that in the (common) activities :

Application CurrentApp = getApplication();
LetterGameApp CurApp = (GameApp) CurrentApp;
MyMiddlewareKey = CurApp.GetMyMiddlewareKey();


But it just did not work : the activities are in the library, so the GameApp cast is interpreted as a
GameLibrary.GameApp - the library application-.
And in my game using the library getApplication returns a GameUsingLibrary.GameApp, that is not related to a GameLibrary.GameApp and the cast fails !

My Solution at this point was to make the GameUsingLibrary application inheriting from the GameLibrary application.
This way, the cast is still valid.

Note that if it were only for the key values, I could have stored them in some xml files. But I'm also using the application to gather some informations on the user behaviour, to help me for debugging crashes, so I call some application methods from all over my code !

Final step, final fail...
At this step, I had everything working correctly.
Both the game used as a library and the game using the library were working correctly.
It was quite late, and I was looking forward to go to bed.
I made a last test on the game using the library ( you should never make a last test !! ), and found something really strange : a breakpoint being triggered on a empty line !! And then I could step throw some comment lines. WTF ???
So I decided to clean the two projects, to rebuild them.
I then tried to launch both of them... And when launching the game used as a library, Eclipse said 'Could Not Find EnglishGame.apk'.... ???
I cleared the project once again. Same message. I modified the code a little... Same message...
I restarted Eclipse. Same message.
I reboot my computer. Same message.
I'm sure all of you have already feel the same when a very last little modification ruins your entire development day. I felt a little distressed, tired, with a hard desire to kick my computer ( and I perhaps had some bad thoughts for Google engineers... Sorry guys... ).

Finally, I unchecked the "Is A library" checkbox, and the .apk could at least be generated !

Dealing with attr, slight return
As light as they can be, my revenues with AdMob are the only income I get from my android games, and at least as a symbol, I really want to keep it, so I wanted to fix the attr issue and have AdMob back before publishing the version.

I couldn't find the solution for the attr issue, but I finally could fix my problem :
I got rid of my attr file, and of the custom attributes of the AdMob view.
And set this custom attribute values by code, and no more in the activity layouts.


Last comments
Finally, I now need to uncheck the 'Is A Library' checkbox when I want to make the first game, and recheck it when I want to make the second game.
This is a complete non sense, and I feel now like having a library that is also a legit application was just plain wrong. But the documentation is not completely clear on this point, and Eclipse partly let you do it, so it's easy to feel like it's possible when it's not really.
I really should have a library project, and two application projects using this library.

After some time using this configuration, I can tell you that Library Projects are really nice to use.
There are still some place to make this feature better, there are still some confusing things. For instance, debugging is quite strange : you debug a kind of fake file, with a path like 'MyProject/PathInTheLibrary/FileInTheLibrary'.
and this file really does not exist !
Modifying this fake file really modifies the file in the library, so every thing seems to fit perfectly in place, but it still is quite strange, and several times, I launched the wrong game.

There are still some issues - hopefully corrected in the next SDK version - but once your setup is done, and most of your issues solved, having this solution to have your code in only one place is a huge confort gain !

Tuesday, June 29, 2010

Mario Live WallPaper

I've just seen this live wallpaper on Frandroid, and i was just really impressed by this work !

I guess the guy took the Mario code from the Mario AI Competition, and make a Live Wall Paper out of it !

On my FRF50 Froyo version, it is really smooth and hypnotic.
I just think it will waste my batterie  (but actually, the wall paper is not executed that often : only when switching from one application to another, so I don't think it is that bad ).


Here is a video :


You can find it on the market with Android 2.2 ( search for "Mario Live Wallpaper" ), or from this Xda-developer forum.

Enjoy !

Monday, May 24, 2010

Application to SD card : it's working !!

Yesterday, I discovered that the applications should put a special tag in the manifest in order to let the user put them on the SD card.

Today, I tried it on Word Prospector, and
Tadaaa ! It's working great :






















(Here in french, obvisouly)

The move is instantaneous, completely transparent.
 And the application looks as fast as when running from the internal memory.
Nice !!

Additionnal note :
I love the Bug-Droid icon when you are connected for debugging !!!

Sunday, May 23, 2010

Installation of applications on the SD card with Froyo : the how- to !

Youpeee !!!
Froyo is finally here, sooner than I expected it !Thanks, Google !!!

Now, like every other geek with their new toy in hand, I tried most of the new things...
And all is great, but...
The install on SDCard is always greyed...

Then, I just read a tweet from Romain Guy explaining it in a few words.
Your application should add this line in the manifest

 xmlns:android="http://schemas.android.com/apk/res/android"
    android:installLocation="auto"
    ... >

in order to be installed by the end user on the SD Card.

http://developer.android.com/intl/fr/guide/appendix/install-location.html

Friday, May 21, 2010

libraries in Android : the new feature no body speak about in the SDK

Hi all,

This Google I/O has been rich in announces and new and nice features.

So everybody is speaking about :
*  flash : This is really nice, and a big plus for Android ( no, I didn't mention the Fruit Phone... )
* performances : x2 - x5 for free is always good for developers. It also seems the general OS has been improved in this direction.
* Teethering
* Installation on SD card ( the size of applications will now explode !! )
* Cloud to device messenging : this is like the push function in the Fruit Phone, but one step further, as it use the intent system, and can launch some application from the Cloud...
* Application Backup Data : This is nice. Some developers were already doing it on their side, but having it included in the SDK is really a big plus.


But there is a little new feature that I was waiting for for such a long time, and that is totally unexpected :
The project Libraries !!

Project libraries are basically, Android libraries, with code and resources included.
This is awesome !!

It means that now, we can finally have one common code for a free and a paid application !
In my case, I have 2 different application for the english and the french version of my game, I will now have only one version !

Here is the thing :
http://developer.android.com/intl/fr/guide/developing/eclipse-adt.html#libraryProject

And there is a sample :
http://developer.android.com/intl/fr/resources/samples/TicTacToeMain/index.html

Enjoy !!!

Friday, April 30, 2010

Google added crash report feedback for developers !

Hi all,

I was telling it over and over again : crash reports are essential for developers, to make their apps more stable, and user experience more enjoyable.
It looks like google agree with this statement, as they added some crash reports in the developer page :











This is very good news, as it will make the Android platform a better platform to develop on.

How does it work ?
It looks like when a application crashed, the phone let the user send a feedback to the developer( with a custom message from the user, the callstack, and ... ??? ).
I still don't know what is in this feedback - as I got 0 reports - but it definitively looks interesting !
You can also see that is also reports freezes, that user made crash reporters can not intercept !
The developer can then mark a report as 'old' ( so it won't appear again ? ).

For the moment it is still at experimental state. For instance, it looks like you can check feedbacks from other application, but I couldn't get it to work ( having 'server errors' ), and I find it strange for privacy reasons.

This is good to see that Android is going in the right direction !

Tuesday, March 30, 2010

How to improve your application : a crash reporter to improve stability ( slight return )

Hi all,

For quite some time, I wanted to return on my crash reporter, and on this article here.

Actually, there are two things that I changed for my crash reporter :
* First, I made it more robust, fixing a bug (that I can't understand, by the way) in it.
* Then, I extended the crash reporter so that it can be even more useful to help me ( and you ? ) in the bug hunt...

1) Making the crash reporter more robust :

At the launch of my crash reporter, I was gathering informations on the application environment, to give me more information on the system the bug was happening on.
The code I was using was this one :
void RecoltInformations( Context context )
{
PackageManager pm = context.getPackageManager();
        try
        {
         PackageInfo pi;
            // Version
            pi = pm.getPackageInfo(context.getPackageName(), 0);
            VersionName = pi.versionName;
            // Package name
            PackageName = pi.packageName;
            // Files dir for storing the stack traces
            FilePath = context.getFilesDir().getAbsolutePath();
            // Device model
            PhoneModel = android.os.Build.MODEL;
            // Android version
            AndroidVersion = android.os.Build.VERSION.RELEASE;
        
            Board = android.os.Build.BOARD;
            Brand  = android.os.Build.BRAND;
            //CPU_ABI = android.os.Build.;
            Device  = android.os.Build.DEVICE;
            Display = android.os.Build.DISPLAY;
            FingerPrint = android.os.Build.FINGERPRINT;
         Host = android.os.Build.HOST;
         ID = android.os.Build.ID;
         //Manufacturer = android.os.Build.;
         Model = android.os.Build.MODEL;
         Product = android.os.Build.PRODUCT;
         Tags = android.os.Build.TAGS;
         Time = android.os.Build.TIME;
         Type = android.os.Build.TYPE;
         User = android.os.Build.USER;
        
        }
        catch (NameNotFoundException e)
        {
                e.printStackTrace();
        }
}
And this code was crashing on some phones...
I still can't understand where this code can crash ( if you have some ideas, I would happily hear them ! ), but it is actually quite easy to fix :
* Catching all the exceptions, and incorporating the getPackageManagerPart in the try part so that its eventual failure would be caught.
* Not calling this function at the start of the application, but only when it really is needed : when a bug occcurs. There was really no need to call it before.

With this sole improvement, I could prevent the crash reporter to crash ( having a debug tool that creates bug is a _bad_ thing ), but still, I was receiving bugs with this info :

Informations :
==============
Version : null
Package : null
FilePath : /data/data/com.alocaly.LetterGame/files
Phone Modelnull
Android Version : null
Board : null
Brand : null
Device : null
Display : null
Finger Print : null
Host : null
ID : null
Model : null
Product : null
Tags : null
Time : 0
Type : null
User : null
Total Internal memory : 274464768
Available Internal memory : 209596416

It means, first that this code is no more crashing and then that the code that was crashing was in the very first lines of the function. So thrown by GetPackageManager, GetPackageName or GetPackageInfo. 
But, from the Android documentation, only the NameNotFoundException could be launched by these functions... And it was handled by my previous code !!
Conclusion : I still don't understand what was happening !


2) Extending the crash reporter.

Taking some time to think about this crash reporter, we could wonder what is the primary goal of this tool. The real goal of this tool is to give us more information on the bugs that occured so that we can understand remotely what was really happening.
The callstack is a very valuable piece of information to get, the environment can give you some clues, but this is just not enough. Trying to understand where a bug lay with so few information is very tricky. 
But we can't add more informations, without doing something really specific for one application...
...
And that was the good idea : we obviously need some information specific for the application !!!
The application developer knows what kind of information would help him understand the bugs !

For instance, in my game 'Word Prospector', I finally had some crash in the function that checks whether or not a word is correct. Knowing what this strange-crash-creating-word is would be very valuable information.

So I decided to add in my crash reporter a way to add any informations. I just added a hash map with some Key/Values strings, add some functions to populate the hash map, and display this hash map in the report the crash reporter is creating when a bug occurs.

This was a really good idea ! I could find that this word was starting with a '?' : the user could enter some letters while quitting the pause state, where all the letters are replaced by some question marks...


So now, when there is a suspicious and hard to understand crash, I can had a lot of value to check, release a new version, and wait in front of my mail box !

Here is the new version of the code.
I still init it from the application object, so I initialize it for the whole game with just one call.


public class ErrorReporter implements Thread.UncaughtExceptionHandler
{
String VersionName;
String PackageName;
String FilePath;
String PhoneModel;
String AndroidVersion;
String Board;
String Brand;
String Device;
String Display;
String FingerPrint;
String Host;
String ID;
String Manufacturer;
String Model;
String Product;
String Tags;
long Time;
String Type;
String User;
HashMap CustomParameters = new HashMap< String, String>();

private Thread.UncaughtExceptionHandler PreviousHandler;
private static ErrorReporter S_mInstance;
private Context CurContext;

public void AddCustomData( String Key, String Value )
{
CustomParameters.put( Key, Value );
}

private String CreateCustomInfoString()
{
String CustomInfo = "";
Iterator iterator = CustomParameters.keySet().iterator();
while( iterator.hasNext() )
{
String CurrentKey = iterator.next();
String CurrentVal = CustomParameters.get( CurrentKey );
CustomInfo += CurrentKey + " = " + CurrentVal + "\n";
}
return CustomInfo;
}

static ErrorReporter getInstance()
{
if ( S_mInstance == null )
S_mInstance = new ErrorReporter();
return S_mInstance;
}

public void Init( Context context )
{
PreviousHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler( this );
CurContext = context;
}

public long getAvailableInternalMemorySize() {
        File path = Environment.getDataDirectory();
        StatFs stat = new StatFs(path.getPath());
        long blockSize = stat.getBlockSize();
        long availableBlocks = stat.getAvailableBlocks();
        return availableBlocks * blockSize;
    }
    
    public long getTotalInternalMemorySize() {
        File path = Environment.getDataDirectory();
        StatFs stat = new StatFs(path.getPath());
        long blockSize = stat.getBlockSize();
        long totalBlocks = stat.getBlockCount();
        return totalBlocks * blockSize;
    }

void RecoltInformations( Context context )
{
        try
        {
     PackageManager pm = context.getPackageManager();
         PackageInfo pi;
            // Version
            pi = pm.getPackageInfo(context.getPackageName(), 0);
            VersionName = pi.versionName;
            // Package name
            PackageName = pi.packageName;
            // Device model
            PhoneModel = android.os.Build.MODEL;
            // Android version
            AndroidVersion = android.os.Build.VERSION.RELEASE;
          
            Board = android.os.Build.BOARD;
            Brand  = android.os.Build.BRAND;
            Device  = android.os.Build.DEVICE;
            Display = android.os.Build.DISPLAY;
            FingerPrint = android.os.Build.FINGERPRINT;
         Host = android.os.Build.HOST;
         ID = android.os.Build.ID;
         Model = android.os.Build.MODEL;
         Product = android.os.Build.PRODUCT;
         Tags = android.os.Build.TAGS;
         Time = android.os.Build.TIME;
         Type = android.os.Build.TYPE;
         User = android.os.Build.USER;
          
        }
        catch( Exception e )
        {
         e.printStackTrace();
        }
}

public String CreateInformationString()
{
RecoltInformations( CurContext );

String ReturnVal = "";
ReturnVal += "Version : " + VersionName;
ReturnVal += "\n";
ReturnVal += "Package : " + PackageName;
ReturnVal += "\n";
ReturnVal += "FilePath : " + FilePath;
ReturnVal += "\n";
ReturnVal += "Phone Model" + PhoneModel;
ReturnVal += "\n";
ReturnVal += "Android Version : " + AndroidVersion;
ReturnVal += "\n";
ReturnVal += "Board : " + Board;
ReturnVal += "\n";
ReturnVal += "Brand : " + Brand;
ReturnVal += "\n";
ReturnVal += "Device : " + Device;
ReturnVal += "\n";
ReturnVal += "Display : " + Display;
ReturnVal += "\n";
ReturnVal += "Finger Print : " + FingerPrint;
ReturnVal += "\n";
ReturnVal += "Host : " + Host;
ReturnVal += "\n";
ReturnVal += "ID : " + ID;
ReturnVal += "\n";
ReturnVal += "Model : " + Model;
ReturnVal += "\n";
ReturnVal += "Product : " + Product;
ReturnVal += "\n";
ReturnVal += "Tags : " + Tags;
ReturnVal += "\n";
ReturnVal += "Time : " + Time;
ReturnVal += "\n";
ReturnVal += "Type : " + Type;
ReturnVal += "\n";
ReturnVal += "User : " + User;
ReturnVal += "\n";
ReturnVal += "Total Internal memory : " + getTotalInternalMemorySize();
ReturnVal += "\n";
ReturnVal += "Available Internal memory : " + getAvailableInternalMemorySize();
ReturnVal += "\n";

return ReturnVal;
}

public void uncaughtException(Thread t, Throwable e)
{
String Report = "";
Date CurDate = new Date();
Report += "Error Report collected on : " + CurDate.toString();
Report += "\n";
Report += "\n";
Report += "Informations :";
Report += "\n";
Report += "==============";
Report += "\n";
Report += "\n";
Report += CreateInformationString();

Report += "Custom Informations :\n";
Report += "=====================\n";
Report += CreateCustomInfoString();

Report += "\n\n";
Report += "Stack : \n";
Report += "======= \n";
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
e.printStackTrace(printWriter);
String stacktrace = result.toString();
Report += stacktrace;

Report += "\n";
Report += "Cause : \n";
Report += "======= \n";

// If the exception was thrown in a background thread inside
// AsyncTask, then the actual exception can be found with getCause
Throwable cause = e.getCause();
while (cause != null)
{
cause.printStackTrace( printWriter );
Report += result.toString();
cause = cause.getCause();
}
printWriter.close();
Report += "****  End of current Report ***";
SaveAsFile(Report);
//SendErrorMail( Report );
PreviousHandler.uncaughtException(t, e);
}

private void SendErrorMail( Context _context, String ErrorContent )
{
Intent sendIntent = new Intent(Intent.ACTION_SEND);
String subject = _context.getResources().getString( R.string.CrashReport_MailSubject );
String body = _context.getResources().getString( R.string.CrashReport_MailBody ) +
"\n\n"+
ErrorContent+
"\n\n";
sendIntent.putExtra(Intent.EXTRA_EMAIL,
new String[] {"postmaster@alocaly.com"});
sendIntent.putExtra(Intent.EXTRA_TEXT, body);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sendIntent.setType("message/rfc822");
_context.startActivity( Intent.createChooser(sendIntent, "Title:") );
}
private void SaveAsFile( String ErrorContent )
{
try
{
Random generator = new Random();
int random = generator.nextInt(99999);
String FileName = "stack-" + random + ".stacktrace";
FileOutputStream trace = CurContext.openFileOutput( FileName, Context.MODE_PRIVATE);
trace.write(ErrorContent.getBytes());
trace.close();
}
catch( Exception e )
{
// ...
}
}

private String[] GetErrorFileList()
{
File dir = new File( FilePath + "/");
        // Try to create the files folder if it doesn't exist
        dir.mkdir();
        // Filter for ".stacktrace" files
        FilenameFilter filter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                        return name.endsWith(".stacktrace");
                }
        };
        return dir.list(filter);
}
private boolean bIsThereAnyErrorFile()
{
return GetErrorFileList().length > 0;
}
public void CheckErrorAndSendMail(Context _context )
{
try
{

FilePath = _context.getFilesDir().getAbsolutePath();
if ( bIsThereAnyErrorFile() )
{
String WholeErrorText = "";
// on limite à N le nombre d'envois de rapports ( car trop lent )
String[] ErrorFileList = GetErrorFileList();
int curIndex = 0;
final int MaxSendMail = 5;
for ( String curString : ErrorFileList )
{
if ( curIndex++ <= MaxSendMail )
{
WholeErrorText+="New Trace collected :\n";
WholeErrorText+="=====================\n ";
String filePath = FilePath + "/" + curString;
BufferedReader input =  new BufferedReader(new FileReader(filePath));
String line;
while (( line = input.readLine()) != null)
{
WholeErrorText += line + "\n";
}
input.close();
}

// DELETE FILES !!!!
File curFile = new File( FilePath + "/" + curString );
curFile.delete();
}
SendErrorMail( _context , WholeErrorText );
}
}
catch( Exception e )
{
e.printStackTrace();
}
}
}

Enjoy it !!


NOTE : I'm no more using this mail technic. Now I'm using ACRA, and the results are much more interesting !! See here for more info.


Ps : here is a link I found while googling :
http://web-authoring.seadvd.com/whats-google-up-to-with-android/
:)

Tuesday, March 2, 2010

Google really takes care of developers !

Google really want developers to be able to develop in the best conditions !

15 days ago, I was in Montreal for my real job...
So I missed the Android Developer Lab in Paris ... and the Nexus One that was given to every attendant !
Then there was an Android Developer Lab during the MWC, in Barcelona... with a new Nexus One distribution...
There will also be a phone distribution at the Game Developer Conference.

And now, I've just received this email :
"Due to your contribution to the success of Android Market, we would like to present you with a brand new Android device as part of our developer device seeding program. You are receiving this message because you're one of the top developers in Android Market with one or more of your applications having a 3.5 star or higher rating and more than 5,000 unique downloads. "

That is really nice !

Actually, I think Google is doing the righ thing... Not only by giving ME a new phone, but by taking care of developers.

The iPhone success has several reasons.
For sure, the phone was really nice, and came with an interface never seen before... But the most important thing, in my opinion, is that they let any developer develop an application for the iPhone. This is why there are so many application on the iPhone, this is why there are so many good applications on the iPhone, so many new little ideas that make the phone really different...
This is such a big thing that this is what they based their ad campaign on !

Archos understood the importance of developers too, by offering a substancial reduction on their android tablet to developers.
And now Google decided to follow the same way.

This is good for developers, this is a good sign for Android - showing how much Google really involves in Android -, this is good for customers...

Thank you, google !!

IMPORTANT UPDATE :
I received this email at 1.00 AM, the email was coming from Eric Chu, who always send us developers mails about Android updates, so I didn't even ask myself about the mail validity.
And this morning, a lot of people were wondering whether it is a fake or a real mail :
The registration form this mail send to us is hosted on Google Site, the google free hosting service, and it's basically filling a document on google docs. So it really could be a Fake mail, trying to collect some 'Google Order Number' ( Can we do something harmful with it ? )

But it now has been validated by google, by several means, including a post by Roman Nurk, whose title is "Android Developer Relations" in this google group :
http://groups.google.com/group/android-developers/browse_thread/thread/49da01a3cb7f8803

Conclusion : you really can trust this mail....

Saturday, January 30, 2010

Who said an Android phone should fit in your pocket ?

The iPad looks like 'just' a bigger iPhone, but there is already 'just' a bigger Android phone :





By the way, speaking of the iPad, I have a hard time thinking Apple wants to make it the best Internet device, WITHOUT Flash !!
And it looks like, from the Adobe blog, flash is still not coming in Apple's World ( link to the blog article )

Thursday, January 21, 2010

Discovering Flurry

First, I just want to point that I'm not in any way affiliated with Flurry, and blablahbla.... :
NO I DON'T KNOW THOSE GUYS... I just tried their tool !!


What is Flurry ?
When you launch a new application, it's always great to have some feedbacks on it.
There are many different feedbacks : error reports, user evaluations, user mails, online scores, or... 'some' statistics.
It's useful for two different things :
* for your ego, it's always a good thing to have some feedback that basically says that people are trying your application. It's even better if they say they like it ! Even if you don't have the super-developed ego that most developers have, having this kind of information is good for motivation, and helps you improve your application ! As strange as this point may sound, I think it is really important specially if you are an independant developer, as you will need some motivation !
* It's  also crucial if you want to know who your application users are, and how they use it ! It will let you analyse what is good in your application, what are the weaknesses, where to put your efforts in order to satisfy your users...

As I had embedded AdMob ( the now famous ads provider for Android ), I already had some sort of feedback for a while : the number of ads impression for every day. It gives me an indication on how many users are playing my game, and on how this number is evolving.

Flurry ( actually Flurry Analytics ) gives you a lot more statistics on the users and on how they use your application.

How to integrate Flurry ?
The integration of Flurry is really easy.
Create an account on their web site, register your application, add the jar in your application, and copy paste some code on each activity, and voilà ! your first integration is done !!
I also added some special events, in order to know which activity was started, and which game mode was used ( the game has 6 different game modes ).
This is a very good point with this middleware !!


What indications Flurry provides you ?

Flurry provides you informations on :
* users : how many users you have, whether they keep on using your application, how many times did they use it every day, how many sessions you have, how long is a session, and where do they come from.
* Technical : info on which phone was used, which carrier, which firmware, and a simple error reporter is provided ( that does not seem to interfere with mine !! )
* Events : Events are special signals you put in your applications and that are collected by Flurry. You basically do whatever you want with them !

By the way, the information is nicely presented, with some beautiful graphes that make it easy to get the information quickly !


How useful is this information ?

To be honest, I'm kind of fascinated by all this feedback !
All of it is not useful, but it's really interesting to navigate throw this pages and graphes on my game !

In the user pages, you can see the evolution of the way your application is used : if it keeps on increasing.
For instance, I really did see the Chrismas effect : the number of new users has really increased after the 25 !



Welcome to all of those new Android Owners !!



The technical part is also really interesting :
* The firmware versions are divided in about one big third Android 1.5, another third Android 2.0.1, and another little third is Android 1.6...
The rest ( yes, the rest after the 3 thirds... ) is Android 2.0 and Android 2.1. That means that if you want to bypass one of this Android version, you are limiting your audience in large proportions !








* The devices : The droid is the big winner here, with about 1/3 of the devices used. Then the HTC Hero / Eris, the HTC G1/Dream and the MyTouch/Magic...
We can observe that the french version of my game, obviously mainly played in France shows a very different picture :
WordProspector version ( essentially played in the US )



"Chasseur de mots" ( the french version, essentially played in France )


We can see that Samsung - that had shipped the Galaxy a long time ago in France - is much more present, and that Motorola is surprisingly unpresent !
Another funny thing : in the french version there is a ... BlackBerry 8230 : I guess someone played with home-made OS and changed the Phone_Model string !

Last point : the events
The events can clearly be the most important point in Flurry if you use them wisely.
If you want to know something specific on how your users are using your application, Events are for you !
For instance, in my game, I registered which game mode the player were playing.
Personally, I always play the game in Full Game mode, 1 minute, but I discovered that  most people played in 'simple mode', for 1 minute.


Conclusion :
Clearly, users want some quick game : both 'simple game' and 'full game' are played.
And the 2 minutes and 3 minutes games are far from being negligeable ( at that point, I was still wondering if I had developed that for more than 3 users :)


Conclusion : was it worth it ?

First, I want to point that, obviously, Flurry your application to open network and localization authorizations. I don't think it is a real issue ( and in my case, I already needed these authorizations for AdMob), but it may be something that you don't want.

If you don't mind opening these authorization, and don't have any other way to get feedbacks, I think Flurry is really a choice to consider.
It's so simple to use and gives you lot of information.
Now I regret that it is not more flexible with the information it gives. I would like to have a way to create my own requests. For instance, I would like to know how the firmwares are distributed on the G1, but you don't have that much freedom ( even if there are a lot of information already available ).
A custom system done by myself would be more flexible. But, let's be honest : I won't invest time enough  to have something as elaborate as their tool ! ( not to mention the fact that I don't know how to present the data in such a nice way ).

Friday, January 1, 2010

Android Market : updating an application does not update its place in the 'by date' order anymore ?

Today, I updated my 'Word Prospector' Game, with a small bugfix.

I then took my phone, went to the market to see and test from the market the change.
And ... I couldn't find my game in the top of the list sorted by date !!!
It should have been there, at the first place. But, no, I tried to find it, but couldn't because it was sooo far in this list...

It could be a temporary, local bug, and maybe the old market behavior will come back in the next days / hours ( the famous bug of the 2K10 year ? )?
But I feel like it is a big change in the market policy !

What are the consequences of such a move :
* The "By Date" order in the market gets its real meaning back : you won't always see the same applications in this list. It's quite true that it was strange to see a very old application at the top of this list !
* Constantly improving / debugging your application will have a much lower impact on your visibility on the market. By now, every new version brings some new users. This could be the end of this behavior. Does it means people won't update / improve / debug their applications as much as before ?
* it certainly means it will be better to make a lot of application rather than having an application you constantly improving
* Perhaps it means you should make a 'top quality' appplication from day one, because improving it won't bring you any more users ( but that was already the case because of the ratings : if at start your ratings are low, it is very difficult to change them )
* Will people create a new application when they want to release a new version, or at least when a new feature is integrated in their version ?

Can we consider it is the market getting more mature ?
In my case, I hope that it's just a bug...

What do you think ?


EDIT : It looks like I've been alarmed by a well known ( but that I didn't know ) : an updated application will be at the top of the 'By Date' only if the previous update was more than 'some time' before ("some time" being around one week )
I didn't know that, and didn't observe this behavior before !

Thank to all the people that gave me this info !