Sunday, 19 October 2014

Eclipse - Editor hidden - Fix!

I hit a weird error today where in Eclipse I couldn't seem to open any files - the editor seemed to be minimized somehow! Even restarting eclipse didn't fix it. After a bit of playing about - I've got a solution I thought I would share.


  1. Go to Window -> New Window. This will open a second instance of Eclipse. 
  2. Notice you can now open files again. But you aren't done - you need Eclipse to remember this setting.
  3. Close the old window (the one you couldn't open files with) first.
  4. Now close the new window (the one you can open files with). This will persist your new setting (where you want  to be able to open files!). 
  5. Open Eclipse - you should be back in business. 
Hope this helps someone :-)

Monday, 13 October 2014

Promoting your own apps - AdMob in-house ad campaigns

As I've mentioned in my previous blogs, the amount an app will earn you is relative to the number of installs you have. To get installs, you need many things, from a great app to a marketing strategy.

So how can you market your own app on a budget?

One thing I have trialed this week for the first time is using AdMob's in-house ad campaigns. Essentially, you sacrifice showing someone else's ad in your app (which affects your income) in order to show ads for your own apps. It costs you nothing (except lost revenue..) but in theory it should build up more downloads for your other apps and make you more money long term.

In only 1 week I've found this to be a very effective way of launching a new app. You can build on the success of your other apps and help your new app get traction. It's great. But it's not free. You will notice a drop in income to begin with (after a week, I'm still down daily on what I'd usually get) as I'm showing a lot of my own ads. I'm basically replacing all my banner ads with my own ads. I'd estimate it costs me about $1 per install that I'm getting out of my advertising in lost revenue.

However, if my new apps start to grow and get organic installs, long term is should be a winning strategy - I'll report back with my findings. Any tips on how best to do in-house ad campaigns please add a comment to start the discussion!

Sunday, 5 October 2014

Top 5 tips for maximising app revenue

There are many ways of monetizing your apps but here are the 5 top things you can do to maximize your apps potential. Note there is one theme that runs throughout these tips - the revenue of your app is relative to the the usage of your app.

  • Display interstitial ads. This was the one change I made to my apps which instantly saw my revenue almost treble. I had previously been showing banner ads within my apps but adding interstitial ads made my revenue boom. Be careful however that you don't drive users away from your app, remember the revenue of your app is relative to the the usage of your app.

  • Establish your app early on before adding ads. I find that ads can be a turn off to users, and every install counts when you are trying to establish your app. The best thing to do is to let the app gain momentum and user base before introducing ads. After all if you have a handful of users adding ads isn't going to gain you more revenue. Remember, the revenue of your app is relative to the the usage of your app.

  • Experiment with ad placement. Do you put a banner at the top or the bottom, on every page or just sub pages, do you show an interstitial on load or on exit or during the app - it changes depending on your app. You need to test the waters with each placement (a week will suffice) and then go with the one which gives you best revenue, without affecting your user base. Remember if you turn off your users, the revenue potential decreases - the revenue of your app is relative to the the usage of your app.

  • In app purchases. This depends on your app and what service you are providing. In app purchases are great for games for adding levels or features, but it can be used in other apps too. This could be from adding other features, to simply allowing the user to remove the ads for a price. Again you need to experiment with your approach - what is the best price etc. Like everything else the more users you have the greater the number of people who may purchase something in your app.  Remember. the revenue of your app is relative to the the usage of your app.

  • Have a great app. This is the hard one - the items above are easy to add - getting the original installs is the problem. Sometimes the app doesn't even need to be great, it just needs to fill a gap or do something better. But if your app is good quality, has few bugs, and offers value to the user then you'll end up with a high number of installs (marketing I'm sure also plays a massive part here - that's a future blog...) and with more installs and users you'll get more revenue.  Remember. the revenue of your app is relative to the the usage of your app! 
These tips are my experience so far - I'd be interested to hear others experience - please add a comment and we can open up the discussion! 

Saturday, 4 October 2014

Checking income on the go

As I mentioned in my first post you can quickly get addicted to your apps and tracking their progress. To do this, you'll want to get stats on the go. There are a few apps on the play store market, and I use a combination of these to get the statistics I need.



This app gives you your days stats. There are handy indicators as to whether yesterdays stats were up or down, and whether your month is up or down on this time last month - very handy! Let's just hope you see lots of green arrows and not red ones.


AdSense Dashboard is an app I've used since I first started monetizing my apps. 

My favourite feature about this is the 2 week history - where you can work out how much you are averaging over a two week period so you can try and forecast future outcome. 

There are many other apps out there but these are the ones I use. 

So what about tracking your store ratings and downloads? That is another story altogether - I've yet to find a good tool for this. I've tried App Annie but couldn't get this to work so the search goes on...

The App Graveyard - The Depressing side of App Development

App development is great. People do it for fun, people do it for work and people do it for both. The barriers of entry to creating your own app (especially Android) are so low you can literally publish an app in a few hours and the statistics available let you track your apps progress in almost real time. The thrill of watching an app soar in terms of downloads and rank is amazing, and only rivalled by watching the money you make from an app soar. That is the justification for starting this blog - to document lessons learned and to share the joy I gain from writing apps. However, this first blog post focusses on another side of app development I've just discovered for the first time - the massive lows after the initial thrills.

My story starts in September 2012 when I published my first ever app. I started developing apps as a way to keep my technical skills up to date. I have a Masters in Computer Science and had a full time job as a Software Developer, but was transitioning to become a Software Development Manager and I knew my opportunity of writing of code would slowly decrease as the years went on. Writing apps was a fun way of developing my skills and keeping current. So I created an app and thought why not just publish it on Google Play and see what happens? I took the leap, and 2 years later that first app has had an astonishing (sarcasm) 4997 installs, of which less than a fifth still have the app installed.

Current installs by device for my first application. Whilst this is a nice linear graph, the fact there is less than 1000 current users for a free app means it will never be a good money maker.

When I started out, I never thought anyone would install my app so there was the thrill of the first time the app was installed. There was the thrill of the first day I hit 10 installs in one day, and then the thrill of my highest ever installs per day for that app of 28! Seeing this app grow at a rate I never expected led me to think - can I monetize this app? I quickly setup AdMob within my app and started to watch the result. My first month I made nothing, the next month I made an incredible 10 cents. Over it's lifetime the app has gained me £65 which is approximately $103. So why am I saying this is depressing?

The depression comes in the fact you look at your first app, and you see it grow. Quickly expectations rise, and the instant feedback every morning of your numbers of installs the previous day become important. You wake up and it's an instant low when you see you had less installs yesterday than the day before. Your earnings have gone down. You then start making more apps. Some apps take off, and others just don't. At time of writing I have published 36 different apps. The majority of them have never taken off. Three have broken 50,000 installs, the rest are all less than 10,000 installs, the majority between 0 and 100. What's worse is that you know within a few days whether the app is going to take off - if you haven broken 10 installs within the first week it's fair to say your app has been consigned to the "App Graveyard" - never to be featured in the top charts, never to be updated again as the initial feedback from the market is that the app is a waste of time.

The apps which take off are thrilling however and offer a completely different experience. These apps offer large volumes of installs per day (into the thousands for me) and if you monetise the apps you can start to get real rewards for your hobby. They key word here is hobby, as every cent or pound earned is seen as a bonus. However the earnings may get to the stage where you start to believe that this could become a business which could really take off. You may earn 30%-40% of your current full time job and think that this is an opportunity to break off on your own. You start making plans, you build more and more apps but it's difficult to get off the ground. This is the point I hit in the last few months - making plans for a business using apps.

The apps which take off have an even crueller experience which you can only experience after a period of prolonged success in an app - it's descent from a top app in the store back to the App Graveyard. You initially notice a drop in installs or earnings, and you see it as a blip. But that continues for a month or so and you start to panic. You start making new updates to your app to try and resuscitate them but it's too late. They are out the top charts and the snowball drop affect has unbelievable momentum you can't stop. My earnings dropped almost 60%  in the space of 2 months, and your dream of going solo with your app business are dashed.
This is my most successful app but you can see it is currently in decline - the nice linear graph has started to tail off to the point I'm getting more uninstalls per day than installs. The descent to the App Graveyard begins.
This is the earnings graph for that successful app - you can see that the earnings drop is even more severe than the apps total installs.

This takes me to today, where my next step is to start this blog, and re-adjust my thinking back to apps being a hobby and every cent earned being a bonus. It's a refreshing change of direction which I feel is going to change my experience back in the right direction. I'm going to create new apps, some of which will take off and some which wont and will just sit in the App Graveyard. Then it will all happen again - one app will show promise and it will move away from being a hobby again, and this cyclical experience of fun to hope to despair will just begin again!

Monday, 27 August 2012

AVD Manager - Unable to find a 'userdata.img' file for ABI armeabi to copy into the AVD folder

When trying to create a new Android 4.0.3 virtual device I hit this problem for ages. When I clicked Create AVD it was just doing nothing:


On further investigation I noticed that eclipse was saying the following:

"Unable to find a 'userdata.img' file for ABI armeabi to copy into the AVD folder."

In order to fix this problem, in Eclipse choose Window -> Android SDK Manager

Once this loads, you need to ensure that you have installed ARM EABI v7a System Image under Android 4.0.3 (API15). 

Once this is installed, try to create the AVD again and it should just work. It would have been much better if the "Create AVD" button did something instead of just failing silently! Hope this helps you out.

Thursday, 23 August 2012

ASP.NET upload image to database and retrieve into gallery

A recent request to a website I've been creating was to allow users to upload photos to the website, so that other users can see them. In order to do this I created this proof of concept which was easy to incorporate into my sites(s).  You can download the complete source here:

The first step is to create a database which will store the images and the description of the image. In Microsoft SQL Server I've created a new Database named "Example" and one table named "Photos". Within this table, create three columns:

Column
Type
UniqueID
Unique Identifier
Photo
Image
Description
Text

Note: You'll need to add something like this to your Web.config file:

<appSettings>
      <add key="ConnectionString" value="Data Source=*your instance name*;Initial Catalog=Example;Integrated Security=True"/>
</appSettings>


For the website, I've kept the default styles when you create a new ASP.NET Web Application, removing the two content pages that come by default and creating two new ones - 'Album.aspx' and 'Upload.aspx'. So my master page looks as follows:

The basic skeleton of site

Now, lets start by allowing users to upload images. In Upload.aspx add three controls; a FileUpload control (photoUpload) to select the file to upload, a TextBox (txtDescription) to allow the description and a Button (btnUpload) to start the upload.. I'd also add a few labels to tidy it up. 

Upload page

Now for all database interaction I've created a class named DatabaseAccess to keep things tidy. This has two methods, UploadImage((byte[] imageBytes, string description) and GetPhotos(). 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.Configuration;
using System.Data;

namespace PhotoUploader
{
    public class DatabaseAccess
    {
        private string _connString = string.Empty;
        private SqlConnection _conn;

        public DatabaseAccess()
        {
            _connString = ConfigurationManager.AppSettings["ConnectionString"];
            _conn = new SqlConnection(_connString);
        }

        public void UploadImage(byte[] imageBytes, string description)
        {
            _conn.Open();

            string insertStatement = "INSERT INTO Photos(UniqueID, Photo, Description) VALUES(@uniqueId, @pic, @description);";
            SqlParameter picParameter = new SqlParameter();

            picParameter.SqlDbType = SqlDbType.Image;
            picParameter.ParameterName = "pic";
            picParameter.Value = imageBytes;

            SqlParameter uniqueId = new SqlParameter();
            uniqueId.ParameterName = "uniqueId";
            uniqueId.SqlDbType = SqlDbType.UniqueIdentifier;
            uniqueId.Value = Guid.NewGuid();

            SqlParameter descriptionParam = new SqlParameter();
            descriptionParam.ParameterName = "description";
            descriptionParam.SqlDbType = SqlDbType.VarChar;
            descriptionParam.Value = description;

            SqlCommand insertCommand = new SqlCommand(insertStatement, _conn);
            insertCommand.Parameters.Add(uniqueId);
            insertCommand.Parameters.Add(picParameter);
            insertCommand.Parameters.Add(descriptionParam);

            insertCommand.ExecuteNonQuery();
            insertCommand.Dispose();
            _conn.Close();
        }

        public DataSet GetPhotos()
        {
            _conn.Open();
            string selectStatement = "SELECT * from Photos;";
            SqlCommand command = new SqlCommand(selectStatement, _conn);
        
            SqlDataAdapter adapter = new SqlDataAdapter(command);
            DataSet ds = new DataSet();
            adapter.Fill(ds);
            command.Dispose();
            _conn.Close();
            return ds;
        }
    }
}


Ideally the SQL statements would be stored procedures, but this will do for now. 

Now, in the code part of Upload.aspx.cs I have the database access being constructed, and the button click event uploading the details to the database, then it automatically transfers to the Album page so you can see your image that has been uploaded:

 private DatabaseAccess _dbAccess = null;

        protected void Page_Load(object sender, EventArgs e)
        {
            _dbAccess = new DatabaseAccess();
        }

        protected void btnUpload_Click(object sender, EventArgs e)
        {
            _dbAccess.UploadImage(photoUpload.FileBytes, txtDescription.Text);
            Server.Transfer("Album.aspx");
        }

If you try this out you should see your database now getting populated with images.

Now we want to display the images on the Album page. To do this I have created a Web User Control which has a label (for the description) and an Image for the photo. 

The basic user control - customize this to make it look great!

We will populate this image control with every image we get returned from the database, and then add it to the album page using an generic handler. Meanwhile in the code for this user control I have the following:

 public string Description { get; set; }
        public Guid PhotoID { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            this.lblDescription.Text = Description;
            this.imgPhoto.ImageUrl = "~/ImageHandler.ashx?id=" + PhotoID.ToString();
        }

What we are doing here is passing the unique id of the photo to the generic handler so that it will handle the retrieval of the image and populate it at runtime. To create the ImageHandler, within Visual Studio choose Add -> New Item -> Generic Handler and name the file ImageHandler.ashx. Within this file, add the following code:

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web;

namespace PhotoUploader
{
    public class ImageHandler : IHttpHandler
    {
        private string _connString;

        public void ProcessRequest(HttpContext context)
        {
            _connString = ConfigurationManager.AppSettings["ConnectionString"];
            SqlConnection myConnection = new SqlConnection(_connString);
            myConnection.Open();
            string sql = "Select Photo from Photos where UniqueID=@ImageId";
            SqlCommand cmd = new SqlCommand(sql, myConnection);

            cmd.Parameters.Add("@ImageId", SqlDbType.UniqueIdentifier).Value = new Guid(context.Request.QueryString["id"]);
            cmd.Prepare();
            SqlDataReader dr = cmd.ExecuteReader();
            dr.Read();
            context.Response.ContentType = "image/jpeg";
            context.Response.BinaryWrite((byte[])dr["Photo"]);
            dr.Close();
            myConnection.Close();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

What this is doing is selecting the photo with the ID we have passed it, and then will return it as bytes and will populate the image in the user control. 

Now to finally tie things together, we need to add code to the Album page to load the images into a gallery. Add a panel (pnlGallery) to the content page so we can add the ImageControl's to this. We created the method to retrieve the image data earlier in the DatabaseAccess class, so we just need to call through to that, iterate through the dataset and create a ImageControl for each photo, and add it to a panel on our page. 

using System;
using System.Data;

namespace PhotoUploader
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        private DatabaseAccess _dbAccess = null;

        protected void Page_Load(object sender, EventArgs e)
        {
            _dbAccess = new DatabaseAccess();
            LoadGallery();
        }

        private void LoadGallery()
        {
            DataSet photoData = _dbAccess.GetPhotos();
           
            foreach (DataRow row in photoData.Tables[0].Rows)
            {
                byte[] imageBytes = (byte[])row["Photo"];
                string description = row["Description"].ToString();
                Guid photoID = (Guid)row["UniqueID"];

                ImageControl image = (ImageControl)Page.LoadControl("ImageControl.ascx");
                image.Description = description;
                image.PhotoID = photoID;
                this.pnlGallery.Controls.Add(image);
            }
        }
    }
}

This now completes the example - you should be able to upload images to the site and then view them in the album.

Uploading in action

The final Gallery


If you have any questions or can correct any mistakes I've made just add a comment. :-) 



Covid-19 impact on mobile applications - a quick case study

Amidst everything that's been going on over the last few months, checking on how my apps have been doing has been low down my priorities...