Android and Google App Engine – Build a simple message board app

Right. I have been building apps for sometime now and I realize I haven’t posted anything related in my blog! So this is my first tutorial based on Android and GAE, and really hope to put some more posts on other super cool technologies I have been learning. So this can be part 1 to a series of posts.

Today I’m gonna show how you can build a simple Android app that can connect to the server hosted on Google’s servers through Google App Engine(GAE). I will focus more on Android here and the connectivity will be done via localhost. It’s pretty easy to build sites on app engine and they have exhaustive tutorials for that.

Anyways the application we are going to build today is a Message board, that is anyone with some username can post a message to the public. Imagine that to be a simplified version of the Twitter. I’m not adding any authentication here (Might come up in future posts) and in trying to keep it simple I have not included exception handling and checks, which btw are very very important. Possibly I will update this post with these additions. Plus I deeply appreciate anyone who can suggest on how to improve this tutorial.

What you would be needing today on your machine is Eclipse with Android ADT plugin and Android SDK in the machine, the Google App Engine Launcher and well a text editor to edit the python and other files in the app engine application. So let’s begin!

Step 1 – Build the app engine application:

Today we are going to build a simple REST based application. If you are using Google App Engine Launcher, then you can create a new application from it’s options and automatically builds you the app.yaml . 

Edit the main.py. Create two models – Message and User.

class User(db.Model):
	# username
	username = db.StringProperty(required=True)
	
	def toDict(self):
		user = {}
		user["username"] = self.username
		return user

class Message(db.Model):
	# Message Content
	content = db.TextProperty(required=True)
	
	# Date Created
	created = db.DateTimeProperty(auto_now_add=True)
	
	# Owner
	owner = db.ReferenceProperty(User,collection_name='messages')
	
	def toDict(self):
		message = {}
		message["content"] = self.content
		message["date_created"] = convert_date(self.created)
		message["owner"] = self.owner.username
		return message

Now create REST handlers for these models.

class UserHandler(webapp2.RequestHandler):
	def post(self, id):
		#logging.info(self.request.get('username'))
		data = self.request.get('username')#json.loads(self.request.body)
		user = User.all().filter('username',data).get()
		if user == None:
			user = User(username=data)
			user.put()
		return self.response.out.write(json.dumps(user.toDict()))
		

class MessageHandler(webapp2.RequestHandler):
	def post(self, id):
		try:
			data = json.loads(self.request.body)
		except ValueError:
			data = {}
			request = self.request
			data['username'] = request.get('username')
			data['content'] = request.get('content')
		except:
			return self.error('403')
		user = User.all().filter('username',data['username']).get()
		logging.info(data['username'])
		if user:
			message = Message(content=data['content'],owner=user)
			message.put()
			return self.response.out.write(json.dumps(message.toDict()))
		return self.error('403')
	
	def get(self,id):
		if id != '':
			message = Message.get_by_id(int(id))
			if message:
				return self.response.out.write(json.dumps(message.toDict()))
			return self.error(403)
		try:
			data = json.loads(self.request.body)
			messages = User.all().filter('username',data['username']).get().messages
		except:
			messages = Message.all()
		messages_to_send = []
		for message in messages:
			messages_to_send.append(message.toDict())
		return self.response.out.write(json.dumps(messages_to_send))
	
	def put(self,id):
		if id != '':
			data = json.loads(self.request.body)
			message = Message.get_by_id(int(id))
			if message:
				message.content = data['content']
				message.put()
				return self.response.out.write(json.dumps(message.toDict()))
		return self.error(403)
	
	def delete(self,id):
		if id != '':
			message = Message.get_by_id(int(id))
			message.delete()

What’s left here is the main handler and if you had like, a test handler with a test html that has forms to test the app handlers.

Once done, check the app out using the preview option and see if everything works fine.

We have a working application, now we move on to android.

Step 2 – Build Android app:

We will have an Android app that will have a ListView to display all the messages and a button which will lead the user to create a message.

First the Main Activity will have a ListView that is populated by the messages from the app engine app you have just created. It also has a button that sends an intent to another activity that creates a new message. I called this app Post a Message that you can find on my github.

public class PaM_Main extends ListActivity {
	Handler handler;
	
	
	private OnClickListener postMessageListener = new OnClickListener() {
		
		public void onClick(View v) {
			Intent postMessageIntent = new Intent(PaM_Main.this, PaM_New_Post.class);
			startActivity(postMessageIntent);
		}
	};
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pam_main);
        
        handler = new Handler();
        Button postButton = (Button) findViewById(R.id.post_button);
        postButton.setOnClickListener(postMessageListener);
    }
    
    @Override
    protected void onResume() {
    	super.onResume();
    	new GetMessages().execute("");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.pam_main, menu);
        return true;
    }

    class GetMessages extends AsyncTask<String, Void, String> {

		@Override
		protected String doInBackground(String... params) {
			String uri = "http://10.0.2.2:8090/messages";
			HttpClient httpClient = new DefaultHttpClient();
			HttpGet httpGet = new HttpGet(uri);
			//Log.d("loltale:params", Integer.toString(params.length));
			
			try {
				HttpResponse response = httpClient.execute(httpGet);
				HttpEntity entity = response.getEntity();
				
				if(entity != null) {
					return EntityUtils.toString(entity);
				}
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return null;
		}
		
		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			Log.d("PaM:result:", result);
			try {
				JSONArray messages = new JSONArray(result);
				ArrayList<PaM_Message> list = new ArrayList<PaM_Message>();
				if (messages != null) { 
				   int len = messages.length();
				   for (int i=0;i<len;i++) { 
				    list.add(new PaM_Message(messages.get(i).toString()));
				   } 
				}
				
				final ArrayList<PaM_Message> mList = list;
				handler.post(new Runnable() {
					
					public void run() {
						setListAdapter(new MessagesAdapter(getApplicationContext(), mList));
					}
				});
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    	
    }
    
    class MessagesAdapter extends ArrayAdapter<PaM_Message> {
    	private List<PaM_Message> messages;
    	
    	public MessagesAdapter(Context context, List<PaM_Message> messages) {
    		super(context, android.R.layout.simple_list_item_2, messages);
    		this.messages = messages;
    	}
    	
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		TwoLineListItem view;
    		
    		if(convertView == null) {
    			LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = (TwoLineListItem)inflater.inflate(android.R.layout.simple_list_item_2, null);
    		}
    		else {
    			view = (TwoLineListItem)convertView;
    		}
    		PaM_Message data = messages.get(position);
            view.getText1().setText(data.getMessage());
            view.getText2().setText(data.getUser());
    		return view;
    	}
    }
} 

The New Message Activity will have two edit boxes and a button which will create a new message. Also note that I am creating a user of the username first and then post the message.

public class PaM_New_Post extends Activity {
	EditText contentEdit, usernameEdit;
	
	private OnClickListener postMessageListener = new OnClickListener() {
		
		public void onClick(View v) {
			String content = contentEdit.getText().toString();
			String username = usernameEdit.getText().toString();
			Log.d("PaM:content:username:",content + ":" + username);
			if(!content.equals("") && !username.equals("")) {
				String user_result = new PaM_API().postUser(username);
				try {
					JSONObject message_result = new JSONObject( new PaM_API().postMessage(content, username) );
					if ( message_result.getString("content").equals(content) ) {
						Toast.makeText(getApplicationContext(), "Message Successfully posted!", Toast.LENGTH_LONG).show();
						contentEdit.setText("");
						usernameEdit.setText("");
					}
					else {
						Toast.makeText(getApplicationContext(), "Something wrong... 😦 ", Toast.LENGTH_LONG).show();
					}
				} catch (JSONException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			else {
				Toast.makeText(getApplicationContext(), "Some fields missing", Toast.LENGTH_LONG).show();
			}
		}
	};
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.new_post);
        
        contentEdit = (EditText)findViewById(R.id.messageContentEdit);
        usernameEdit = (EditText)findViewById(R.id.usernameEdit);
        ((Button) findViewById(R.id.postNewMessageButton)).setOnClickListener(postMessageListener);
    }
}

Few things to note, if you are testing on your system, you need to put http://10.0.2.2:/ to the uri the android app will connect to the GAE app. And do remember to change the url when the app is deployed to google app engine.

That’s it! You can run the android app and check it out. Plus if you deploy you GAE app, then anyone can access your app and post on the message board! The code’s on github

Hope this post was helpful. In the next series of posts, I will try to show a little more complex apps plus use some awesome frameworks like Backbone.js, Knockout.js, Node.js and many more to make some really cool apps!

Note: There will be a lot of revisions to this. This is my first attempt in writing a tutorial, so will be glad to know I can make this post better. Suggestions welcome!

Advertisements

8 comments

  1. Почему нужно обращаться именно к Voronkin Studio?

    – мы соблюдаем все сроки перед нашими клиентами;
    – у нас индивидуальный подход к каждому клиенту;
    – наши клиенты всегда остаются довольными;
    – при заказе у нас проекта, мы даем Вам год обслуживания;
    – наши цены никогда не смущали наших заказчиков;

    Что мы можем сделать, если Вам нужен большой проект в кратчайшие сроки?

    – благодаря развитию интернет технологий, мы имеем множество решений для реализации проектов практически любой сложности. Мы прибегаем к помощи PHP Frameworks: CodeIgniter, Kohana;

    – CMS движки: WordPress, Joomla;

    Что нужно для начала создания Вашего проекта?

    – отправить нам техническое задание проекта;
    – согласовать стоимость проекта;
    – оплатить 50% стоимости проекта.

    Занимаемся ли мы раскруткой сайтов?

    – мы занимаемся регистрацией сайтов на досках объявлений, форумах, блогах;
    – мы занимаемся СЕО оптимизацией сайтов под поисковые машины;
    – мы занимаемся различного рода рекламными рассылками.

    http://voronkin.com

    моб. 0637248836
    skype: argonaft95
    icq: 554840


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s