This is part 2 of a walkthrough of one of my first ever Android app, that happened to get a little bit of love in the android market, and for which the ENTIRE(!!) source code of the application is available to you good people to do whatever you want with.. see part 1 for more details of the app, what happened and the first steps in getting started!
So far, we have created a simple Activity class, designed a layout with several buttons and then implemented an onClickListener within our Activity class to handle the required actions for all of our buttons. The remaining Activities are all fairly straight forward, and work in a similar fashion: the RulesActivity is just a simple activity what has a block of text with a brief description of the game; the SettingsActivity is a simple activity that has three radio buttons which allows the user to select the difficulty; the QuestionsActivity just displays a question and then a selection of multiple choice answers.
If you are not familiar with Activities then you can open up those other classes and see exactly what is going on, they are all pretty similar but each have slight nuances or differences in their components and actions.
At the moment the application is pretty simple – there is a basic welcome screen with some buttons, and once you start the game you get taken through the QuestionsActivity several times, refreshing with different questions until you have answered all the questions, and then at the end it displays a simple graphic based on your accumulated score. Easy right?!
Really, the only big gap in what is going on is the DB – all the questions need to be stored in the DB on the client device, and then that data needs to be accessed, so we basically need a DAO type class.
In this app, we have a single DBHelper class – you may want to wrap this with a service class, to allow better separation of layers and decreased coupling etc, but for the sake of this app, lets just jump into the DBHelper. To access the DB on Android you need to have your class extend SQLLiteOpenHelper, now lets walk through the code required
private static String DB_PATH = "/data/data/com.tmm.android.chuck/databases/";
private static String DB_NAME = "questionsDb";
private SQLiteDatabase myDataBase;
private final Context myContext;
First of all we define some simple class member variables. The DB_PATH will be used later to access our file that contains all our questions in it, the DB_NAME is just a name of choice to reference the DB.
public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
You have to make sure you override the constructor, as this needs to initialise the context and DB before starting.
Next we have the createDatabase method – this one is quite straight forward:
public void createDataBase() throws IOException{
boolean dbExist = checkDataBase();
if(!dbExist)
{
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
As you can see, it simply checks if the DB already exists, if it doesn’t (in which case its the first time that the app has been launched on this device) then it calls the copyDataBase() method:
private void copyDataBase() throws IOException{
InputStream myInput = myContext.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
OutputStream myOutput = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
Again, this is pretty straight forward – it just opens an Input stream to read the file (our questions file that we have saved in the /assets/ directory) and then writes it to the output stream which has been set up to be our DB Path/Name. Easy right?
So now we pretty much have our DBHelper class that checks for an existing DB, if its found then it opens a connection, if its not then it loads the data (our question set) in for the first time.
Now, the only remaining thing that our helper needs to do is to be able to access the data so we can select questions to show the user. So let’s look at how we add some DAO access methods. What I have done here is create a simple method to return a (random) question set:
public List getQuestionSet(int difficulty, int numQ){
List questionSet = new ArrayList();
Cursor c = myDataBase.rawQuery("SELECT * FROM QUESTIONS WHERE DIFFICULTY=" + difficulty +
" ORDER BY RANDOM() LIMIT " + numQ, null);
while (c.moveToNext()){
Question q = new Question();
q.setQuestion(c.getString(1));
q.setAnswer(c.getString(2));
q.setOption1(c.getString(3));
q.setOption2(c.getString(4));
q.setOption3(c.getString(5));
q.setRating(difficulty);
questionSet.add(q);
}
return questionSet;
}
Here, I am modelling the questions in a pre-created “Question” class – this just has the necessary member variables so I can handle the questions more easily than having to try and deal with DB cursors throughout the code. In the above method we just call a pure SQL statement, and then iterate through the cursor to populate my Question ArrayList.
And thats it! There are some other Activity classes and helper classes running doing small things throughout the app, but the core features are covered, so feel free to have a look around the code, edit it, change the design and just generally have fun.
About a year ago I released my first app on to the Android market. I had developed the market primarily as a learning tool, and at the end decided that putting it on the market would be a good step to understand the process and get some feedback.
The application was a simple quiz app based on the popular NBC series Chuck, I released it for free (no-ads) and no real advertising other than one or two target tweets. The app currently has about 4,000 – 5,000 downloads with a 4 star rating – both of which it achieved pretty quickly in the first month or two.
DISCLAIMER: I believe this to be purely down to the fact that at the time there weren’t really any other Chuck related apps, and it has a nerd-ish, cult like following, so I think the rating and popularity is more of a reflection on the show’s popularity than the quality of the app!
Anyway, I thought it might be interesting/helpful to release the source code here and walkthrough the different aspects of the code in a tutorial “my first app” kind of way.
I won’t go in to the setting up Eclipse/Android plug-ins stuff here, so will assume familiarity with getting around the IDE.
Android Directory Structure
Creating a new Android project in Eclipse (using the Android plug-ins) creates the required project structure automatically for you. Lets have a look at that now:
Src : as you would expect, this is the main java source directory, so this is where all your source code lives
Gen: this is the generated code that Eclipse handles for you – the files in here will be generated from your config. You needn’t really worry about what happens in here as Eclipse takes care of it for you
Assets: this can be used to contain any resources or libraries you want to bundle with the app – such as third party jars and data sets (you will see later we use this directory to store the data that we will load to populate all the questions in the DB when the app is first launched on a new device)
Res: this is where the configuration for the layout/UI aspects live:
- Drawable – keep you image files in here (.png/jpg/etc)
- Layout – this is where you keep the xml config for UI layout
- Values – this is where you keep the config with application code, such as application name or set text. This means you can easily change text through out the application without having to change hardcoded strings in the java code and re-compile
Getting Started
Ok, lets get started – the first thing we need is a welcome screen that is displayed when a user first launches the app. In Android, a screen is modelled by an “Activity” class, so to create our welcome screen we need to extend Activity.
public class SplashActivity extends Activity implements OnClickListener{
@Override
public void onCreate(Bundle savedInstanceState) {
Every class that overrides the Activity class must implement the onCreate() method – this is called when the activity is launched. As our welcome screen will just be a simple screen with a few buttons on it (play, rules, exit) we don’t need to do much at this point other than set up the buttons to have a listener so we know when one of them is pressed. For convenience here, we will set our current activity class to implement “OnClickListener” – this means we can define the onClick() method directly in our activity to handle the button events.
@Override
public void onClick(View v) {
Intent i;
switch (v.getId()){
case R.id.playBtn :
List questions = getQuestionSetFromDb();
GamePlay c = new GamePlay();
c.setQuestions(questions);
c.setNumRounds(getNumQuestions());
((ChuckApplication)getApplication()).setCurrentGame(c);
//Start Game Now.. //
i = new Intent(this, QuestionActivity.class);
startActivityForResult(i, Constants.PLAYBUTTON);
break;
case R.id.rulesBtn :
i = new Intent(this, RulesActivity.class);
startActivityForResult(i, Constants.RULESBUTTON);
break;
case R.id.settingsBtn :
i = new Intent(this, SettingsActivity.class);
startActivityForResult(i, Constants.SETTINGSBUTTON);
break;
case R.id.exitBtn :
finish();
break;
}
}
As you can see, we have one single method that handles any button press, and we manage this with a switch statement checking the id of the view (we will go into this later). Mostly, the cases are simple, “exit” finishes the application whilst “rules” and “settings” have code like this:
i = new Intent(this, RulesActivity.class);
startActivityForResult(i, Constants.RULESBUTTON);
Intents are objects in Android that are used to communicate between activities, and as you can probably spot from the above, this simply creates a new Intent and then uses it to start another Activity class, in this case, our RulesActivity class.
The “Play” case is slightly more complicated – as this needs to initialise the game and load the questions to be used from the DB. Once it has done this, it loads the information in to our “Application” class (we will cover this later also) and then just kicks of the QuestionsActivity with a new Intent as per above.
So that is our welcome page pretty much done, we have a single screen that has a few buttons and we have setup listeners to perform appropriate actions on pressing them. The only remaining issue is the layout of the screen.
Activity Design
In the onCreate() method of our Activity you will notice the second line is
setContentView(R.layout.welcome);
This call tells Android which layout xml file should be used to configure the design – to find the relevant file is quite intuitive, its simply going to be located at /layout/welcome.xml
There are several layouts available to use, each of which are applicable in different circumstances, but as we are just going to have a straight list of buttons, we will use the simple “LinearLayout”. You can nest layouts, as you will see in this example, but lets walk through some of the config options we are using for our layout:
android:orientation="vertical" – This defines our layout as vertical, so new elements will be added on a new row below (if it were horizontal it would add as a new column)
android:layout_width="fill_parent" – this tells the activity to fill the whole width of the screen with the contents
android:layout_height="fill_parent" – this tells the activity to fill the whole width of the screen with the contents
android:gravity="center_horizontal" – this centers all elements that are contained in this layout
android:background="@drawable/background – this defines a background image to use for the activity (this image should exist at /drawable/background.png)
Now, if we look inside the layout you will see some simple components. The first one being a simple ImageView, which points to an image that we want to use as a header. The following elements are the buttons – you will notice that the ID of each button corresponds to the onCreate() method of our Activity where we inflate the button to set the onClickListener.
If you press the “Graphical Layout” tab in eclipse, you will see what the welcome screen will look like.
Ok, so we have our welcome Activty, we have designed a nice UI to control the app and have an onClick handler - the next part will cover intergrating with the DB to pull out the questions and then we are all there!
Grab complete source code for the project here
For some time I have been working on developing a Java web app using Spring MVC & Hibernate, and as many will have discovered, this throws up lots of questions with unit testing. To increase my coverage (and general test confidence) I decided to implement some tests around the DAO layer. Up untill now, the DB access had been mocked out, and whilst some purists will argue that these are strictly "Integration tests" rather than unit tests, I decided to go ahead with the.
As I didn't want to be messing around with going near my dev DB, or worrying about dropping my dev data everytime I run a test I decided to test the DAO layer using in memory DB (HSQL), so here's how its done:
First, I added the dependency to my POM so the HSQL JARs were downloaded:
hsqldb
hsqldb
1.8.0.10
jar
test
Next, in my Persistence.xml I added a new Persistence Unit, that would point my tests to my in-mem DB
Important points to note here are that the database name can be anything you like (well, as long as you have the url jdbc:hsqldb:mem: ..). Also, the username must be "sa" and the password "". There is no manual install needed, or explicit DBs created, as long as you have the JAR in your project everything will be handled for you.
Next, I copied my regular applicationContext.xml to src/test/resources, as I needed to configure this to point to my new Persistence Unit defined (and new data source)
As you can see, I have updated my Entity Manager Factory Bean to be using my newly created Test Persistence unit - this means any time Spring is fired up using the context definition, it will be pointing to my in-mem DB - Now I have configured everything, I am ready to start testing.
I started by writing an abstract test class - I used this to define my common @Before method (I needed to setup a common "test" user to allow db updates to complete), but the important thing here is the annotations on the class
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/test-applicationContext.xml"})
@Transactional
public abstract class AbstractServiceTest {
These annotations tell the tests that they are running with the Spring junit runner, and to use my newly created test-applicationContext.xml. The @Transactional is neccesary so we know to roll back all data persisted during the tests at the end of each test (we dont want test data leaking in to other tests and potentially affecting the outcome).
Now its just a case of writing the tests, here is an example of a test:
public class AccountServiceDbTest extends AbstractServiceTest{
@Autowired
AccountService service;
@Test
public void testFindAllAccounts() {
List accs = service.findAllAccounts();
assertEquals("Check DB is empty first", 0, accs.size());
Account a = new Account();
a.setUserName("robb");
a.setPassword("password");
service.storeAccount(a);
accs = service.findAllAccounts();
assertEquals("check Account has been created", 1, accs.size());
}
}
The test checks that nothing is in the DB, then persists the new account to the table (all in-memory) and then checks the size to make sure it has been created correctly. At the end of the test the transaction is rolled back, so this will always work!
Another interesting point, is as I have defined the Spring context in the Abstract class, my tests are all now Sprin-managed, which means I can simply use the @Autowired annotation to autowire other Spring classes such as Service classes (these have my DAOs etc in).
Following on from the RSS Reader, some time last year I was investigating using Twitter in some of my apps, so I put together this simple Twitter reader (it only reads tweets from a given user - it doesnt allow posting etc)
For the most it is the same as the RSS Reader in terms of laying out the Android Lists and the layout.xml, so I won't go over that, I will just go through the Twitter integration.
The first step is to retrieve all the tweets, for this I am using the very useful Twitter4J Java library that simply wraps the Twitter API with a Java API
Twitter twitter = new TwitterFactory().getInstance();
List statuses = new ArrayList();
try {
statuses = twitter.getUserTimeline(screenName);
} catch (TwitterException e) {
Log.e("Twitter", "Error logging in to Twitter");
Log.e("Twitter", e.getMessage());
}
Once you have the list of latest tweets, I convert them in to JSON so my listAdapter can process them more easily:
ArrayList JOBS = new ArrayList();
try {
if (statuses.size()>0){
for (Status s : statuses){
String avatar = "http://" + s.getUser().getProfileImageURL().getHost() + s.getUser().getProfileImageURL().getPath();
JSONObject object = new JSONObject();
object.put("tweet", s.getText());
String timePosted = Utility.getDateDifference(s.getCreatedAt());
object.put("tweetDate", timePosted);
object.put("author", s.getUser().getName());
object.put("avatar", avatar);
object.put("userObj", s.getUser());
object.put("tweetId", s.getId());
JOBS.add(object);
}
}else{
JSONObject object = new JSONObject();
object.put("tweet", "You have not logged in yet! Please log on to view latest tweets");
object.put("author", "");
JOBS.add(object);
}
} catch (JSONException e1) {
Log.e("JSON", "There was an error creating the JSONObject", e1);
}
And thats pretty much that! the listAdapter just pulls the information out of the JSON objects as per above and displays as necessary. The only other thing I will show is a little Date util class to make the date posted a little more fun:
public static String getDateDifference(Date thenDate){
Calendar now = Calendar.getInstance();
Calendar then = Calendar.getInstance();
now.setTime(new Date());
then.setTime(thenDate);
// Get the represented date in milliseconds
long nowMs = now.getTimeInMillis();
long thenMs = then.getTimeInMillis();
// Calculate difference in milliseconds
long diff = nowMs - thenMs;
// Calculate difference in seconds
long diffMinutes = diff / (60 * 1000);
long diffHours = diff / (60 * 60 * 1000);
long diffDays = diff / (24 * 60 * 60 * 1000);
if (diffMinutes<60){
if (diffMinutes==1)
return diffMinutes + " minute ago";
else
return diffMinutes + " minutes ago";
} else if (diffHours<24){
if (diffHours==1)
return diffHours + " hour ago";
else
return diffHours + " hours ago";
}else if (diffDays<30){
if (diffDays==1)
return diffDays + " day ago";
else
return diffDays + " days ago";
}else {
return "a long time ago..";
}
}
Well, its now 2013, and I have updated a version of this post - a long with a complete working application on GitHub - you can find that here it covers Android 3.0+ so includes fragments and parsing the RSS in an AsyncTask. Of course, you can still read this post and access the code for the pre-3.0 version!
Since posting a link to my simple RSS Reader for Android implemented using a custom SAX parser I have some questions regarding upgrading to handle images - I have previously posted on how to implement new tags in the feed that you want to handle (including nested tags) - but one question I received was regarding when images are just embedded in the text (e.g. not a specific image tag, but say as part of a description as HTML).
So, I have updated the code (uploaded, see the sidebar) - this time it parses the description text data to retrieve any Image links its pretty grim string manipulation, but it does the job!
As you can see, it now parses the image URL from the description and inserts it to the left of the feed item.
What did I change?
First I needed to change the Article class to parse the link. I updated the setDescription method to also check for IMAGE tags to parse, if any found then it would set the ImgLink member:
public void setDescription(String description) {
this.description = description;
//parse description for any image or video links
if (description.contains("<img ")){
String img = description.substring(description.indexOf("<img "));
String cleanUp = img.substring(0, img.indexOf(">")+1);
img = img.substring(img.indexOf("src=") + 5);
int indexOf = img.indexOf("'");
if (indexOf==-1){
indexOf = img.indexOf("\"");
}
img = img.substring(0, indexOf);
setImgLink(img);
this.description = this.description.replace(cleanUp, "");
}
}
I then also had to update the RssListAdapter class - this needed the necessary action to pull down the link based on our parsed URL and then inflate the ImageView in our row:
if (jsonImageText.get("imageLink") != null){
String url = (String) jsonImageText.get("imageLink");
URL feedImage= new URL(url);
HttpURLConnection conn= (HttpURLConnection)feedImage.openConnection();
InputStream is = conn.getInputStream();
Bitmap img = BitmapFactory.decodeStream(is);
imageView.setImageBitmap(img);
}