Category Archives: Android

Customising F-Droid client (Android repo)

This is part 2 of a 2 part series about rolling out your own “Play Store” based on F-Droid for Android devices.

Server set up instructions can be found at http://paulscott.co.za/blog/setting-up-a-private-android-appstore-with-fdroid/

The first thing that we need to do, is to get hold of the source for the client at gitorius.

Do a git clone of the code with

git clone https://git.gitorious.org/f-droid/fdroidclient.git fdroid-client

which will give you a checkout in the fdroid-client directory.
Directly afetr the checkout completes, cd into your new fdroid-client directory and grab the git submodules with:

git submodule update --init

and then we need to prepare for our ant biulds with:

./ant-prepare.sh # This runs 'android update' on the libs and the main project
ant clean release

Which will then build the stock release version of the client. Now, the point here is to do some customising right? OK, so let’s start with that then…

The first step here is to make sure that you can install your new package manager client alongside FDroid client too. In order to do this, edit tools/change-package-name.sh and change the parameters that you need to (package name and identifier)

FDROID_PACKAGE=${1:-org.your.fdroid}
FDROID_NAME=${2:-Your FDroid}

Change these values to something closer to what you want i.e.

FDROID_PACKAGE=${1:-za.co.paulscott.fdroid}
FDROID_NAME=${2:-Pauls FDroid Client}

Execute the package name changer script and get yourself a cookie for getting this far!

Now would be a good tme to import the code into an IDE like Eclipse or Android Studio. Note, however, that due to the way this project is built and laid out, it will probably not compile, and Eclipse will moan about it. No matter though, you are only modifying code in Eclipse, and will rebuild via the command line once done.

One thing that you really should change is to edit res/values/default-repo.xml and modify it to at least include your new server repo that you set up in part 1.
You probably also want to change no-trans.xml and the “about” clause to reflect your own installations too.
If you are rolling out a complete solution, don’t forget to check out strings.xml as well, so that your application makes sense!

Once that is done, you are free to look through the code to modify whatever else you think may be useful, but I think that the above should do for most situations. If you want to change the launcher icon etc, then create a new Android icon project in Eclipse and do it that way, as it is far easier than manually.

Once all your changes are done, rebuild and ship your brand new client!

ant clean emma debug install [test]

Done!

Android AsyncTask

I have seen with many apps that the main thread is sometimes (ab)used by doing too many asynchronous tasks in it. This is very easily resolved by making use of the AsyncTask class in android.os.AsyncTask.

A simple example. Let us assume that you want to call a set of WordPress REST URL’s and get a bunch of JSON back to work with. This is actually really simple and non-blocking if you do it properly with AsyncTask.

The key here is that you need to subclass all of your calls with AsyncTask calls.The class will need to override at least one method

doInBackground(Params ...)

and in some cases, you will probably want to override

onPostExecute(Result)

.

I think that the method names are sufficient description for what they do in this case.

With that in mind, let’s create our “Task” class:

package com.myapp.tasks;

import java.util.ArrayList;
import java.util.List;

import com.myapp.PostLister;
import com.myapp.model.PostInfo;

import de.akquinet.android.androlog.Log;
import android.os.AsyncTask;

public class PostListTask extends AsyncTask<String, Integer, List<PostInfo>> {

	private static final String TAG = "PostListTask";

	@Override
	protected List<PostInfo> doInBackground(String... params) {
		List<PostInfo> posts = new ArrayList<PostInfo>();
		for (String urlid : params) {
			PostLister postlist = new PostLister();
			PostInfo post = postlist.getURL(urlid);
			posts.add(post);
		}
		if (isCancelled()) {
			Log.e(TAG, "User cancelled listing " + params);
		}
		Log.i(TAG, "Post list done..");
		return posts;
	}
}

Once that is done, we need to fill in the missing classes

package com.myapp;

import org.json.JSONException;
import org.json.JSONObject;

import com.myapp.model.PostInfo;
import com.myapp.util.JSONParser;

public class PostLister {

	public static final String url="http://paulscott.co.za/blog/wp-json.php/posts/";
	public static final String urlid = "";
	public static final String TAG_CONTENT = "content";
	public static final String TAG_TITLE = "title";
	public static final String TAG_LINK = "link";
	public static final String TAG_ID = "ID";
	public static final String TAG_SLUG = "slug";
	public static final String TAG_DATE = "date";

	JSONParser jParser = new JSONParser();
	
	public PostInfo getURL(String urlid) {
		JSONObject json = jParser.getJSONFromUrlByGet(url+urlid);
		try {
			String str_content = json.getString(TAG_CONTENT);
			String str_title = json.getString(TAG_TITLE);
			String str_link = json.getString(TAG_LINK);
			String str_ID = json.getString(TAG_ID);
			String str_slug = json.getString(TAG_SLUG);
			String str_date = json.getString(TAG_DATE);
			PostInfo post = new PostInfo(str_content, str_title, str_link, str_ID, str_slug, str_date);
			return post;
		} catch (JSONException e) {
			// whatever...
		}
		return null;
		
	}
}

As you can see, we are only using a few fields from the JSON produced by the WP-JSON plugin, but you get the gist right?

Now for a model

package com.myapp.model;

public class PostInfo {
	
	private String content;
	private String title;
	private String link;
	private String id;
	private String slug;
	private String date;
	
	public PostInfo(String content, String title, String link, String id,
			String slug, String date) {
		super();
		this.content = content;
		this.title = title;
		this.link = link;
		this.id = id;
		this.slug = slug;
		this.date = date;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getLink() {
		return link;
	}

	public void setLink(String link) {
		this.link = link;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getSlug() {
		return slug;
	}

	public void setSlug(String slug) {
		this.slug = slug;
	}

	public String getDate() {
		return date;
	}

	public void setDate(String date) {
		this.date = date;
	}	
}

Pretty standard stuff.

Once we are ready to fire it off, we simply invoke the AsyncTask with

AsyncTask<String, Integer, List<PostInfo>> posts = new PostListTask().execute("492", "491");

Which you can pretty much do whatever you want with:

try {
        	List<PostInfo> res = posts.get();
        	for(PostInfo post : res) {
        	    String content = post.getContent();
        	    Log.d(TAG, content);
        	}
        } catch (InterruptedException e) {
        	e.printStackTrace();
        }
        catch (ExecutionException e) {
        	e.printStackTrace();
        }

Where the String array we send is a list of the posts that we want to retrieve.

Dead simple, fast and efficient! Yay!

Android Volley – HTTP async Swiss Army Knife

This serves as a post to help you get started with Android Volley. Volley is used for all sorts of HTTP requests, and supports a whole bang of cool features that will make your life way easier.

It is a relatively simple API to implement, and allows request queuing, which comes in very useful.

The code below is a simple example to make a request to this blog and get a JSON response back, parse it and display it in a simple TextView widget on the device.

package za.co.paulscott.volleytest;

import org.json.JSONObject;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

public class MainActivity extends ActionBarActivity {

    private TextView txtDisplay;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
        
        txtDisplay = (TextView) findViewById(R.id.txtDisplay);

		RequestQueue queue = Volley.newRequestQueue(this);
		String url = "http://paulscott.co.za/blog/wp-json.php/posts/100";

		JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

			@Override
			public void onResponse(JSONObject response) {
				Log.i("volleytest",response.toString());
				String txt = response.toString();
				Log.i("volleytest", txt);
				txtDisplay.setText(txt);
				findViewById(R.id.progressBar1).setVisibility(View.GONE);
			}
		}, new Response.ErrorListener() {

			@Override
			public void onErrorResponse(VolleyError error) {
				// TODO Auto-generated method stub

			}
		});

		queue.add(jsObjRequest);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }
    }

}

Maven for Android

This is a quick howto on setting up and using Maven for your Android projects. Maven Android integration is not yet excellent, but is coming along nicely, and if you are familiar with Maven projects, will make managing your dependencies a lot easier!

I will be working with Ubuntu, but your set up will be similar. Just adapt paths etc for your setup as you need.

Ubuntu ships with Maven2, but we need Maven-3.0.5 at least in order to work with Android. I prefer to install maven manually because you don’t need to stress about pinning and other such nonsense from a binary distro. I also usually install stuff in /opt/ so that is where we will be working from.

The first thing that you need to do, is to grab the maven distribution file. I used 3.2.1, but anything later than 3.0.5 should work OK.

wget http://apache.saix.net/maven/maven-3/3.2.1/binaries/apache-maven-3.2.1-bin.tar.gz

Extract the archive and copy it to /opt/

sudo cp apache-maven-3.2.1 /opt/

Great! First steps completed! You are doing well so far!
I am assuming that you have a semi-recent JDK installed, in our case we need JDK 6+. Check for your JDK version with

java -version

If all comes back OK, we are ready to proceed.

Get the path to your JDK now with

locate bin/java | grep jdk

and make a note of it. Mine is at

/opt/java7/jdk1.7.0_45

Edit your bashrc file (located at /etc/bash.bashrc on Ubuntu) and add the following parameters (modify according to your paths) to the end of the file:

export ANDROID_HOME=/opt/android-sdk-linux
export M3_HOME=/opt/apache-maven-3.2.1
export M3=$M3_HOME/bin
export PATH=$M3:$PATH
export JAVA_HOME=/opt/java7/jdk1.7.0_45
export PATH=$JAVA_HOME/bin:$PATH:/opt/java7/jdk1.7.0_45

Load up your new basrc file with

source /etc/bash.bashrc

and check that everything is OK.
You should now be able to test your brand new Maven3 installation with

mvn -version

If that seems OK, you are ready to install the Android m2e connector in Eclipse. Please note that this works best in Eclipse Juno or later (I use Kepler).

Open up Eclipse, and choose to install software from the Eclipse Marketplace. This is found in Help -> Eclipse Marketplace. Do a search for “android m2e” and install the Android configurator for M2E 0.4.3 connector. It will go ahead and resolve some dependencies for you and install.

You should now be able to generate a new Android project in Eclipse with New Project -> Maven -> new Maven project and in the archetype selection, look only in the Android catalogue or filter on “de.akquinet.android.archetypes” and choose the android quickstart project.

If this fails, you can also generate a new project on the command line and simply import it to Eclipse.

mvn archetype:generate \
  -DarchetypeArtifactId=android-quickstart \
  -DarchetypeGroupId=de.akquinet.android.archetypes \
  -DarchetypeVersion=1.0.11 \
  -DgroupId=com.your.company \
  -DartifactId=myshinyapp

Once all of that is complete, dev carries on as usual. Remember that now dependencies are in your POM.xml document, so check that out first and ensure that you have some basics in there:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.your.company</groupId>
	<artifactId>myshinyapp</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>apk</packaging>
	<name>myshinyapp</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<platform.version></platform.version>
		<android.plugin.version>3.6.0</android.plugin.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>com.google.android</groupId>
			<artifactId>android</artifactId>
			<version>4.1.1.4</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>com.actionbarsherlock</groupId>
			<artifactId>actionbarsherlock</artifactId>
			<version>4.4.0</version>
		</dependency>

		<!-- Androlog is a logging and reporting library for Android -->
		<dependency>
			<groupId>de.akquinet.android.androlog</groupId>
			<artifactId>androlog</artifactId>
			<version>1.0.5</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>joda-time</groupId>
			<artifactId>joda-time</artifactId>
			<version>2.3</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>${project.artifactId}</finalName>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>com.jayway.maven.plugins.android.generation2</groupId>
					<artifactId>android-maven-plugin</artifactId>
					<version>${android.plugin.version}</version>
					<extensions>true</extensions>
				</plugin>
			</plugins>
		</pluginManagement>
		<plugins>
			<plugin>
				<groupId>com.jayway.maven.plugins.android.generation2</groupId>
				<artifactId>android-maven-plugin</artifactId>
				<configuration>
					<sdk>
						<platform>17</platform>
					</sdk>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

As you can see, I have included some other stuff, like ActionBarSherlock and JodaTime in case, as they are generally really useful, and it may save you some time just copying the dependency information!

Have fun!

How to start an Android app at boot time

I wanted to have my custom Android ROM boot up and start an application specific to my needs start immediately. This is the way to accomplish it:

In your AndroidManifest.xml document (application part):

<receiver android:enabled="true" android:name=".BootUpReceiver"
        android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
        <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
</receiver>

You also need to set up a permission with

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

and then create the BootUpReceiver class to handle it

public class BootUpReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
                Intent i = new Intent(context, MyWhateverActivity.class);  
                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(i);  
        }
}

Android Intent to install app dependencies

I finally find myself actually having to learn Android app development properly now, and came up against an issue where I need my app to [optionally] install another app form my F-Droid repo or the Google Play store.

Luckily, either way, you should be covered as the Android framework is clever enough to parse the market:// URI’s as “generic package manager” so if you have a Free ROM that does NOT include Google Play services, you should still be OK with this code.

I have set a button click event to go and check if the app is installed, and then install it with an alert.

Button boButton = (Button) findViewById(R.id.bo_button);

        boButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View arg0) {

                try{
                    Intent intent = new Intent("com.dstv.boxoffice.android.SCAN");
                    intent.setPackage("com.dstv.boxoffice.client.android");
                    startActivityForResult(intent, 0);
                } catch (Exception e) {
                    createAlert("BoxOffice Application not installed!", "This application uses " +
                            "the BoxOffice application by DStv, you need to install " +
                            "this before you can use this functionality!", true, "market://search?q=pname:com.dstv.boxoffice.client.android");
                }

            }
        });

The intent above will fire off an alert (see code below) and prompt the user to install the required package.

private void createAlert(String title, String message, Boolean button, final String marketURL) {
        AlertDialog alertDialog;
        alertDialog = new AlertDialog.Builder(this).create();
        alertDialog.setTitle(title);
        alertDialog.setMessage(message);
        if ((button == true)) {
            alertDialog.setButton("Download Now",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface arg0, int arg1) {
                            Intent browserIntent = new Intent(
                                    Intent.ACTION_VIEW,
                                    Uri.parse(marketURL));
                            startActivity(browserIntent);
                        }
                    });
        }
        alertDialog.show();
    }

Simple!

Setting up a private Android appstore with FDroid

Introduction

There are a number of use cases to create an appstore with private data. As we create and release more devices and products, we are able to create matching AppStores to support them.
Appstores can be a way of controlling the software on the devices accurately, providing speed of update via a local store as opposed to Google Play, and also allows us a lot more flexibility in what we push to devices (agility). Our own curated appstores will also allow us to generate an income stream if we so desire, as well as create a dev community ecosystem for creation of apps for devices.

F-Droid server

FDroid is an Open Source package of server and client applications to deploy an app ecosystem similar to Google Play. The server scripts are written in Python language and the client is written in Java using Android SDK.

The FDroid Repository is an easily-installable catalogue of FOSS applications for the Android platform. The server contains the details of multiple versions of each application, and the Android client makes it easy to browse, install them onto your device, and keep track of updates.

Detailed installation instructions can be found in the official manual.

Prerequisites

Python

You will need a server, with a Python runtime installed. The python runtime should have the following prerequisites installed:
* PIL
* pip
* Python buildtools

Remember to install PIL with JPEG and Zlib support for reading APK files! I.e. install libjpeg and zlib1-dev packages for your OS BEFORE installing PIL with pip!

Android SDK

The Android SDK is used throughout the server to manage and read metadata from the APK files. It is also used for signing, compiling source and numerous other tasks
* Grab the SDK from http://developer.android.com/sdk/index.html
* Update the SDK once unpacked on the server with android sdk update –no-ui
* export ANDROID_HOME to the pwd

FDroid source

Grab the fdroid-server source from Gitorious
* git clone git://gitorious.org/f-droid/fdroidserver.git

Nginx installation

I used Nginx to serve the repo files, as nginx is very well suited to serving static content very quickly. There will be little modifications and no dynamic content as we proceed, so nginx is a good choice. You may use other web servers, but you will be on your own.
* sudo apt-get install nginx (on Ubuntu flavours)
* edit /etc/nginx/nginx.conf to suit your setup
* service nginx start/restart
* The nginx docroot is in /usr/share/nginx/www
* You may need to edit /etc/nginx/sites-enabled and allow directory listing of index.xml too.

Browsing to the IP/hostname of the server should now bring up an nginx placeholder page.

FDroid setup

* Create a directory to hold your repo.
* Copy the file config.py from fdroid. Edit the two following propertis in config.py with appropriate values:
* sdk_path = “/tools/android-sdks” (This should reflect the path of your Android SDK installation)
* repo_url = “http://localhost/fdroid” (this should be your server IP/hostname)
* repo_icon = “icon.png” (this must exist as a 48x48px png)
* Run fdroid update -c This will create a repo and aerchive directory
* Place all APK’s inside the repo directory
* Run fdroid update again. This will unpack and parse all of the APK files and make entries for them
* Done!

You should now be able to browse your repo via a web browser!

Next steps will be to customize the client, which will be on a seperate post