Accessing expansion patch files bug (and solution)

You may have used expansion files on Android. They are very handy when your app goes over 50Mb which, for games, happens pretty soon. In my case I have KendamApp – The Kendama App which has an extensive library of videos included.

The expansion files are organized on a main and a patch files. Each of them can be up to 2Gb. Since uploading them can be a pain, you can reuse them from one version to the next.

There are 2 libraries provided with android SDK to help managing expansion files:

  • downloader_library: Which purpose should be obvious.
  • zip_file: Which is a nice utility to manage expansion files via a content provider. You should be using it if you are using videos.

Also, if you are going to use videos, do not compress them when zipping the obb file.

Updating only with a patch file

Essentially, if you are going to add a few assets, it is better to use a patch file and reuse the main file. It will save lots of bandwidth and time.

You don’t have to re-upload the main file and users do not need to re-download it. Everyone wins.

Except that it may not be too straight forward when using the zip_file library.

The first pitfall (a.k.a. the undocumented feature)

First thing first, you should know that if you do not add some meta-data to the AndroidManifest, the library is going to look for the versions that match with the version number of the app. I don’t recall reading this in the documentation. I actually figured it out by reading the code.

So, the content provider on your AndroidManifest should look like this:

<provider android:authorities="com.plattysoft.zipprovider" android:name="com.plattysoft.provider.ZipFileContentProvider" >
   <meta-data android:name="mainVersion" android:value="17"/>
   <meta-data android:name="patchVersion" android:value="18"/>
</provider>

The second pitfall (a.k.a. the terrible bug)

Then, I realized that every time I tried to watch one of the new videos the app was crashing. I checked for the main and patch files and they were properly downloaded and inside the obb directory.

Something weird was going on.

Digging with the debugger, I ended up in a function that is supposed to return an string array with the name of the available expansion files based on the version numbers and after checking that the files do exist.

static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
   String packageName = ctx.getPackageName();
   Vector<String> ret = new Vector<String>();
      if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
         // Build the full path to the app's expansion files
         File root = Environment.getExternalStorageDirectory();
         File expPath = new File(root.toString() + EXP_PATH + packageName);

         // Check that expansion file path exists
         if (expPath.exists()) {
            if ( mainVersion > 0 ) {
               String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb";
               File main = new File(strMainPath);
               if ( main.isFile() ) {
                  ret.add(strMainPath);
               }
            }
            if ( patchVersion > 0 ) {
               String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb";
               File main = new File(strPatchPath);
               if ( main.isFile() ) {
                  ret.add(strPatchPath);
               }
            }
         }
      }
      String[] retArray = new String[ret.size()];
      ret.toArray(retArray);
      return retArray;
}

Except that it was returning a single string and not two. So, paying extra attention I noticed this specific piece of code.

if ( patchVersion > 0 ) {
   String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb";
   File main = new File(strPatchPath);
   if ( main.isFile() ) {
      ret.add(strPatchPath);
   }
}

Of course it couldn’t find the patch expansion file, it is constructing the name using the mainVersion number instead of the patchVersion. Event the File variable is called main!

Copy and paste is an anti-pattern.

I have checked the latest version of this library that comes with the android SDK and the bug is still there. I’d check where the code is and send a push request. In the meantime, you know what you have to change to fix it.

Safe scenarios

You would not encounter this bug if:

  • You are not using the zip_file library.
  • You are not using a patch file.
  • Your main and patch files have the same version number (you update both).

GDGFest Dublin & Playful Design

Playful design is a term that comes from the games industry, it means juiciness, it means maximum feedback for minimum input, it means delight the user.

In my talk at GDGFest Dublin,  I talked about animations, transitions and how playful design fits into material design.

Playful design works at subconscious levels on the users, they prefer to use your app to another one, but they won’t be able to tell why.

This has been one of the big differences between Android and iOS: How the default components managed the transitions. It seems minor, but devil is in the details. With material design Google has made a big statement of just how important playful design is.

It is very easy to improve the playfulness of any app with very simple animations that are available on the Android framework. That will make your users happier, without them being able to tell you why.

The slides of “Playful Design: What, Why and How” are available on Google Docs.

IMG_20141101_122952

MTG Tracker 5.6

WotC has just released a new set designed for draft: Conspiracy. As any new set, it has new cards, and so, a new version of MTG Tracker is released.

MTG Tracker on Google Play

Changelog:

  • Added Conspiracy
  • Added Modern Event Deck
  • Added option “Import into List” on Card Lists.
  • Minor UI tweaks
  • Improved stability

A bit more in detail

So, in addition to including Conspiracy and Modern Event Deck, other improvements have been done.

MTG Tracker has migrated away from ActionBarSherlock and now uses the official ActionBarCompat and DrawerMenu. This visually only means that the icon on the top left shows three lines when it opens a menu and an arrow when it goes back. Subtle, yet important.

The drawer menu seems to be hard to discover, so now the dashboard adds two new items, one of them called “More…” which opens the side menu. You can compare the old and new dashboards and also see the difference in the action bar icon (arrow Vs. three lines).

dashboard_comparison

Some animations have been added to the card display and the life counter, they are meant to make the user experience more enjoyable with a more playful design.

When we added multiple lists, the functionality to import cards from a text file inside an existing list was missed. It has been added again, now named “Import Into List”

And a few bug fixes, specially crashes when browsing many cards.

cardshark_header

Leonids Particle System Lib

Leonids is a particle system library that works with the standard Android UI. It is Free Software and is available on GitHub.

You can download Leonids Demo from Google Play to check out what can be done with it.

The library is extremely lightweight, it is just a jar file of 18Kb you can add to your project. You can download LeonidsLib.jar from the project page on GitHub.

Why this library?

Particle systems are often used in games for a wide range of purposes: Explosions, fire, smoke, etc. This effects can also be used on normal apps to add an element of “juiciness” or Playful Design.

Precisely because its main use is games, all engines have support for particle systems, but there is no such thing for standard Android UI.

This means that if you are building an Android app and you want a particle system, you have to include a graphics engine and use OpenGL -which is quite an overkill- or you have to implement it yourself.

Leonids is made to fill this gap, bringing particle sytems to developers that use the standard Android UI.

Code examples and features

You can get the code for all the examples on GitHub, but let’s get to the basics.

Simple one shot

Creating and firing a one-shot particle system is very easy, just 3 lines of code.

new ParticleSystem(this, numParticles, drawableResId, timeToLive)
.setSpeedRange(0.2f, 0.5f)
.oneShot(anchorView, numParticles);

When you create the particle system, you tell how many particles will it use as a maximum, the resourceId of the drawable you want to use for the particles and for how long the particles will live.

Then you configure the particle system. In this case we specify that the particles will have a speed between 0.2 and 0.5 pixels per milisecond (support for dips will be included in the future). Since we did not provide an angle range, it will be considered as “any angle”.

Finally, we call oneShot, passing the view from which the particles will be launched and saying how many particles we want to be shot.

This produces a fireworks-like effect as you can see here:

Leonids_one_shot

One Shot – Dust simulation

As an example of something used in production, this dust simulation is almost identical to the one implemented on the game Rabbit and Eggs, but made with Leonids instead of AndEngine.

new ParticleSystem(this, 4, R.drawable.dust, 3000)
.setSpeedByComponentsRange(-0.07f, 0.07f, -0.18f, -0.24f)
.setAcceleration(0.00003f, 30)
.setInitialRotationRange(0, 360)
.addModifier(new AlphaModifier(255, 0, 1000, 3000))
.addModifier(new ScaleModifier(0.5f, 2f, 0, 1000))
.oneShot(findViewById(R.id.emiter_bottom), 4);

It sets an initial rotation range and then 2 modifiers for Alpha and Scale. Using modifiers is the advanced way of managing a particle system. It allows more flexibility about the starting and end times.

The parameters for the modifiers are: initialValue, endValue, startTime and endTime. In this case, we do a scale from 0.5 to 2 in the first 1.5 seconds and a fade out from second 1 to 3.

We also set an external acceleration simulating wind.

And it looks almost exactly like the one in the game, but on a standard Android UI:

leonids_dust

Using emitters – Confeti

Another example we used on Rabbit and Eggs was confeti. This is done using two emitters one on each side of the top of the screen

new ParticleSystem(this, 80, R.drawable.confeti2, 10000)
.setSpeedModuleAndAngleRange(0f, 0.3f, 180, 180)
.setRotationSpeed(144)
.setAcceleration(0.00005f, 90)
.emit(findViewById(R.id.emiter_top_right), 8);

new ParticleSystem(this, 80, R.drawable.confeti3, 10000)
.setSpeedModuleAndAngleRange(0f, 0.3f, 0, 0)
.setRotationSpeed(144)
.setAcceleration(0.00005f, 90)
.emit(findViewById(R.id.emiter_top_left), 8);

Which looks like this:

leonids_confeti

Other details

Leonids requires minSDK 11 because it uses ValueAnimators. It should be very easy, however to use the compatibility library and make it work on Gingerbread.

The library is Free Software, you can use it, extended with no requirement to open source your changes. You can also make paid apps using it.

The importance of a “Rate Us” dialog

Paid apps always had a better ratio of users leaving feedback than free ones, but in the case of MTG Tracker it was too much.

MTG Tracker used to have a very good ratio of users that leave comments. This was reduced when Google Play required G+ sign in to leave a comment, and that was specially bad for the free version, which was released after that change was made.

Things looked like this:

Screen Shot 2014-01-31 at 13.07.04

Both had good average rating, but while almost a 6% of the users leave a rating on the paid version, for the free one that number it is under 0.6%. Yes, 10 times less.

So, I decided to add a “Rate Us” dialog to improve that, knowing that the users of the app use it a lot and just may never thought of rating it.

mtg_rate_us_framed

I did not add it before because for a while I only had the paid app, which has a good amount of reviews. After one week, I went to check the results of the dialog. The stats looked like this:

Please note that the free version has much more installs per day.

Screen Shot 2014-02-06 at 12.09.30

So, almost 100 new ratings on the paid version and 35 more for the free. The conversion rate grow for both apps. Still the paid version got even more ratings.

I decided to let a few more days pass and checked again.

Screen Shot 2014-02-17 at 14.48.11

Another 80 ratings for the paid version and 40 for the free one. Also, average ratings for the paid app keep getting higher, but the ones for the free seem to oscillate.

As it turns out, the percentage of users leaving a rating on the paid app is almost the same as before the dialog was introduced, but the ratio of reviews for the free version has grown up to 0.7%, may not seem much, but the free app has 30% more ratings than before.

Conclusions

After this experiment, it is very clear to me that you must always add a “Rate Us” dialog, even if your current amount of ratings is good. Most users are lazy and will not rate your app unless you ask them to do it.

Some final notes:

  • Don’t be intrusive, if the user does not want to rate you, so be it. Always have a “No, thanks” option
  • Trigger the pop-up after a satisfying user experience, users will be more likely to give you positive reviews if they are in good mood.

Closing the conference season

It is time to close the conference season for this year. It has been a very interesting one, although I’ve spoken only at 4 places, they have been all very interesting.

GOTO Amsterdam: A very important software development conference in the Amsterdam edition, they had a mobile track, I guess I couldn’t miss it.

GDC China: My first time speaking at a GDC, where I presented my “Indie Game Developer Survival Guide”. Great experience, great city and great conference.

DroidCon Amsterdam: For the 3rd year in a row DroidCon celebrated its Dutch edition, and for the 3rd year on a row, I presented a talk there. I think I am holding a record on this one.

GDC Taipei: I was asked to repeat my talk from China at GDC Taipei. I am really humbled that they like it so much that they wanted it there. Also a great experience.

And for next year?

One of the great things of being an international speaker is that you get to travel a lot, given the sort of conferences I like and the expertise I have (Android & Game development) GDCs and DroidCons are where I find myself at home.

So, for 2014 I am sending talk proposals to:

GDC and GDC Europe: The bigger editions of GDC, held in San Francisco (largest) and Cologne, are my next targets to speak at. I want to be able to say that I’ve done the whole set and I’ve been told that the one in San Francisco is amazing.

DroidCon London and DroidCon Berlin: These editions are slightly larger and older than the Amsterdam one, and I’d like to see them first hand.

“Learn to Hoop” 1.0 Released

Platty Soft has a history of making apps for jugglers: First JuggleDroid, and more recently KendamApp – The Kendama App. Today, a new one is added to our family of juggling apps: “Learn to Hoop”.

The app is an a tutorial for learning to juggle with hula hoops. with both textual explanations and videos. It is an adaptation of the educational DVD “Hooping” from Peachysteve.

intro_framed

The app features almost 1h20m of high quality video among 7 chapters containing  51 lessons.

chapters_expanded_no_iap_framed

Subtitles show the instructions during the videos, and more detailed instructions are included separately for you to read at your leisure.

All videos are bundled into the app, so you can view them any time without the need for an internet connection.

video_framed


Get it on Google Play

SpaceCat returns in HD

Today I announce a new version of the most popular title of The Pill Tree. We bring you SpaceCat HD.

space_cat_hd_xxhpi

Back in 2011, when we were developing SpaceCat, phones did not had enough memory and we had to reduce the size of the textures to make the game playable. Some tears were spilled when downsizing the textures, but it was necessary.

Today, that is no longer a limitation. Modern devices can use the original textures without any problem, so we have put them back. Check the scenarios and how they look a lot better.

SpaceCat

device-2013-10-24-164530

SpaceCat HD

device-2013-10-24-164511

SpaceCat HD is a premium app, and it comes with a value pack:

  • Ad-free
  • Vents world is unlocked
  • 500 pills instead of 200 at the beginning of the game

But we didn’t just put better textures, we did some other upgrades to SpaceCat

  • Improved gamepad support (including Green Throttle)
  • Integration with Play Games
  • A revamped shop for spaceships and cats
  • Tips on the loading screen

Get SpaceCat HD from Google Play

The (Mobile) Indie Game Dev Survival Guide

The (Mobile) Indie Game Dev Survival Guide is the talk I presented at GDC China 2013.

Can an indie survive in the mobile world?

It may look like games for mobile is a paradise for indie developers, but truth is that it is quite hard to be noticed among the swarm of apps.

In this presentation, Raul shares the tips & tricks he has learned about how to survive as a mobile indie game developer for the past 3 years, including a post-mortem of all the games of The Pill Tree and also a post-mortem of The Pill Tree as a company.