Due: Thursday May 7, end of day.
Submit to: http://www.crowdgrader.org/crowdgrader/venues/view_venue/1077
This assignment builds on assignment 2.
In assignment 2, you could post and retrieve messages from anyone close to you. We are going to do it one step better now: not only you can chat anonymously and publicly with everybody at a location, but you can then engage in private conversations with any one of the people close to you.
The app will have two activities. The main activity lets you do public, anonymous chat. If you select any message, you can then engage in private chat with the author of that message.
The application should consist of two activities. The MainActivity behaves as in Assignment 2: it shows all non-private messages.
The format for display should be similar to the one below, or provide the same functionality.
In particular, the green dot indicates whether you are having at the same time a private conversation with the person who authored the message above.
When you click on a message by yourself, nothing happens.
When you click on a message by someone else, you go to another activity, say, ChatActivity, in which you can chat with the author of that message.
In the ChatActivity, you should have a POST and a Refresh button. The POST button posts your message, private to the other person. The Refresh button refreshes the list of messages from the other person.
When you click on the back button, you should go back to MainActivity.
First of all, you have to create a userid string for yourself. You will store the userid in the AppInfo class, so you will have it accessible from all activities.
Here is how to create an AppInfo class that automatically initializes the userid if you don't have one already:
public class AppInfo {
public static final String PREF_USERID = "userid";
private static AppInfo instance = null;
protected AppInfo() {
// Exists only to defeat instantiation.
}
// Here are some values we want to keep global.
public String userid;
public static AppInfo getInstance(Context context) {
if (instance == null) {
instance = new AppInfo();
// Creates a userid, if I don't have one.
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
instance.userid = settings.getString(PREF_USERID, null);
if (instance.userid == null) {
// We need to create a userid.
SecureRandom random = new SecureRandom();
instance.userid = new BigInteger(130, random).toString(32);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_USERID, instance.userid);
editor.commit();
}
}
return instance;
}
}
In onCreate, you can then do:
appInfo = AppInfo.getInstance(this);
to obtain the singleton object.
We are using a slightly modified version of our API, because now we need to distinguish public and private messages.
You have the following two methods.
Parameters:
appInfo.userid
where appInfo
is the object. Return:
The function returns a list of either the latest public messages (if you posted a public message yourself), or a list of private messages between you and the other person (if you posted a private message). In the json, this is the meaning of the fields:
Examples
'peter' sends a public message:
https://hw3n-dot-luca-teaching.appspot.com/store/default/put_local?lat=100&lng=100&userid=peter&msg=conchesalsa&msgid=msg3
{"messages": [
{"dest": "public", "msgid": "msg3", "userid": "peter", "ts": "2015-05-01T18:01:39.812310", "conversation": false, "msg": "conchesalsa"},
{"dest": "public", "msgid": "msg3", "userid": "peter", "ts": "2015-04-27T00:14:21.123400", "conversation": false, "msg": "conchesalsa"},
{"dest": "public", "msgid": "msg3", "userid": "peter", "ts": "2015-04-27T00:08:59.781850", "conversation": false, "msg": "conchesalsa"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:50:42.016950", "conversation": false, "msg": "pasta"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:47:17.823790", "conversation": false, "msg": "pasta"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:31:17.082770", "conversation": false, "msg": "pasta"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:18:00.906200", "conversation": false, "msg": "pasta"}]}
'luca' sends a private message to 'peter':
https://hw3n-dot-luca-teaching.appspot.com/store/default/put_local?lat=100&lng=100&userid=luca&dest=peter&msg=conchesalsa&msgid=msg5
{"messages": [
{"conversation": true, "dest": "peter", "msgid": "msg5", "userid": "luca", "ts": "2015-05-01T18:03:47.304780", "msg": "conchesalsa"},
{"conversation": true, "dest": "peter", "msgid": "msg5", "userid": "luca", "ts": "2015-04-27T00:26:42.050870", "msg": "conchesalsa"},
{"conversation": true, "dest": "luca", "msgid": "msg2", "userid": "peter", "ts": "2015-04-27T00:08:19.587070", "msg": "mivabene"},
{"conversation": true, "dest": "luca", "msgid": "msg2", "userid": "peter", "ts": "2015-04-26T23:56:43.865970", "msg": "mivabene"},
{"conversation": true, "dest": "luca", "msgid": "msg2", "userid": "peter", "ts": "2015-04-26T23:53:11.291820", "msg": "mivabene"},
{"conversation": true, "dest": "peter", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:52:38.359530", "msg": "pasta"},
{"conversation": true, "dest": "peter", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:51:12.401750", "msg": "pasta"}]}
Note how all the messages above are between luca and peter.
'peter' sends a private message to 'luca':
https://hw3n-dot-luca-teaching.appspot.com/store/default/put_local?lat=100&lng=100&userid=peter&dest=luca&msg=tomato&msgid=msg5
{"messages": [
{"conversation": true, "dest": "luca", "msgid": "msg5", "userid": "peter", "ts": "2015-05-01T18:07:50.498160", "msg": "tomato"},
{"conversation": true, "dest": "peter", "msgid": "msg5", "userid": "luca", "ts": "2015-05-01T18:03:47.304780", "msg": "conchesalsa"},
{"conversation": true, "dest": "peter", "msgid": "msg5", "userid": "luca", "ts": "2015-04-27T00:26:42.050870", "msg": "conchesalsa"},
{"conversation": true, "dest": "luca", "msgid": "msg2", "userid": "peter", "ts": "2015-04-27T00:08:19.587070", "msg": "mivabene"},
{"conversation": true, "dest": "luca", "msgid": "msg2", "userid": "peter", "ts": "2015-04-26T23:56:43.865970", "msg": "mivabene"},
{"conversation": true, "dest": "luca", "msgid": "msg2", "userid": "peter", "ts": "2015-04-26T23:53:11.291820", "msg": "mivabene"},
{"conversation": true, "dest": "peter", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:52:38.359530", "msg": "pasta"},
{"conversation": true, "dest": "peter", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:51:12.401750", "msg": "pasta"}]}
Parameters:
appInfo.userid
where appInfo
is the object. Return:
The return Json is identical to put_local.
Examples
The user with userid 'luca' gets a list of public messages, after the exchanges above:
https://hw3n-dot-luca-teaching.appspot.com/store/default/get_local?lat=100&lng=100&userid=luca
{"messages": [
{"dest": "public", "msgid": "msg3", "userid": "peter", "ts": "2015-05-01T18:01:39.812310", "conversation": true, "msg": "conchesalsa"},
{"dest": "public", "msgid": "msg3", "userid": "peter", "ts": "2015-04-27T00:14:21.123400", "conversation": true, "msg": "conchesalsa"},
{"dest": "public", "msgid": "msg3", "userid": "peter", "ts": "2015-04-27T00:08:59.781850", "conversation": true, "msg": "conchesalsa"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:50:42.016950", "conversation": false, "msg": "pasta"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:47:17.823790", "conversation": false, "msg": "pasta"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:31:17.082770", "conversation": false, "msg": "pasta"},
{"dest": "public", "msgid": "msg1", "userid": "luca", "ts": "2015-04-26T23:18:00.906200", "conversation": false, "msg": "pasta"}]}
Implementation suggestions
This assignment is not very different from the previous one. Here are some suggestions.
First, there is no point in keeping the list of messages in appInfo, since they are local to each of the two activities.
Decoding the Json
To decode the Json, you will need (as before) two classes. One class will contain only the field messages. The other class, call it Message, contains all the fields of a message. You can declare these classes like this:
public class Message {
public Message() {};
public String msg;
public String userid;
public String dest;
public String ts;
public String msgid;
public Boolean conversation;
}
public class CallResult {
public CallResult() {};
Message[] messages;
}
The ListView adapter
When you get a list of messages back, you have a list of objects of type Message. You can then define your list adaptor in order to display the messages as an adaptor that takes a list of Messages, as follows:
private ArrayList<Message> aList;
private class MyAdapter extends ArrayAdapter<Message> {
int resource;
Context context;
public MyAdapter(Context _context, int _resource, List<Message> items) {
super(_context, _resource, items);
resource = _resource;
context = _context;
this.context = _context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) { ... }
}
That is, you can use the array of Messages that you get from the server call directly as the array that you adapt to the ListView.
Going from one activity to the other
When you go from one activity to the other, the only thing you need to pass along with the intent is the id of the person with whom you want to have a private conversation. Pass it as follows (assuming that id is in the string variable dest):
Intent intent = new
Intent(MainActivity.this, ChatActivity.class);intent.putExtra("dest", dest);startActivity(intent);
Note that if you do this from the list adaptor, the last line should really be:
context.startActivity(intent);
If you are curious, you can find the server code at: https://bitbucket.org/luca_de_alfaro/backingstore/ and go to the hw3 branch.
The relevant code is in controllers/default.py.
Unlike previous codes, this is not well written at all; it spends too much time checking whether there are private messages associated with public ones. But, for the moment it will do. Then we will discuss in class how to improve the app.
As before, you can view the latest message by going to: