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!