Monday, July 16, 2012

Android - Steps to run GCM Demo


I would insist to first go through Android Docs and then try to implement using below steps.

Make sure you have installed Apache Tomcat and Apache Ant.

Steps:-

1.) Make sure you have dowloaded support library from SDK Manager -> "Google Cloud Messaging             for Android Library".
2.) This will add "gcm" folder with demo application in your SDK folder.
3.) So go to "your_sdk\extras\google\gcm\samples\gcm-demo-server\WebContent\WEB-INF\classes\api.key". And here enter your API-KEY that you got from registering to
    Google Console.

4.)
  •     Now you have to set System variables for Ant upto its "bin". So, edit Path and append it at the end using a semi-colon - "your_path\apache-ant-1.8.4\bin".
  •     Now set the user variable for "JAVA_HOME" - "C:\Program Files\Java\jdk1.6.0_23"(without "bin") and Ant upto its "bin" folder. Go to cmd and type ant.


Buildfile: build.xml does not exist!
Build failed

If you get above error means your Ant is working fine.

5.) Now go to "your_sdk\extras\google\gcm\samples\gcm-demo-server" and type ant.

If successful you will see output like,


init:
    [mkdir] Created dir: your_sdk\extras\google\gcm\samples\gcm-demo-
server\build\classes
    [mkdir] Created dir: your_sdk\extras\google\gcm\samples\gcm-demo-
server\dist

compile:
    [javac] Compiling 7 source files to your_sdk\extras\google\gcm\sa
mples\gcm-demo-server\build\classes

war:
      [war] Building war: your_sdk\extras\google\gcm\samples\gcm-demo
-server\dist\gcm-demo.war

dist:

BUILD SUCCESSFUL
Total time: 0 seconds


Also you will see a "dist" folder created with "gcm-demo.war" file.

6.) Now start your apache tomcat server and check that its working fine by running it using "http://your_host_ip:8080/"
7.) If its working fine go to "C:\Program Files\Apache Software Foundation\Tomcat 7.0\webapps\copy gcm-demo.war" here.
8.) Restart your Server and open the gcm-demo in browser - "http://your_host_ip:8080/gcm-demo/"

You will see result as,

Unregistered device #0
No devices registered!

So, server side is completed now.


9.) Now, import "gcm-demo-client" Application from "your_sdk\extras\google\gcm\samples\gcm-demo-client"
10.) Open "CommonUtilities.java" - here you need to give
SERVER_URL="http://your_host_ip/gcm-demo/" and
SENDER_ID="sender id you got from google console url".
11.) Build "gcm-demo-client" into your Android Device to register to your Server

Succesfully sent message to device #0
1 device(s) registered!

You will see above output if successfully registered will a send button.

12.) Press Send Button to get Notification on your device.

You can find the sorce demo from github, it also include the gcm-demo.war file that you have to deploy. The things that are required to be change are,

1.) API Key,
2.) SENDER_ID
3.) SERVER_URL

Thanks :)

Monday, July 2, 2012

Section Adapter

I found an interested post about SectionAdapter by Jeff Sharkey. I will insist that it is the better way of creating SectionAdapter in the way that Jeff has opted here in his blog.

The blog just says to use seperate Adapter for every Header, so that we can customize it easily in the best possible way. The idea is very much clear and interesting. So, we can customize it and create as required by us. Below is how we can customize the SectionAdapter.

SectionedAdapter.java

abstract public class SectionedAdapter extends BaseAdapter {

    
    String TAG = getClass().getSimpleName();
    
    abstract protected View getHeaderView(String caption, int index, View convertView, ViewGroup parent);
    
    private List<Section> sections = new ArrayList<Section>();
    private static int TYPE_SECTION_HEADER = 0;
    
    public SectionedAdapter() {
        super();
        sections.clear();
    }
    
    public void addSection(String caption, Adapter adapter) {
        sections.add(new Section(caption, adapter));
    }
    
    public void clear() {
        sections.clear();
        notifyDataSetChanged();
    }
    
    public Object getItem(int position) {
        for (Section section : this.sections) {
            if (position == 0) {
                return (section);
            }
            
            int size = section.adapter.getCount() + 1;
            
            if (position < size) {
                return (section.adapter.getItem(position - 1));
            }
            position -= size;
        }
        return (null);
    }
    
    public int getCount() {
        int total = 0;
        
        for (Section section : this.sections) {
            total += section.adapter.getCount() + 1; // add one for header
        }
        return (total);
    }
    
    public int getViewTypeCount() {
        int total = 1; // one for the header, plus those from sections
        
        for (Section section : this.sections) {
            total += section.adapter.getViewTypeCount();
        }
        return (total);
    }
    
    public int getItemViewType(int position) {
        int typeOffset = TYPE_SECTION_HEADER + 1; // start counting from here
        
        for (Section section : this.sections) {
            if (position == 0) {
                return (TYPE_SECTION_HEADER);
            }
            
            int size = section.adapter.getCount() + 1;
            
            if (position < size) {
                int value = (typeOffset + section.adapter
                .getItemViewType(position - 1));
                return value;
            }
            
            position -= size;
            typeOffset += section.adapter.getViewTypeCount();
        }
        return (-1);
    }
    
    public boolean areAllItemsSelectable() {
        return (false);
    }
    
    public boolean isEnabled(int position) {
        return (getItemViewType(position) != TYPE_SECTION_HEADER);
    }
    
    public View getView(int position, View convertView, ViewGroup parent) {
        int sectionIndex = 0;
        
        for (Section section : this.sections) {
            if (position == 0) {
                return (getHeaderView(section.caption, sectionIndex, convertView, parent));
            }
            
            int size = section.adapter.getCount() + 1;
            
            if (position < size) {
                return (section.adapter.getView(position - 1, convertView, parent));
            }
            
            position -= size;
            sectionIndex++;
        }
        return (null);
    }
    
    public long getItemId(int position) {
        return (position);
    }
    
    class Section {
        String caption = null;
        Adapter adapter = null;
        
        Section(String caption, Adapter adapter) {
            this.caption = caption;
            this.adapter = adapter;
        }
    }
}

Above class is abstract and having an abstract method 
getHeaderView(String caption, int index, View convertView, ViewGroup parent) that is because we can easily customize the Header Section also dynamically from our Activity. In Above class we have an inner class Section that does the work of maintaining every Header having its own Adapter. So, the class is nicely set-up to be used.
So, now we will create an Activity having a ListView,


MainActivity.java

public class MainActivity extends Activity {

    
    ListView mListView;
    ArrayList<String> mArrayList = new ArrayList<String>();
    SectionedAdapter adapter;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mListView = (ListView) findViewById(R.id.listview);
        
        adapter = new SectionedAdapter() {
            
            @Override
            protected View getHeaderView(String caption, int index,
            View convertView, ViewGroup parent) {
                convertView = getLayoutInflater().inflate(R.layout.section_header, null);
                TextView header = (TextView) convertView.findViewById(R.id.header);
                header.setText(caption);
                return convertView;
            }
        };
        
        for (int i = 0; i < 5; i++) {
            mArrayList.add("Item " + i);
            MyAdapter myAdapter = new MyAdapter();
            adapter.addSection("Header " + i, myAdapter);
        }
        mListView.setOnItemClickListener(new OnItemClickListener() {
            
            public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
                Toast.makeText(getApplicationContext(), arg0.getAdapter().getItem(position).toString(), Toast.LENGTH_LONG).show();
            }
        });
        mListView.setAdapter(adapter);
    }
    
    class MyAdapter extends BaseAdapter {
        
        public int getCount() {
            return mArrayList.size();
        }
        
        public Object getItem(int position) {
            return mArrayList.get(position);
        }
        
        public long getItemId(int position) {
            return position;
        }
        
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = (TextView) getLayoutInflater().inflate(R.layout.section_item, null);
            TextView item = (TextView) convertView.findViewById(R.id.item);
            item.setText(mArrayList.get(position));
            return convertView;
        }
    }
}

So, what we have in the Activity class is an instance of 
SectionedAdapter adapter; class with override method getHeaderView(String caption, int index, View convertView, ViewGroup parent)  by which we can inflate any xml and create our own custom Header. Also I had created an inner class MyAdapter that extends BaseAdapter which will be used as an Adapter for each Header. To add an Seperator as Header we can use addSection(String Caption, Adapter adapter) method to add the Header caption and a seperate Adapter related to that Header. In this way you can create seperate Adapter for each Header and link to it. I am attaching Demo source code for the same. Enjoy!!!!