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. :-) 



Sunday 19 August 2012

Set JAVA_HOME in Windows 7

A common hassle whenever you get a new laptop or PC is setting up the java environment variables so that when you install Eclipse or Netbeans you are ready to go. For your, and my own future reference, this is how it is done (on Windows 7 at least).

1. Find out where you have installed the JDK, or install it from here:

e.g. C:\Program Files\Java\jdk1.7.0_04

2. Press the Windows button and type in "Environment Variables" and press enter.


This will open the following window:


3. Under System Variables, click "New..." and enter JAVA_HOME for the variable name, and the path to your jdk for your variable value:



4. Press OK.

5. Select the variable "Path" and choose to Edit this, and add the following at the end of the string:

%JAVA_HOME%\bin; 


6. Press OK, and then OK to the main dialog. You will now have your Java environment variables set up correctly.

That should be it - if I've missed anything out or you have any problems let me know!

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...