22 December 2014

Android Lollipop Shared Elements


In Lollipop the new material design has given us a new feature called shared elements or shared transitions. This allows us an easy method of animating common elements between screens. Sure it doesn't add any features, but it looks nice. Anything that gives the user a tiny bit of a happy design feeling is worth some effort.

The kodaline example demonstrates this:
http://android-developers.blogspot.co.uk/2014/10/implementing-material-design-in-your.html

It wasn't immediately obvious to me how this works after following Android developer's code examples there. So I created a simple example. I wasn't really interested in the full page animation with all the other pieces animating into place. I just wanted a really quick and easy win to take advantage of material. My example is a main activity with two small images on. Then a details activity with a single large activity. You click on an image, it gets bigger.

So there are four elements to this simple transition.


  1. values-v21/styles.xml
    Create a values-v21 directory in your res folder and add in a styles.xml file. This will override your existing styles file for Lollipop, you need the windowContentTransitions element. Here's my whole file:

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:android="http://schemas.android.com/apk/res/android">
    
        <style name="AppTheme" parent="android:Theme.Material">
            <item name="android:windowContentTransitions">true</item>
        </style>
    </resources>
    


  2. Activity xml
    Add a transitionName element to the xml of the item you wish to share. For my example I'm just using an image, so I've added the transitionName  to the image in activity_main, and of course the image in the details activity.

    Activity main:
        <ImageView
            android:id="@+id/activity_main_monkey"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center_horizontal"
            android:tag="@string/monkey"
            android:layout_marginTop="30dp"
            android:transitionName="@string/MyTransitionName"
            android:src="@drawable/monkey" />
    

    Activity Details:
        <ImageView
            android:id="@+id/activity_details_image"
            android:layout_width="wrap_content"
            android:layout_height="400dp"
            android:layout_gravity="top|center_horizontal"
            android:layout_marginTop="10dp"
            android:transitionName="@string/MyTransitionName"
            android:src="@drawable/monkey" />
    


  3. Launch detail activity from main
    Now comes the bit where we launch the second activity, from the first. Normally this is just a simple startActivity but the magic of shared transitions works by adding some options here. The important bit is this:
    options = ActivityOptions.makeSceneTransitionAnimation(this, v, getString(R.string.MyTransitionName));
    

    As you can see we use the same transition name we put in the xml. I've also added in a few if statements so we can run this on pre lollipop and it won't crash.

        @SuppressLint("NewApi")
        private void startDetailsActivity(View v){
            Intent intent = new Intent(this, DetailsActivity.class);
            ActivityOptions options = null;
            // create the transition animation - the images in the layouts of both activities are defined with android:transitionName="MyTransition"
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.L) {
                options = ActivityOptions.makeSceneTransitionAnimation(this, v, getString(R.string.MyTransitionName));
            }
            intent.putExtra(DetailsActivity.TAG_IMAGE_NAME, (String) v.getTag());
            // start the new activity
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && options != null) {
                this.startActivity(intent, options.toBundle());
            }else{
                startActivity(intent);
            }
        }
    
That's it. Hope it helps.

Here's the code: https://github.com/jimbo1299/shared-transitions



05 December 2014

Lollipop update for Nexus 7 (2012) - Update


I previously blogged about problems with Lollipop on my Nexus 7. After quite a few emails with Google Play support I have been promised that an update to fix this issue began roll out globally on 3rd December. So fingers crossed the many upset users out there will shortly have the problem solved. Well done Google (assuming it works).

20 November 2014

Android Studio Keyboard shortcuts


I've previously blogged about my frustrations moving from Eclipse to Android Studio. Largely this extends from a re-work of the keyboard mappings. I've used Eclipse for many years and have become very reliant on my keyboard short-cuts to speed up my work.

Recently I discovered the ability to make Android Studio run using Eclipse shortcuts. This literally changed my whole working day and has made me so happy!

Simply click File -> Settings then either scroll down to keymap or search for keymap.

In the dropdown just change the keyboard shortcuts to Eclipse:


Brilliant :D

Lollipop with Nexus 7 (2012)

Just a quick one to anyone who owns the 2012 version of the Nexus 7. DON'T upgrade to Lollipop.
I got the Over the Air (OTA) update last week and put it off for a few days as I don't like to rush. Eventually I got fed up with the reminders and went ahead.

Well what a disaster, its made my Nexus 7 almost unusable. The Keyboard doesn't open for sometimes up to a minute, very few of the apps run at all and its generally slow and un-responsive. 

This is a crying shame as I think Lollipop is a great release, I like the direction Android has taken. Its just a real shame Google didn't test this before pushing it out to everyone.

05 November 2014

Android ripple effect in Lollipop

Just a quick how-to today.

So Google have made a big thing of the new ripple effect in Android Lollipop.
http://android-developers.blogspot.co.uk/2014/10/implementing-material-design-in-your.html

 To my eyes however it wasn't immediately clear how to implement it, particularly if you wanted your app to work in older versions of Android, which almost everyone will.

So the first point to note is android buttons automatically implement the ripple effect if you've got the material theme running. However I mostly used TextViews as buttons with custom drawables for their background and onClick state.

Second I'm not talking about getting ripple working in old versions of Android, just a graceful rollback to a pressed state.

So first create a drawable and a drawable-v21 folder under res, in each add a button_selector.xml

res/drawable/button_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Selected -->
    <item android:state_selected="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Focus -->
    <item android:state_focused="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Default -->
    <item android:drawable="@color/colorPrimary"/>
</selector>

res/drawable-v21/button_selector.xml
<ripple
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/accent">
    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Selected -->
    <item android:state_selected="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Focus -->
    <item android:state_focused="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Default -->
    <item android:drawable="@color/colorPrimary"/>
</ripple>

now in your activity_main or wherever you need to add a button with a background

<TextView
    android:id="@+id/activity_main_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:background="@drawable/button_selector"
    android:padding="20dp"
    android:text="Button"/>

You'll notice the ripple xml basically incorporates the same drawables as the selector does, adding on the ripple effect.

04 November 2014

Android CursorAdapter


So the other day some stack overflow numpty yelled at me that I should use a cursor adapter instead of an array adapter. Well, just a point of habbit I thought. However I'd not extensively used a cursor adapter before, so I thought I'd give it a shot before I dismissed his suggestion.

Anyway I decided to come up with a simple example as there aren't that many examples of straight cursorAdapters out there and simpleCursorAdapters just don't cut it. The basic idea of a cursor adapter is to directly link to database adapter, rather than say an array adapter, which displays an array of any type of data.

So first grab you data, obviously this can be whatever you want. I copied a Google contacts example and simplified it.

This is inside a Fragment:

    @SuppressLint("InlinedApi") 
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        
        @SuppressLint("InlinedApi")
        final String SORT_ORDER = Utils.hasHoneycomb() ? Contacts.SORT_KEY_PRIMARY : Contacts.DISPLAY_NAME;
        
        @SuppressLint("InlinedApi")
        final String[] PROJECTION = {
                Contacts._ID,
                Contacts.LOOKUP_KEY,
                Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME,
                Utils.hasHoneycomb() ? Contacts.PHOTO_THUMBNAIL_URI : Contacts._ID
        };
        
        final String SELECTION =
                (Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME) +
                "<>''" + " AND " + Contacts.IN_VISIBLE_GROUP + "=1";
        
        if(id == QUERY_ID){
            return new CursorLoader(getActivity(),
                    Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    null,
                    SORT_ORDER);
        }
        
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if (loader.getId() == QUERY_ID) {
            mAdapter.swapCursor(data);
            mAdapter.notifyDataSetChanged();
        }
    }


Then we need our adapter:

public class AdapterNewContacts extends CursorAdapter{
    
    private LayoutInflater mInflater;
    
    public AdapterNewContacts(Context context) {
        super(context, null, 0);

        mInflater = LayoutInflater.from(context);
    }
    
    @SuppressLint("InlinedApi") 
    public void bindView(View view, Context context, Cursor cursor) {
        
        final ViewHolder holder = (ViewHolder) view.getTag();
        
        holder.textName.setText(
            cursor.getString(cursor.getColumnIndex((Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME)))
        );
    }

    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View convertView = mInflater.inflate(R.layout.contacts_list_item, parent, false);
        
        final ViewHolder holder = new ViewHolder();
        holder.textName = (TextView) convertView.findViewById(R.id.contacts_list_item_name);
        
        convertView.setTag(holder);
        return convertView;
    }
    
    private class ViewHolder {
        TextView textName;
    }
}



That's it! Notice the difference between this and our normal ArrayAdapter, there's no getView() and we actually have one method for creating the view and one for re-aquiring the view. This is different from a getView where we have to create and get views in one method.

So pretty simple. I still don't agree with whoever suggested this though, it's too restrictive. In an ArrayAdapter I can create my own dataset and update it however I want. With this example I'm limited to only the one cursor.

Oh well, it was an interesting experiment.



20 September 2014

Android listview with differing rows


One of the Android questions I regularly see on stack overflow is how to have a listview with different rows. Different images, different highlights or whatever the UI calls for. I've answered the question more than once myself but as it comes up so often, I thought I'd write a article on it. Plus it lets me highlight a few extra points around adapters and caching that I often see missed.

So for this tutorial I'm going to create a super simple example of a few listview rows with differing color backgrounds. Hopefully from this super simple example you can see how you might add an imageview or various text elements, or whatever your heart my fancy.

First lets say each element in our listview is going to be an international rugby player. Now these big chaps all come from different countries so we'll identify them as such. First we need a rugby player object:


package example.com.multiitemlistview;

public class rugbyPlayer {

    public static final int COUNTRY_ENGLAND = 1;
    public static final int COUNTRY_NZ = 2;
    public static final int COUNTRY_AUS = 3;
    public static final int COUNTRY_SA = 4;

    private String playerName;
    private int countryId;

    public rugbyPlayer() {}

    public rugbyPlayer(String playerName, int countryId) {
        this.playerName = playerName;
        this.countryId = countryId;
    }

    public String getPlayerName(){
        return this.playerName;
    }

    public void setPlayerName(String playerName){
        this.playerName = playerName;
    }

    public int getCountryId(){
        return this.countryId;
    }

    public void setCountryId(int countryId){
        this.countryId = countryId;
    }
}

Note that I've added some public magic numbers to help easily identify where the rugbyPlayer is from.

Now our activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/activity_main_listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>

and ActivityMain.java, nothing here should be rocket science, but take a minute to note that we create an ArrayList of players which we then pass in as the third argument to the adapter.

package example.com.multiitemlistview;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

import java.util.ArrayList;


public class ActivityMain extends Activity {

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

        rugbyPlayer player;

        //Create list of rugby players
        ArrayList<rugbyPlayer> players = new ArrayList<rugbyPlayer>();
        player = new rugbyPlayer("Jonny Wilkinson", rugbyPlayer.COUNTRY_ENGLAND);
        players.add(player);
        player = new rugbyPlayer("Richie McCaw", rugbyPlayer.COUNTRY_NZ);
        players.add(player);
        player = new rugbyPlayer("Martin Johnson", rugbyPlayer.COUNTRY_ENGLAND);
        players.add(player);
        player = new rugbyPlayer("Brian Habana", rugbyPlayer.COUNTRY_SA);
        players.add(player);

        //Create Adapter
        AdapterPlayers adapter = new AdapterPlayers(this, R.layout.item_player, players);

        //Set Listview adapter
        ((ListView) findViewById(R.id.activity_main_listview)).setAdapter(adapter);
    }

}



So far so good, now the important part is the Adapter which, as they say, is where the magic happens.

package example.com.multiitemlistview;


import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.ArrayList;


public class AdapterPlayers extends ArrayAdapter {

    ArrayList<rugbyPlayer> mPlayers;
    LayoutInflater mInflater;
    Context mContext;

    public AdapterPlayers(Context context, int resource, ArrayList<rugbyPlayer> items) {
        super(context, resource);
        mInflater = LayoutInflater.from(context);
        mContext = context;
        mPlayers = items;
    }

    @Override
    public int getCount() {
        return mPlayers.size();
    }

    @Override
    public Object getItem(int position) {
        return mPlayers.get(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder viewHolder = new ViewHolder();
        rugbyPlayer currentPlayer = mPlayers.get(position);

        if(convertView == null){
            //If convertView is null we must re-infalte view
            convertView = mInflater.inflate(R.layout.item_player, parent, false);
            viewHolder.playerName = (TextView) convertView.findViewById(R.id.item_players_name);
        }else{
            //Else object has been cached
            viewHolder = (ViewHolder) convertView.getTag();
        }

        //Now we just set the name
        viewHolder.playerName.setText(currentPlayer.getPlayerName());

        //Now we set the background color
        switch(currentPlayer.getCountryId()){
            case rugbyPlayer.COUNTRY_ENGLAND:
                viewHolder.playerName.setBackgroundColor(mContext.getResources().getColor(R.color.color_white));
                viewHolder.playerName.setTextColor(mContext.getResources().getColor(R.color.color_black));
                break;
            case rugbyPlayer.COUNTRY_NZ:
                viewHolder.playerName.setBackgroundColor(mContext.getResources().getColor(R.color.color_black));
                viewHolder.playerName.setTextColor(mContext.getResources().getColor(R.color.color_white));
                break;
            case rugbyPlayer.COUNTRY_SA:
                viewHolder.playerName.setBackgroundColor(mContext.getResources().getColor(R.color.color_sa));
                viewHolder.playerName.setTextColor(mContext.getResources().getColor(R.color.color_black));
                break;
            case rugbyPlayer.COUNTRY_AUS:
                viewHolder.playerName.setBackgroundColor(mContext.getResources().getColor(R.color.color_aus));
                viewHolder.playerName.setTextColor(mContext.getResources().getColor(R.color.color_black));
                break;
        }

        convertView.setTag(viewHolder);
        return convertView;

    }

    static class ViewHolder {
        public TextView playerName;
    }

}

The interesting thing with regard to caching starts at the bottom. ViewHolder is a class we use to keep the whole listitem contained within. This represents every view in the layout file. It could be one imageview, it could be twenty. In my case it's one simple textview. So when we come to getView() we first get the current item from the adapter's arraylist (the adapter is maintaining the data, not the activity).
So first, if the convertView is null, it means the listview has never used this layout before, so lets inflate it. Then apply the elements in the layout are set to the viewHolder.
If convertview is not null, we can just read in the viewHolder which we are storing in the tag.

Now comes the bit where we can customize the list element for its data. Look how simple it is to set the background color based on the data. We just switch on the countryId.

One very important thing to remember in all of this is that the listview will cache views itself and re-use them. So if you set something, you must always set the opposite in the alternate case. For example if I just set the COUNTRY_NZ case to have white text, when that view is re-used it will STILL have white text, so we could have white text and white background! So just remember always re-set anything you change for the else condition :)

That's it. Hope it helps.



22 July 2014

REST Web Services - Part 1 - CFML

Dammit Adam! If there's one thing I hate more than learning its being inspired to learn!

Last week Adam Cameron got me thinking about REST Web Services, he challenged his readers, what is the best language for building them. Well I had a few thoughts which I shared. Basically it depends which language the developer in question is most comfortable in, and which environment is most accessible. In my time I've been lucky enough to be paid to work in a lot of languages and a lot of environments, so I thought I’d do a quick re-cap of a few languages and maybe even attempt a few which I've never learnt. How long it takes before my patience and time run out, we’ll see :)


My environment will be mostly Ubuntu as that’s what I run at home these days, and I’m too poor to run a server. However if I make it on to Microsoft development I’m anticipating switching to Windows 7. As for the REST services themselves I’m going for Keep It Simple Stupid here, basic proof of concept stuff. I want a GET and a POST method and that’s about it. Also I’m largely ignoring security for the time being.


First up is ColdFusion.
I’ve spent many of my years being paid as a CFML developer and I’m most comfortable with it. I should be able to knock out some REST web services here pretty quick. As much as I like CFML, Rest in CF is a pain in the neck. To me the syntax is not intuitive, mistakes are difficult to pin point, debugging is very difficult and testing a nightmare. Someone reminded me of an add on called Taffy, which is a REST framework for CF. I've never encountered Taffy myself, so I'm hopeful and excited for the challenge.


First I install Railo express, not rocket science, suffice to say it works and in just two minutes I’m running a test cfm page. (Awesome job Railo guys!)


Now I need to get Taffy running. So I download the zip file and extract it. Loosely skimming through the docs I see I can just drop the taffy folder into my Railo root. Awesome, drag and drop, ok, now what? OK So back to the docs, I’ve got to add an extension to my Application.cfc and create a hello.cfc. So not that drag and drop then. After about thirty minutes fiddling around with resources directories and tweaking Application.cfc files I’ve finally got the Taffy welcome page working properly. What held me up was *not* using the Application.cfc supplied in the docs but instead borrowing the one from the help folder. My setup is as such:


Railo/webapps/ROOT/hiya
-- Application.cfc (see below)
-- index.cfm (empty cfm file)
Railo/webapps/ROOT/hiya/resources
-- hello.cfc (my REST webservices)


I now have Taffy running with Railo. This is awesome, the format for web services is pretty good, I don't need to endlessly update Railo or my application config, it just works. The test bed is the best thing, I love the bootstrap layout and it makes it so simple and easy to test. I've put a screenshot down the bottom.

Here's some code.


My Application.cfc is:

<cfcomponent extends="taffy.core.api">
    <cfscript>

        this.name = hash(getCurrentTemplatePath());

        variables.framework = {};
        variables.framework.debugKey = "debug";
        variables.framework.reloadKey = "reload";
        variables.framework.reloadPassword = "true";
        variables.framework.representationClass = "taffy.core.genericRepresentation";
        variables.framework.returnExceptionsAsJson = true;

        function onApplicationStart(){
            return super.onApplicationStart();
        }

        function onRequestStart(TARGETPATH){
            return super.onRequestStart(TARGETPATH);
        }

        // this function is called after the request has been parsed and all request details are known
        function onTaffyRequest(verb, cfc, requestArguments, mimeExt){
            // this would be a good place for you to check API key validity and other non-resource-specific validation
            return true;
        }

    </cfscript>
</cfcomponent>




Finally the important bit here are my web services (hello.cfc):

component extends="taffy.core.resource" taffy_uri="/hello" {

     function get(){
        return representationOf(['all your base are belong to me. :)']);
    }
    
    function post(String name){
        return representationOf(['Nice to meet you ' & name]);
    }

 }
And CFML is done. All this, including install of Railo and Taffy took me around an hour.

If you’re Adam Tuttle, congratulations Taffy is a fantastic product and I'm very impressed. The ease at which I can deploy web services and most importantly test them is now fantastic. I don’t need to create silly little html forms or anything, it’s all built in. This is genius.  If I may offer my two cents here, you've got a little work to do on the docs. Especially the initial deployment. It’s gotta be step by step built for idiots like me. If I can’t make it work quickly, I lose interest. That aside you should be really proud.



07 July 2014

Android Wearables First Go

I thought I’d try the new Android wear SDK and see if I could do anything useful with it. It took a while and a few head scratching moments but I got there in the end. What I wanted to do was send a message from the Android wear watch to my Android phone. Seems like such an easy ask!


First up you need Android Studio. I hope they make it possible with Eclipse / ADT, but I couldn't make it happen and quickly gave up! You also need to be super up to date with your SDK, as of today my versions:


  • Android SDK Tools 23.0.2
  • Android 4.4W (API 20)
  • Android 4.4.2 (API 19)
  • Google Play Services revision 18 (5.0)


Once all that is ready and working without error you need a to create a new project and follow the steps as per the Android developer page: http://developer.android.com/training/wearables/apps/creating.html
Basically create an app for mobile as per usual and a partner application for wear. You also need to setup an emulator or use a real watch. I can’t afford a real wear watch so I'm on the emulator. Follow the steps to connect your phone via usb cable and the emulator, bit fiddly but works eventually. The basic idea here is to use Google Play Services to transfer messages between the watch and the phone with the Message API. I believe this is new which is why it is so important to ensure everything is up to date.


Now the code, first the mobile side of things.


This is the gradle dependancies. My min sdk is 9 and my target sdk is 20.
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    wearApp project(':wear')
    compile 'com.android.support:appcompat-v7:19.+'
    compile 'com.google.android.gms:play-services-wearable:+'
}


Now we’re not making a fancy front end here as this is just really a proof of concept. Here’s the MainActivity.java. As you should be able to see this is a fairly simple listener.


public class MainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    MessageApi.MessageListener{


Now some member variables:


    GoogleApiClient mGoogleApiClient;
    public static final String START_ACTIVITY_PATH = "/start/MainActivity";


Here is the on create:


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

         // Create a GoogleApiClient instance
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

         mGoogleApiClient.connect();
}


What we’re doing here is initialising the Google API Client with the Wearable.API. These are the methods overwritten from the implements section.


    @Override
    public void onConnected(Bundle bundle) {
        Log.i("mobile", "Connected");
        //We are connected, we can add our listener to this Activity.
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
    }

     @Override
    public void onConnectionSuspended(int i) {
        Log.i("mobile", "Connection Suspended");
    }

     @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i("mobile", "Connection Failed");
    }

     @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        Log.i("mobile", "msg recieved and understood");

         if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
            Log.i("mobile", "******WHOOOO*******");
            //Send a message to a Handler for UI Update.
            myHandler.sendEmptyMessage(DO_UPDATE_TEXT);
        }
    }


That’s the mobile side of things more or less done :) Again, nothing fancy, just proof of concept.


Now the wearable part of the project. Here we’re going to send the message, but again we have to connect to Google API Client.


I’m just going to post the whole file here as it’s probably easier. I’ll skip the layout, as its just a button.


  1. First (in onCreate) we define and connect to our mGoogleApiClient.
  2. OnConnected we start of the getConnectedNodes Async Task. This needs to run seperate from the UI and basically grabs all connected nodes. In our case there is only one, but you should really check here and maybe flash up a dialog or something if there are more or less than one clients connected.


  1. Once that’s done we send a message as such sendMsg(results.get(0));. This sends the node ID we got from the Wearable API and calls the sendMsg function


  1. In SendMsg we call Wearable.MessageApi.sendMessage. As expected this sends our message. Right now the message is meaningless, but you could easily modify this example to send a real message and have the listener display it.


That’s it. Hope it helps, here is the wear project code:



package com.example.com.wearable;

 import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

 import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;

 import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

 public class WearActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

     private TextView mTextView;
    GoogleApiClient mGoogleApiClient;
    public static final String START_ACTIVITY_PATH = "/start/MainActivity";

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wear);

         // Create a GoogleApiClient instance
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

         mGoogleApiClient.connect();

         final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);

                 findViewById(R.id.activity_wear_send_msg).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        GetConnectedNodes task = new GetConnectedNodes();
                        task.execute(new String[]{"com"});
                    }
                });

             }
        });
    }

     private Collection<String> getNodes() {
        HashSet<String> results = new HashSet<String>();
        NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
        for (Node node : nodes.getNodes()) {
            results.add(node.getId());
            Log.i("wear", node.getId());
        }
        return results;
    }

     @Override
    public void onConnected(Bundle bundle) {
        Log.i("wear", "Connection success");
        GetConnectedNodes task = new GetConnectedNodes();
        task.execute(new String[] { "" });
    }

     @Override
    public void onConnectionSuspended(int i) {
        Log.i("wear", "Connection suspended");
    }

     @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i("wear", "Connection failed");
    }

     private void sendMsg(String node){
        String msg = "All your base";

         MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(mGoogleApiClient, node, START_ACTIVITY_PATH, msg.getBytes()).await();
        if (!result.getStatus().isSuccess()) {
            Log.e("wear", "ERROR: failed to send Message: " + result.getStatus());
        }else{
            Log.i("wear", "Message sent: " + result.getStatus());
        }

     }

     private class GetConnectedNodes extends AsyncTask<String, Void, Void> {
        protected Void doInBackground(String... params) {
            ArrayList<String> results = new ArrayList<String>();
            NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();

             Log.i("wear", "Hello from GetConnectedNodes");
            Log.i("wear", "node count:" + String.valueOf(nodes.getNodes().size()));

             for (Node node : nodes.getNodes()) {
                results.add(node.getId());
                Log.i("wear", node.getId());
            }

             if(results.size() > 0){
                sendMsg(results.get(0));
            }
            return null;
        }
    }
}

And here's the gradle:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.support:wearable:+'
    compile 'com.google.android.gms:play-services:+'
}




03 July 2014

24 Hours with Google Glass

So I've been lucky enough to get my hands on a pair of Google Glass and took them for a spin. Opinion is largely split as to their worth. They've certainly caused some excitement and a lot of chatter around the office.


Recently news has reached us that they've been banned at UK Cinemas, not a huge story but for some reason the media has made a big fuss about it. Why on earth anyone would want to record a whole film using a shaky crummy camera on your head I don't know. Does anyone download poor quality in-cinema recorded films these days?


The first thing everyone asks is about the privacy, are you filming me? toilets etc. I think this is just a fear thing, it's not that people record video where they shouldn't, it is just that they could. I remember the same discussion about camera phones, the amount of people I see texting while in the washroom is barely noticed any more....although it is gross. It’s slightly odd to me that people worry about this but they don’t worry about CCTV.


The second thing, (and I think the most important thing) that strikes most people is what do they do. OK so you put them on and take a photo or two or maybe play with the fantastic star chart app. After that it's a case of....ok now what? I think the big takeaway here is that Glass is re-active. It's great for notifications, texts, emails even phone calls work really well. Glass is happy to read them out to you and you can even reply via voice which works brilliantly. As a thing to "play" with, Glass is not impressive.


Most apps work well with voice, Glass does a good job of presenting the options available to you although it does need to improve. Google Play Music was notably poor here. You can't start nor stop music with voice, instead you have to tap the side. Any application that forces me to tap the side of the glasses loses the point. Why have a hands free wearable I can talk to, if I can only talk to it half of the time! I can tap my phone for that. It's in development though!


Next thing is in the car, obviously I tried this as a passenger :) First up the navigation app, this is very impressive. Turning itself off until a maneuver comes up then piping up with clear instructions and audio. Second is notifications with hands free, as I mentioned before, this is really great hands free and does mostly work well. However it is incredibly distracting. Even though you're still looking in the direction of the car in front, you're not focusing and so I would strongly suggest not using it whilst driving.


This leads nicely into my conclusion, what are they for? The hard cold fact is they are big and look weird. So you're not going to use them out with friends. I was very self conscious in public so avoided wearing them out and about. So if you don't use them when driving, don't use them in public and don't use them when with friends when would you use them? Which begs the question where are Google taking them? Are they hoping we'll all just suck it up and start looking a bit nerdy? Or is it just one big experiment?


So to conclude they are neat, they mostly work well and seem like a great step toward augmented reality. However they *are not* augmented reality, a few killer apps would work great for this but I can’t see that happening. I think the privacy critics will hush eventually. I really don't think much will happen until they make them smaller and more discrete, but maybe this is just Google's aim to nudge the rest of the world toward better lenses and smaller technology. For now I think Google will push wearables like watches more than Glass, watches don’t have cameras! I suspect Glass will become just another experiment or niche product. Maybe the technology for augmented reality just isn't there yet, but good try Google.

25 June 2014

Android and Google wishlist


Tomorrow is Google IO. The highlight of the year for all things Google, basically Christmas for an Android developer! There are millions of tech blogs out there speculating as to what we should expect. Sadly I'm in not at all affiliated with Google, or its employees, so I'm in no position to offer any insight what-so-ever!

I can however outline a few things off my Google / Android wishlist. This is very much a work in progress and I would love some comments and feedback on your thoughts / wants!

1) Android App Build Speed
This is a biggie for developers, I admit, I have no idea as to its feasibility. Building and deploying an app from Eclipse onto a device / Virtual Device takes forever. Especially annoying if you're working on the UI and need to tweak and deploy small regular updates. I wish it was super fast!

2) Google Now on Lock Screen
OK I haven't *really* thought this one through, as there are obvious security restrictions. However when I'm driving, I want to be able to say "OK Google...." Some things would be great when I'm in the car. For example, "OK Google, call Arthur Dent...." Very useful if I have bluetooth. "OK Google, play Guns N Roses". Again possibly brilliantly useful.

3) Android TV
Chromecast rocks, it really is brilliant, what happened to that idea of getting it built into TVs? That would save me a HDPI port and that annoying usb cable.

4) Upload my movies to Google Movies
I know this will never happen, but how bout letting me upload my existing dvd movies or ultraviolet movie collection to Google Movies so I can play them on my chromecast?

5) Google Photowall
Random one, my Mum doesn't shut up about photos on the TV. She will literally die happy if she can show her bffs her boring (trust me) holiday snaps on the TV. Preferablly controlled by her tablet. I did try a photowall product from Google but it was vaporware and a bit rubbish.

6) Shadows in Android
My boss is an iPhone user <sigh>. He doesn't shut up about Shadows! Sure we can add a hack with degrading lines but they don't look or feel as good or as natural shadows.

7) Java 1.7 for Android
Would be nice

8) Chromecast Que
I want to be able to que music. Through all my friends. Much like the Nexus Q. I want party mode, so all my friends can contribute to a shared music que from their unique music que.

9) Development on a Chromebook
I want a chromebook, I like the concept. However I can't justify a laptop that I can't develop on!

10) Bring back Ned Stark.

That's all I've got for now. Would love to hear your ideas....

--
I've updated this a bit as watching Google I/O. Seems like they got a few of my ideas sorted before I could articulate them ;)

21 March 2014

Android type ahead filter


Any time I find a self filtering search box that works properly, my life is improved! Good UI and all that :) I find auto complete a great way to search and thought I'd share. There is no rocket science here, I just pieced this together from a few various examples.

First a simple activity main:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- Pretty hint text, and maxLines -->
    <EditText 
        android:id="@+id/searchbox1" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="@string/filter_hint"
        android:inputType="text"
        android:maxLines="1"/>

    <!-- Set height to 0, and let the weight param expand it -->
    <!-- Note the use of the default ID! This lets us use a ListActivity still! -->
    <ListView android:id="@+id/listview1"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1" /> 
</LinearLayout>

Next in my ActivityMain I create a helper method getNames() to load in the data. Obviously you'll do this in your own unique way. Also note the TextWatcher, this is pretty simple.

    private TextWatcher filterTextWatcher = new TextWatcher() {

        public void afterTextChanged(Editable s) {
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
            arrayAdapter.getFilter().filter(s);
        }

    };

   
    private ArrayList<String> getNames(){
        String[] names = getResources().getStringArray(R.array.planets_array);
        ArrayList<String> listNames = new ArrayList<String>();
        
        for(String name: names){
            listNames.add(name);
        }
        
        return listNames;
    }

Then in onCreate we put it all together:

        mFriendsList = (ListView) findViewById(R.id.listview1);
        
        //Apply Filter
        filterText = (EditText) findViewById(R.id.searchbox1);
        filterText.addTextChangedListener(filterTextWatcher);
        
        arrayAdapter = new newNameAdapter(this, getNames());
        arrayAdapter.sort();
        
        mFriendsList.setAdapter(arrayAdapter);

Finally the complicated (ish) bit is the Adapter.

package com.example.testleaguefinder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;


import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;

public class newNameAdapter extends BaseAdapter implements Filterable{

    Context context;
    ArrayList<String> names;
    private LayoutInflater mInflater;
    private GameFilter filter;
    ArrayList<String> orig;
    
    public newNameAdapter(Context _context, ArrayList<String> _names) {
        this.context = _context;
        this.names = _names;
        filter = new GameFilter();
        
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    
    @Override
    public int getCount() {
        return names.size();
    }

    @Override
    public String getItem(int position) {
        return names.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.listview_names, null);
        }
        
        TextView txtName = (TextView) convertView.findViewById(R.id.listview_names_name);
        txtName.setText(getItem(position));
            
        return convertView;
    }
    
    public void sort(){
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String fruite1, String fruite2)
            {
                return  fruite1.compareTo(fruite2);
            }
        });
    }
    
    @Override
    public Filter getFilter() {
        return filter;
    }
    
    private class GameFilter extends Filter {
        
        public GameFilter() {
        }
        
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            String lowerConstraint = constraint.toString();
            FilterResults oReturn = new FilterResults();
            ArrayList<String> results = new ArrayList<String>();
            String league;
            
            if (orig == null){
                orig = names;
            }
            
            if (lowerConstraint != null){
                lowerConstraint = lowerConstraint.toLowerCase(Locale.UK);
                if (orig != null && orig.size() > 0) {
                    for (String g : orig) {
                        league = g.toLowerCase(Locale.UK);
                        if (league.contains(lowerConstraint)){
                            results.add(g);
                        }
                    }
                }
                oReturn.values = results;
            }
            return oReturn;
        }
        
        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            names = (ArrayList<String>)results.values;
            notifyDataSetChanged();
        }
    }
    
    
}

Most of it should be fairly standard, the only interesting bit should be the Filter. All we're doing really is taking the entered text and lower casing it. Then we loop through the list of entries and find any that contain the filter string.

Simples!



28 February 2014

Android Unit Tests


At my old gig I did unit tests....lots and lots of unit tests. At the mercy of one boisterous kiwi I got pretty good at it too, truth be told I began to really enjoy it. Unit testing (done well) gives you confidence in your code and allows you to make changes reassured that previous functionality won't break.
IMHO Any programmer worth their salt should unit test where possible.

Anyway, I've been in the Android world for some time and haven't really had the chance to start experimenting with unit testing. Thankfully Google has a really good explanation of how to set-up a unit testing environment:

http://developer.android.com/tools/testing/testing_android.html

However there's tonnes of information thrown at you there and their Eclipse example focuses on the UI. Now in my experience UI testing with automated tests is tricky. While it's fantastic if you can do it, I've found the UI changes too frequently too maintain automated tests. Business cases are naturally more concerned with the UI so it changes regularly and more often that not their are just too many factors involved. So I've written a very quick demo that focuses just on unit testing code.

The Android App To Be Tested

For the purposes of example I've created a very very simple Android app. Just run through the wizard and create a default app. I called mine myAwesomeAndroidApp. Then add the following method too the MainActivity:

public static boolean validateIsMonth(int month){
    
    if(month > 0 && month < 13){
        return true;
    }
    
    return false;
}

This is the method I want to test, simplest thing I could think of.

The Test Framework.

Eclipse ADT contains a framework for creating stand alone testing apps. This enables you to keep your tests and your deployable app separate. To start a new app goto
New -> Other -> Android Test Project


Follow the wizard through, of course ensuring you select your myAwesomeAndroidApp as the test target. You should now have two projects, the test project having an empty package.
In the empty package create a new class, call it TestValidation.java

Now paste the code in as such:

package com.example.myawesomeandroidapp.test;

import android.test.AndroidTestCase;

public class TestValidation extends AndroidTestCase {

    protected void setUp() throws Exception {
        super.setUp();
    }
    
}


Note the use of extends AndroidTestCase. This is very important and allows us to test Android projects.

Now add the unit tests, again this is a very basic example and you should make this more complicated as neccessary to suit your project and expectations.

    public void testValidation(){
        assertTrue(MainActivity.validateIsMonth(2));
    }
    
    public void testValidationFail(){
        assertFalse(MainActivity.validateIsMonth(13));
    }

You'll see we are testing our validateIsMonth method in the MainActivity, we're testing a pass case and a fail case. Again basic basic unit tests here but hopefully you get the idea.

If you right click on your project and click Run as -> Android JUnit Test your project should run and Eclipse should switch to JUnit view and show you a beautiful column of green lights indicating your unit tests have all passed :)



Hope this helps and happy testing.

Oh and jokes aside Adam Cameron has done some really awesome work on CFML unit testing, check out his blog for some great examples: http://cfmlblog.adamcameron.me/search/label/Unit%20Testing



25 January 2014

Google Cloud Messaging with Android and ColdFusion backend


Google Cloud Messaging (GCM) is an awesome little service. It enabled "push" messages to be sent from Google to a specific Android phone and picked up by your application. The scope for these messages is massive but the intent is for the content to be fairly small. A great example (and a great name) is a tickle. This is very small message intended to instruct the application to go and perform an action.

For example, say your app needs data to be kept in sync with your server. This could be done by polling, but polling is resource intensive and sucks up precious bandwidth. A far better idea would be for your server to send out a message whenever the necessary data is updated and tell your app to come and re-fetch the data when possible. The message sent to your app is called a tickle!

I've created a simple tutorial to briefly describe GCM and show an example. My example does have a lot of moving parts to it but hopefully is simple enough to get the basic idea. For my backend server I'm using a CFML server which keeps track of any device registering for messages and allows me to send a message out too that device.

The sample app uses three components:

  1. GCM Connection Server - A Google Cloud project to send the message from you to the device.
  2. Application Server - Your own personal server that tracks device ids and issues messages. This can be cfml, php aspx, whatever, but it needs the capacity to store data, preferably in a db.
  3. Your Android app - The app you write and distribute as necessary.

Step 1 - The GCM Server

OK First we need to setup our Google Cloud Messaging service. To do this we log onto Google's cloud messaging panel and configure a new project. This project will now sit and wait for instructions and when told will relay messages from our server to our elected android device.

To set this up, follow the instructions here: http://developer.android.com/google/gcm/gs.html

Step 2 - The Application Server

As mentioned for this I'm using CFML here with a mysql db. I've not spent much time on this so it doesn't look pretty but I just wanted to illustrate the point. First the db, I've used one simple table:

CREATE TABLE gcmtest{
 intID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 strName varchar(100) not null,
 registrationid varchar(200) not null
}

That's it for db work! We have an ID column, a human readable name and a registration id which will contain the device id which is registered with Google.

Now the application server needs two files. One to respond to incoming registration requests and one to send messages, first the registration responder. Now a word of warning, this isn't production worthy, it's just a test bed. You should sanitise and protect your data much more thoroughly. As you can see it takes two values from the URL and inserts them into the db.

receive.cfm:
<cfif structKeyExists(url, "registrationid") AND structKeyExists(url, "name")>

    <cfquery name="qInsertReg" datasource="local">
        insert into gcmtest 
        (
            registrationid,
            strname
        )
        values
        (
            <cfqueryparam cfsqltype="CF_SQL_VARCHAR"    value="#url.registrationid#" />,
            <cfqueryparam cfsqltype="CF_SQL_VARCHAR"    value="#url.name#" />
        )
    </cfquery>

    <cfoutput>Done.</cfoutput>

</cfif>

Next you need the page which will send out messages, send.cfm:


<cfif structKeyExists(url, "regid")>

    <cfif structKeyExists(url, "strmsg")>
        <cfset strmymsg = url.strmsg />
    <cfelse>
        <cfset strmymsg = "all your base are belong to me" />
    </cfif>

    <cfset stFields = { "registration_ids": [ "#url.regid#" ], "data": {msg: "#strmymsg#"} }>
    
    <cfhttp url="https://android.googleapis.com/gcm/send" method="post" result="httpResp" timeout="60">
        <cfhttpparam type="header" name="Content-Type" value="application/json" />
        <cfhttpparam type="header" name="Authorization" value="key=***yourkeyfromgoogle***" />
        <cfhttpparam type="body" value="#serializeJSON(stFields)#">
    </cfhttp>
    
        
    <cfif httpResp.status_code eq 200>
        <span style="color:green;font-weight:bold;">Message Sent</span><br />
    </cfif>
</cfif>


<cfquery name="qGet" datasource="local">
    select * from gcmtest
</cfquery>

<form action="send.cfm" method="GET">
    <strong>Select Recipient:</strong>
    <br />
    
    <cfoutput query="qGet">
        <input type="radio" name="regid" value="#registrationid#">#strname#<br>
    </cfoutput>
    <br />
    
    <strong>Message:</strong><br />
    <input name="strmsg" type="text" /><br /><br />
    <input type="submit" value="submit" name="submit">
</form>

As you can see, we get everything from the gcmtest table and output it with radio buttons and a message box. When you hit send it self posts and the cfhttp takes over. For the cfhttp we use a json struct containing the registration id and the message. This we pass to the url https://android.googleapis.com/gcm/send obviously not forgetting to pass along our authorization key we created in step one.

That's it for the server side!

Step 3 - Android In Action

Now we need to have our Android app a) register with and b) receive messages from CGM. Now there is a lot of code here, so I'll post the project on gitHub but I'm hoping to cover the basics. There are obviously loads of things you can do with this message but for now I'm just going to use the basic Google demo method of posting a notification. To do this we register a WakefulBroadcastReceiver which will keep the device alive in case it hears a broadcast message. Then it will fire an intentService.

a) Manifest.xml
You need these permissions:

    <!-- GCM connects to Google Services. -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <permission android:name="com.google.android.gcm.demo.app.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.google.android.gcm.demo.app.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

You also need to register the services:


<receiver
      android:name=".GcmBroadcastReceiver"
      android:permission="com.google.android.c2dm.permission.SEND" >
          <intent-filter>
               <!-- Receives the actual messages. -->
               <action android:name="com.google.android.c2dm.intent.RECEIVE" />
               <category android:name="com.google.android.gcm.demo.app" />
          </intent-filter>
</receiver>
<service android:name=".GcmIntentService" />

Then what we're going to do in ActivityMain is check for the existence of a stored preference for the users name. If found we'll start a Fragment to welcome the user, if not we'll ask the user for their name.


FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
String strName = getNameFromPrefs();

if(strName.length() > 0){
 Bundle bundle = new Bundle();
 bundle.putString(FragmentWelcome.TAG_NAME, strName);
 FragmentWelcome frag = new FragmentWelcome();
 frag.setArguments(bundle);
 ft.replace(R.id.activity_default_fragment_container, frag, FragmentWelcome.class.getSimpleName());
}else{
 ft.replace(R.id.activity_default_fragment_container, new FragmentName(), FragmentName.class.getSimpleName());
}

ft.commit();

Now FragmentName is really simple and I shall spare you the details. It is a simple layout with an input text box. It checks for internet and validates the input and if so it calls a listener which returns to the main activity. The Activity saves the user's name in the savedPreferences and opens FragmentWelcome.

FragmentWelcome covers the following steps:
  • Get user's name via intent
  • verify GooglePlayServices
  • Gets your GCM Sender ID (created in step 1). Store this in strings.xml or somewhere sensible.
  • Using your GCM Sender ID it posts to Google Play Services and gets your device id.
        private void registerInBackground() {
            new AsyncTask<String, Void, String>() {
                @Override
                protected String doInBackground(String... params) {
                    String msg = "";
                    String name = "";
                    try {
                        if (gcm == null) {
                            gcm = GoogleCloudMessaging.getInstance(context);
                        }
                        regid = gcm.register(SENDER_ID);
    

  • Gets the URL of the receieve.cfm file we created in step 2 (again store it in strings or prefs) and appends the user's name and the device id to the url.
  • Call an AsyncTask to open DefaultHttpClient and hit the url we have now created.
        private class UploadRegistrationID extends AsyncTask<String, Void, String> {
            @Override
            protected String doInBackground(String... urls) {
                String response = "";
                for (String url : urls) {
                    DefaultHttpClient client = new DefaultHttpClient();
                    HttpGet httpGet = new HttpGet(url);
                    try {
                        HttpResponse execute = client.execute(httpGet);
                        InputStream content = execute.getEntity().getContent();
        
                        BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
                        String s = "ServerResponse:";
                        while ((s = buffer.readLine()) != null) {
                            response += s;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return response;
            }
    

That's it. Once you've run your Android app and entered your name you should be able to go to your send.cfm file in a web browser and send a message through to your device. The message will then pop up in your notification window!

Here's the Android project on github:
https://github.com/jimbo1299/gcmPushDemo

Magic ;)






15 January 2014

Noisy Maps is dead...long live Noisy Maps!

Some time ago I started a project called Noisy Maps. The idea being an easy way to find nearby utilities like cash points, mail boxes and phone boxes. This project quickly became a labour of love and something that at times was really challenging. I've learnt a lot about linux, apache tomcat, railo and coldfusion. Not to mention some awesome sql procs for finding nearby things.

However the time has come when the project has out-grown me....plus I can't afford to keep hosting it ;)

So I've decided to open source it and launch it on github.

If you feel you can contribute or indeed host it, go for it. Good luck to you:

https://github.com/jimbo1299/noisymaps

11 January 2014

The death of noisypix.com

Some time ago I noticed every one of my (technologically challenged) parent's photos were taken at around eight million gigabytes. Backing them up was becoming a pain and taking up a huge amount of space! So I created noisypix, a simple website to upload photos and images too and be provided in return with a smaller shrunk down file which was more reasonable to maintain.

The core of the project was Coldfusion and I already knew photo manipulation was quite simple using
<cfimage>
The exciting part of the project for me was going to be some HTML 5 goodness tucked in with some bootstrap, jquery and jquery filedrop.

Sadly time has passed and I've moved on to other things. Noisypix sits on a linux box with a copy of railo making it all tick. The problem being it costs me money! Not a huge amount but some, and I'm trying to cut back. Plus by the looks of Google Analytics, hardly anyone uses it!

So I've come to the painful decision to kill my linux box and let noisypix out into the wild. I'm going to open source it, post it on github and see what the world does with it. I doubt much will happen but I hope someone one day will learn something from it.

So sometime in February I'll be pulling the plug. If you'd like to check out my code and see what you think, or perhaps even improve and host it yourself, you're most welcome too.

https://github.com/jimbo1299/noisypix