diff --git a/adb_usb.ini b/adb_usb.ini
new file mode 100644
index 00000000..a5d11bc5
--- /dev/null
+++ b/adb_usb.ini
@@ -0,0 +1,2 @@
+22b8
+04e8
\ No newline at end of file
diff --git a/etc/broadcast.svg b/etc/broadcast.svg
new file mode 100644
index 00000000..74d10a81
--- /dev/null
+++ b/etc/broadcast.svg
@@ -0,0 +1,226 @@
+
+
+
+
diff --git a/etc/central_transport.svg b/etc/central_transport.svg
new file mode 100644
index 00000000..90126514
--- /dev/null
+++ b/etc/central_transport.svg
@@ -0,0 +1,187 @@
+
+
+
+
diff --git a/etc/pki.png b/etc/pki.png
new file mode 100644
index 00000000..e575ac3c
Binary files /dev/null and b/etc/pki.png differ
diff --git a/etc/pki.svg b/etc/pki.svg
new file mode 100644
index 00000000..ae301f82
--- /dev/null
+++ b/etc/pki.svg
@@ -0,0 +1,207 @@
+
+
+
+
diff --git a/res/layout/handle_group_session.xml b/res/layout/handle_group_session.xml
index 4c95713b..9e1ab3f7 100644
--- a/res/layout/handle_group_session.xml
+++ b/res/layout/handle_group_session.xml
@@ -5,18 +5,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_gravity="center_horizontal"
- android:background="#FFFFFFFF"
>
-
-
-
diff --git a/res/layout/main.xml b/res/layout/main.xml
index 936f343b..55f89a9d 100644
--- a/res/layout/main.xml
+++ b/res/layout/main.xml
@@ -23,22 +23,6 @@
android:layout_height="fill_parent"
android:layout_weight="0.5"
android:padding="5dp" />
-
-
-
-
-
-
diff --git a/src/edu/stanford/mobisocial/dungbeetle/ContactsActivity.java b/src/edu/stanford/mobisocial/dungbeetle/ContactsActivity.java
index 191fb7e9..4b770b33 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/ContactsActivity.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/ContactsActivity.java
@@ -90,10 +90,12 @@ public void onItemClick(AdapterView> parent, View view, int position, long id)
public void onClick(DialogInterface dialog, int item) {
switch(item){
case 0:
- sendMessageToContact(c);
+ UIHelpers.sendMessageToContact(ContactsActivity.this,
+ Collections.singletonList(c));
break;
case 1:
- startApplicationWithContact(c);
+ UIHelpers.startApplicationWithContact(ContactsActivity.this,
+ Collections.singletonList(c));
break;
case 2:
UIHelpers.showGroupPicker(ContactsActivity.this, c);
@@ -105,92 +107,6 @@ public void onClick(DialogInterface dialog, int item) {
alert.show();
}
- private void sendMessageToContact(final Contact contact){
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
- alert.setMessage("Enter message:");
- final EditText input = new EditText(this);
- alert.setView(input);
- alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- DBHelper helper = new DBHelper(ContactsActivity.this);
- Helpers.sendIM(
- ContactsActivity.this,
- Collections.singletonList(contact),
- input.getText().toString());
- }
- });
- alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- }
- });
- alert.show();
- }
-
- private void startApplicationWithContact(final Contact contact){
- final PackageManager mgr = getPackageManager();
- Intent i = new Intent("android.intent.action.CONFIGURE");
- i.addCategory("android.intent.category.P2P");
- final List infos = mgr.queryBroadcastReceivers(i, 0);
- if(infos.size() > 0){
- ArrayList names = new ArrayList();
- for(ResolveInfo info : infos){
- names.add(info.loadLabel(mgr).toString());
- }
- final CharSequence[] items = names.toArray(new CharSequence[]{});
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Share application:");
- builder.setItems(items, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int item) {
- final ResolveInfo info = infos.get(item);
- Intent i = new Intent();
- i.setClassName(info.activityInfo.packageName,
- info.activityInfo.name);
- i.setAction("android.intent.action.CONFIGURE");
- i.addCategory("android.intent.category.P2P");
- BroadcastReceiver rec = new BroadcastReceiver(){
- public void onReceive(Context c, Intent i){
- Intent launch = new Intent();
- launch.setAction(Intent.ACTION_MAIN);
- launch.addCategory(Intent.CATEGORY_LAUNCHER);
- launch.setPackage(info.activityInfo.packageName);
- List resolved =
- mgr.queryIntentActivities(launch, 0);
- if (resolved.size() > 0) {
- ActivityInfo info = resolved.get(0).activityInfo;
- String arg = getResultData();
- launch.setComponent(new ComponentName(
- info.packageName,
- info.name));
- launch.putExtra("creator", true);
- launch.putExtra(
- "android.intent.extra.APPLICATION_ARGUMENT",
- arg);
- startActivity(launch);
- Helpers.sendApplicationInvite(
- ContactsActivity.this,
- Collections.singletonList(contact),
- info.packageName, arg);
- }
- else{
- Toast.makeText(getApplicationContext(),
- "Sorry, no response from applications.",
- Toast.LENGTH_SHORT).show();
- }
- }
- };
- sendOrderedBroadcast(i, null, rec, null, RESULT_OK, null, null);
- }
- });
- AlertDialog alert = builder.create();
- alert.show();
- }
- else{
- Toast.makeText(getApplicationContext(),
- "Sorry, couldn't find any compatible apps.",
- Toast.LENGTH_SHORT).show();
- }
- }
-
private class ContactListCursorAdapter extends CursorAdapter {
@@ -226,16 +142,26 @@ public boolean onCreateOptionsMenu(Menu menu){
return true;
}
+ private final static int SHARE_INFO = 0;
+ private final static int SET_EMAIL = 1;
+ private final static int FACEBOOK_BOOTSTRAP = 2;
+
+
public boolean onPreparePanel(int featureId, View view, Menu menu) {
menu.clear();
- menu.add(0, 0, 0, "Set email (debug)");
- menu.add(0, 1, 0, "Facebook Bootstrap");
+ menu.add(0, SHARE_INFO, 0, "Exchange info");
+ menu.add(0, SET_EMAIL, 0, "Set email (debug)");
+ menu.add(0, FACEBOOK_BOOTSTRAP, 0, "Facebook Bootstrap");
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
- case 0: {
+ case SHARE_INFO: {
+ ((DungBeetleActivity)getParent()).shareContactInfo();
+ return true;
+ }
+ case SET_EMAIL: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setMessage("Enter email:");
final EditText input = new EditText(this);
@@ -254,7 +180,7 @@ public void onClick(DialogInterface dialog, int whichButton) {
alert.show();
return true;
}
- case 1: {
+ case FACEBOOK_BOOTSTRAP: {
Intent intent = new Intent(this, FacebookInterfaceActivity.class);
startActivity(intent);
return true;
diff --git a/src/edu/stanford/mobisocial/dungbeetle/DBHelper.java b/src/edu/stanford/mobisocial/dungbeetle/DBHelper.java
index 4e21340e..aa163112 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/DBHelper.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/DBHelper.java
@@ -33,7 +33,7 @@
public class DBHelper extends SQLiteOpenHelper {
public static final String TAG = "DBHelper";
public static final String DB_NAME = "DUNG_HEAP";
- public static final int VERSION = 20;
+ public static final int VERSION = 21;
private final Context mContext;
public DBHelper(Context context) {
@@ -151,7 +151,8 @@ public void onCreate(SQLiteDatabase db) {
createTable(db, GroupMember.TABLE,
GroupMember._ID, "INTEGER PRIMARY KEY",
GroupMember.GROUP_ID, "INTEGER",
- GroupMember.CONTACT_ID, "INTEGER");
+ GroupMember.CONTACT_ID, "INTEGER",
+ GroupMember.GLOBAL_CONTACT_ID, "TEXT");
createIndex(db, "INDEX", "group_members_by_group_id", GroupMember.TABLE, GroupMember.GROUP_ID);
generateAndStorePersonalInfo(db);
@@ -276,7 +277,7 @@ long addToFeed(String appId, String feedName, String type, JSONObject json) {
}
- long addObjectByJson(long contactId, JSONObject json) {
+ long addObjectByJson(long contactId, JSONObject json){
try{
long seqId = json.optLong("sequenceId");
long timestamp = json.getLong("timestamp");
@@ -291,6 +292,7 @@ long addObjectByJson(long contactId, JSONObject json) {
cv.put(Object.SEQUENCE_ID, seqId);
cv.put(Object.JSON, json.toString());
cv.put(Object.TIMESTAMP, timestamp);
+ cv.put(Object.SENT, 1);
getWritableDatabase().insertOrThrow("objects", null, cv);
return seqId;
}
@@ -456,7 +458,7 @@ public Cursor queryDynamicGroups() {
return getReadableDatabase().query(
Group.TABLE,
null,
- Group.DYN_UPDATE_URI + " != NULL",
+ Group.DYN_UPDATE_URI + " is not NULL",
new String[]{ },
null,
null,
@@ -468,12 +470,13 @@ public void markObjectsAsSent(Collection ids) {
ContentValues cv = new ContentValues();
cv.put(Object.SENT, 1);
getWritableDatabase().update(
- Object.TABLE, cv,
+ Object.TABLE,
+ cv,
Object._ID + " in (" + Util.joinLongs(ids,",") + ")",
null);
}
- public Cursor queryGroupsMembership(Long contactId) {
+ public Cursor queryGroupsMembership(long contactId) {
return getReadableDatabase().query(
GroupMember.TABLE,
new String[]{ GroupMember._ID, GroupMember.GROUP_ID },
@@ -484,12 +487,12 @@ public Cursor queryGroupsMembership(Long contactId) {
null);
}
- public Cursor queryGroupContacts(Long group_id) {
+ public Cursor queryGroupContacts(long groupId) {
return getReadableDatabase().rawQuery(
" SELECT C._id, C.name, C.public_key, C.person_id, C.email " +
" FROM contacts C, group_members G WHERE " +
"G.group_id = ? AND C._id = G.contact_id",
- new String[] { String.valueOf(group_id) });
+ new String[] { String.valueOf(groupId) });
}
public Maybe contactForPersonId(String personId){
diff --git a/src/edu/stanford/mobisocial/dungbeetle/DungBeetleActivity.java b/src/edu/stanford/mobisocial/dungbeetle/DungBeetleActivity.java
index b8b0d0ad..d61b0696 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/DungBeetleActivity.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/DungBeetleActivity.java
@@ -10,9 +10,7 @@
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.os.Bundle;
-import android.view.View.OnClickListener;
-import android.view.View;
-import android.widget.Button;
+import android.util.Log;
import android.widget.TabHost;
import android.widget.Toast;
import edu.stanford.mobisocial.dungbeetle.util.HTTPDownloadTextFileTask;
@@ -50,8 +48,7 @@ public void onPostExecute(String result) {
notifyApkDownload(AUTO_UPDATE_URL_BASE + "/" + AUTO_UPDATE_APK_FILE);
}
else if(pInfo.versionCode == versionCode){
- Toast.makeText(DungBeetleActivity.this,
- "Up to date.", Toast.LENGTH_SHORT).show();
+ Log.i(TAG, "Up to date.");
}
else {
Toast.makeText(DungBeetleActivity.this,
@@ -133,14 +130,6 @@ public void onCreate(Bundle savedInstanceState)
tabHost.setCurrentTab(0);
-
- Button button = (Button)findViewById(R.id.share_info_button);
- button.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- shareContactInfo();
- }
- });
-
mNfc = new Nfc(this);
mNfc.addNdefHandler(new Nfc.NdefHandler(){
public int handleNdef(final NdefMessage[] messages){
@@ -153,7 +142,27 @@ public void run(){
}
});
-
+ mNfc.setOnTagWriteListener(new Nfc.OnTagWriteListener(){
+ public void onTagWrite(final int status){
+ DungBeetleActivity.this.runOnUiThread(new Runnable(){
+ public void run(){
+ if(status == WRITE_OK){
+ Toast.makeText(DungBeetleActivity.this, "Wrote successfully!",
+ Toast.LENGTH_SHORT).show();
+ }
+ else if(status == WRITE_ERROR_READ_ONLY){
+ Toast.makeText(DungBeetleActivity.this, "Can't write read-only tag!",
+ Toast.LENGTH_SHORT).show();
+ }
+ else{
+ Toast.makeText(DungBeetleActivity.this, "Failed to write!",
+ Toast.LENGTH_SHORT).show();
+ }
+ mNfc.clearSharing();
+ }
+ });
+ }
+ });
}
protected void doHandleNdef(NdefMessage[] messages){
@@ -173,8 +182,19 @@ protected void doHandleNdef(NdefMessage[] messages){
startActivity(intent);
}
+ public void writeGroupToTag(Uri uri){
+ NdefRecord urlRecord = new NdefRecord(
+ NdefRecord.TNF_ABSOLUTE_URI,
+ NdefRecord.RTD_URI, new byte[] {},
+ uri.toString().getBytes());
+ NdefMessage ndef = new NdefMessage(new NdefRecord[] { urlRecord });
+ mNfc.enableTagWriteMode(ndef);
+ Toast.makeText(this,
+ "Touch a tag to write the group...",
+ Toast.LENGTH_SHORT).show();
+ }
- protected void shareContactInfo(){
+ public void shareContactInfo(){
DBHelper helper = new DBHelper(this);
IdentityProvider ident = new DBIdentityProvider(helper);
String name = ident.userName();
diff --git a/src/edu/stanford/mobisocial/dungbeetle/DungBeetleContentProvider.java b/src/edu/stanford/mobisocial/dungbeetle/DungBeetleContentProvider.java
index bc24832b..57b887a4 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/DungBeetleContentProvider.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/DungBeetleContentProvider.java
@@ -1,4 +1,5 @@
package edu.stanford.mobisocial.dungbeetle;
+import edu.stanford.mobisocial.dungbeetle.group_providers.GroupProviders;
import edu.stanford.mobisocial.dungbeetle.model.Contact;
import edu.stanford.mobisocial.dungbeetle.model.Group;
import edu.stanford.mobisocial.dungbeetle.model.Object;
@@ -151,9 +152,10 @@ else if(match(uri, "group_members")){
else if(match(uri, "dynamic_groups")){
if(!appId.equals(SUPER_APP_ID)) return null;
Uri gUri = Uri.parse(values.getAsString("uri"));
- values.put(Group.NAME, gUri.getQueryParameter("name"));
- values.put(Group.DYN_UPDATE_URI, uri.toString());
- long id = mHelper.insertGroup(values);
+ ContentValues cv = new ContentValues();
+ cv.put(Group.NAME, GroupProviders.groupName(gUri));
+ cv.put(Group.DYN_UPDATE_URI, gUri.toString());
+ long id = mHelper.insertGroup(cv);
getContext().getContentResolver().notifyChange(Uri.parse(CONTENT_URI + "/dynamic_groups"), null);
getContext().getContentResolver().notifyChange(Uri.parse(CONTENT_URI + "/groups"), null);
return uriWithId(uri, id);
diff --git a/src/edu/stanford/mobisocial/dungbeetle/DungBeetleService.java b/src/edu/stanford/mobisocial/dungbeetle/DungBeetleService.java
index 81d42d40..f2a13cc5 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/DungBeetleService.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/DungBeetleService.java
@@ -10,7 +10,8 @@
public class DungBeetleService extends Service {
private NotificationManager mNotificationManager;
- private MessagingManagerThread mManagerThread;
+ private MessagingManagerThread mMessagingManagerThread;
+ private GroupManagerThread mGroupManagerThread;
private DBHelper mHelper;
public static final String TAG = "DungBeetleService";
@@ -19,8 +20,10 @@ public class DungBeetleService extends Service {
public void onCreate() {
mHelper = new DBHelper(this);
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
- mManagerThread = new MessagingManagerThread(this);
- mManagerThread.start();
+ mMessagingManagerThread = new MessagingManagerThread(this);
+ mMessagingManagerThread.start();
+ mGroupManagerThread = new GroupManagerThread(this);
+ mGroupManagerThread.start();
}
diff --git a/src/edu/stanford/mobisocial/dungbeetle/GroupManagerThread.java b/src/edu/stanford/mobisocial/dungbeetle/GroupManagerThread.java
index ec07822c..99f94352 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/GroupManagerThread.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/GroupManagerThread.java
@@ -5,6 +5,7 @@
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
+import edu.stanford.mobisocial.dungbeetle.group_providers.GroupProviders.PrplGroupRefreshHandler;
import edu.stanford.mobisocial.dungbeetle.model.Group;
import java.util.ArrayList;
import java.util.List;
@@ -25,7 +26,7 @@ public GroupManagerThread(final Context context){
mContext.getContentResolver().registerContentObserver(
Uri.parse(DungBeetleContentProvider.CONTENT_URI +
"/dynamic_groups"), true, mOco);
- mHandlers.add(new PrplGroupHandler());
+ mHandlers.add(new PrplGroupRefreshHandler());
}
@Override
@@ -74,37 +75,26 @@ public synchronized void clearChanged() {
}
};
- private List mHandlers = new ArrayList();
+ private List mHandlers = new ArrayList();
// FYI: Invoked in manager thread
private void handleUpdate(final Group g){
final Uri uri = Uri.parse(g.dynUpdateUri);
- for(final GroupHandler h : mHandlers){
+ for(final GroupRefreshHandler h : mHandlers){
if(h.willHandle(uri)){
new Thread(){public void run(){
- h.handle(g.id, uri);
+ h.handle(g.id, uri, mContext, mIdent);
}}.start();
break;
}
}
}
- // These handlers should be stateless
- abstract class GroupHandler{
- abstract boolean willHandle(Uri uri);
- abstract void handle(long id, Uri uri);
- }
- class PrplGroupHandler extends GroupHandler{
- public boolean willHandle(Uri uri){
- return uri.getAuthority().equals("suif.stanford.edu");
- }
- public void handle(long id, Uri uriIn){
- Uri.Builder b = uriIn.buildUpon();
- b.scheme("http");
- Uri uri = b.build();
- Log.i(TAG, "Doing dynamic group update for " + uri);
- }
+ // These handlers should be stateless
+ public static abstract class GroupRefreshHandler{
+ public abstract boolean willHandle(Uri uri);
+ public abstract void handle(long id, Uri uri, Context context, IdentityProvider ident);
}
}
diff --git a/src/edu/stanford/mobisocial/dungbeetle/GroupsActivity.java b/src/edu/stanford/mobisocial/dungbeetle/GroupsActivity.java
index 970c9db9..ce8e087d 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/GroupsActivity.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/GroupsActivity.java
@@ -13,47 +13,78 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.TextView;
+import edu.stanford.mobisocial.dungbeetle.group_providers.GroupProviders;
+import edu.stanford.mobisocial.dungbeetle.model.Contact;
import edu.stanford.mobisocial.dungbeetle.model.Group;
import edu.stanford.mobisocial.dungbeetle.util.BitmapManager;
+import java.util.Collection;
public class GroupsActivity extends ListActivity implements OnItemClickListener{
-
private GroupListCursorAdapter mGroups;
public static final String SHARE_SCHEME = "db-share-contact";
protected final BitmapManager mBitmaps = new BitmapManager(10);
+ private DBHelper mHelper;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
+ mHelper = new DBHelper(this);
Cursor c = getContentResolver().query(
Uri.parse(DungBeetleContentProvider.CONTENT_URI + "/groups"),
- new String[]{ Group._ID, Group.NAME },
- null, null, null);
+ null, null, null, null);
mGroups = new GroupListCursorAdapter(this, c);
setListAdapter(mGroups);
getListView().setOnItemClickListener(this);
}
- public void onItemClick(AdapterView> parent, View view, int position, long id){
- Intent viewGroupIntent = new Intent(this, ContactsActivity.class);
- Cursor c = (Cursor)mGroups.getItem(position);
- Long group_id = c.getLong(c.getColumnIndexOrThrow(Group._ID));
- viewGroupIntent.putExtra("group_id", group_id);
- startActivity(viewGroupIntent);
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, final int position, long id){
+ Cursor cursor = (Cursor)mGroups.getItem(position);
+ final Group g = new Group(cursor);
+ final Collection contactsInGroup = g.contactCollection(mHelper);
+ final CharSequence[] items = new CharSequence[]{ "Send Message", "Start Application", "View Contacts", "Delete" };
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Actions");
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ switch(item){
+ case 0:
+ UIHelpers.sendMessageToContact(
+ GroupsActivity.this,
+ contactsInGroup);
+ break;
+ case 1:
+ UIHelpers.startApplicationWithContact(
+ GroupsActivity.this,
+ contactsInGroup);
+ break;
+ case 2:
+ Intent viewGroupIntent = new Intent(GroupsActivity.this, ContactsActivity.class);
+ viewGroupIntent.putExtra("group_id", g.id);
+ startActivity(viewGroupIntent);
+ break;
+ case 3:
+ Helpers.deleteGroup(GroupsActivity.this, g.id);
+ break;
+ }
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
}
- private class GroupListCursorAdapter extends CursorAdapter {
+ private class GroupListCursorAdapter extends CursorAdapter {
public GroupListCursorAdapter (Context context, Cursor c) {
super(context, c);
}
-
@Override
public View newView(Context context, Cursor c, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -61,8 +92,6 @@ public View newView(Context context, Cursor c, ViewGroup parent) {
bindView(v, context, c);
return v;
}
-
-
@Override
public void bindView(View v, Context context, Cursor c) {
String name = c.getString(c.getColumnIndexOrThrow(Group.NAME));
@@ -71,25 +100,27 @@ public void bindView(View v, Context context, Cursor c) {
nameText.setText(name);
}
}
-
}
- private final static int UPDATE_GROUP = 0;
-
public boolean onCreateOptionsMenu(Menu menu){
return true;
}
+ private final static int ADD_GROUP = 0;
+ private final static int WRITE_GROUP_TO_TAG = 1;
+
+
public boolean onPreparePanel(int featureId, View view, Menu menu) {
menu.clear();
- menu.add(0, 0, 0, "Add group");
+ menu.add(0, ADD_GROUP, 0, "Add group");
+ menu.add(0, WRITE_GROUP_TO_TAG, 0, "Write dynamic group to tag");
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
- case UPDATE_GROUP: {
+ case ADD_GROUP: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setMessage("Enter group name:");
final EditText input = new EditText(this);
@@ -102,7 +133,6 @@ public void onClick(DialogInterface dialog, int whichButton) {
Uri.parse(DungBeetleContentProvider.CONTENT_URI + "/groups"), values);
}
});
-
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
@@ -110,6 +140,26 @@ public void onClick(DialogInterface dialog, int whichButton) {
alert.show();
return true;
}
+ case WRITE_GROUP_TO_TAG: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ alert.setMessage("Enter group name:");
+ final EditText input = new EditText(this);
+ alert.setView(input);
+ alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ IdentityProvider ident = new DBIdentityProvider(mHelper);
+ Uri uri = GroupProviders.newSessionUri(ident, input.getText().toString());
+ ((DungBeetleActivity)getParent()).writeGroupToTag(uri);
+ }
+ });
+ alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ }
+ });
+ alert.show();
+
+ return true;
+ }
default: return false;
}
}
@@ -117,6 +167,7 @@ public void onClick(DialogInterface dialog, int whichButton) {
@Override
public void finish() {
super.finish();
+ mHelper.close();
}
}
diff --git a/src/edu/stanford/mobisocial/dungbeetle/HandleGroupSessionActivity.java b/src/edu/stanford/mobisocial/dungbeetle/HandleGroupSessionActivity.java
index d3125d01..b2d6f5f3 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/HandleGroupSessionActivity.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/HandleGroupSessionActivity.java
@@ -12,6 +12,7 @@
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
+import edu.stanford.mobisocial.dungbeetle.group_providers.GroupProviders;
public class HandleGroupSessionActivity extends Activity {
@@ -28,13 +29,14 @@ public void onCreate(Bundle savedInstanceState) {
if(scheme != null && scheme.equals(SCHEME)){
final Uri uri = intent.getData();
if(uri != null){
- String name = uri.getQueryParameter("name");
- mNameText = (TextView)findViewById(R.id.name_text);
- mNameText.setText("Would you like to join the group '" + name + "'?");
+ String groupName = GroupProviders.groupName(uri);
+ mNameText = (TextView)findViewById(R.id.text);
+ mNameText.setText("Would you like to join the group '" + groupName + "'?");
Button b1 = (Button)findViewById(R.id.yes_button);
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
loadGroup(uri);
+ finish();
}
});
Button b2 = (Button)findViewById(R.id.no_button);
@@ -55,7 +57,6 @@ public void onClick(View v) {
private void loadGroup(Uri uri){
Helpers.addDynamicGroup(this, uri);
-
Intent launch = new Intent();
launch.setAction(Intent.ACTION_MAIN);
launch.addCategory(Intent.CATEGORY_LAUNCHER);
diff --git a/src/edu/stanford/mobisocial/dungbeetle/Helpers.java b/src/edu/stanford/mobisocial/dungbeetle/Helpers.java
index c751271e..82f58f33 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/Helpers.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/Helpers.java
@@ -1,4 +1,7 @@
package edu.stanford.mobisocial.dungbeetle;
+import android.util.Log;
+import edu.stanford.mobisocial.dungbeetle.model.Group;
+import edu.stanford.mobisocial.dungbeetle.model.GroupMember;
import edu.stanford.mobisocial.dungbeetle.model.InviteObj;
import edu.stanford.mobisocial.dungbeetle.model.Subscriber;
import edu.stanford.mobisocial.dungbeetle.model.Object;
@@ -13,6 +16,7 @@
import android.content.Context;
public class Helpers {
+ public static final String TAG = "Helpers";
public static void insertSubscriber(final Context c,
Long contactId,
@@ -24,6 +28,16 @@ public static void insertSubscriber(final Context c,
c.getContentResolver().insert(url, values);
}
+ public static void deleteGroup(final Context c,
+ Long groupId){
+ c.getContentResolver().delete(
+ Uri.parse(DungBeetleContentProvider.CONTENT_URI + "/group_members"),
+ GroupMember.GROUP_ID + "=?",
+ new String[]{ String.valueOf(groupId)});
+ c.getContentResolver().delete(
+ Uri.parse(DungBeetleContentProvider.CONTENT_URI + "/groups"),
+ Group._ID + "=?", new String[]{ String.valueOf(groupId)});
+ }
public static Uri insertContact(final Context c, String pubKeyStr,
String name, String email){
@@ -34,6 +48,18 @@ public static Uri insertContact(final Context c, String pubKeyStr,
Uri url = Uri.parse(DungBeetleContentProvider.CONTENT_URI + "/contacts");
return c.getContentResolver().insert(url, values);
}
+
+ public static void insertGroupMember(final Context c,
+ final long groupId,
+ final long contactId,
+ final String idInGroup){
+ Uri url = Uri.parse(DungBeetleContentProvider.CONTENT_URI + "/group_members");
+ ContentValues values = new ContentValues();
+ values.put(GroupMember.GROUP_ID, groupId);
+ values.put(GroupMember.CONTACT_ID, contactId);
+ values.put(GroupMember.GLOBAL_CONTACT_ID, idInGroup);
+ c.getContentResolver().insert(url, values);
+ }
public static void sendApplicationInvite(final Context c,
final Collection contacts,
@@ -61,7 +87,10 @@ public static void sendIM(final Context c,
obj.put("text", msg);
}catch(JSONException e){}
values.put(Object.JSON, obj.toString());
- values.put(Object.DESTINATION, buildAddresses(contacts));
+ Log.i(TAG, "Num contacts: " + contacts.size());
+ String to = buildAddresses(contacts);
+ Log.i(TAG, "To " + to);
+ values.put(Object.DESTINATION, to);
values.put(Object.TYPE, "instant_message");
c.getContentResolver().insert(url, values);
}
diff --git a/src/edu/stanford/mobisocial/dungbeetle/MessagingManagerThread.java b/src/edu/stanford/mobisocial/dungbeetle/MessagingManagerThread.java
index 4c9c16d0..d4bfb6af 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/MessagingManagerThread.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/MessagingManagerThread.java
@@ -71,10 +71,10 @@ public boolean isConnected(){
mMessenger = new XMPPMessengerService(wrapIdent(mIdent), status);
mMessenger.addStateListener(new StateListener() {
public void onReady() {
- toastInMainThread("Connected to message transport!");
+ Log.i(TAG, "Connected to message transport!");
}
public void onNotReady() {
- toastInMainThread("Message transport not available.");
+ Log.i(TAG, "Message transport not available.");
}
});
mMessenger.addMessageListener(new MessageListener() {
@@ -355,7 +355,7 @@ void handle(Contact from, JSONObject obj){
launch.setPackage(packageName);
final PackageManager mgr = mContext.getPackageManager();
List resolved = mgr.queryIntentActivities(launch, 0);
- if (resolved.size() == 0) {
+ if (resolved == null || resolved.size() == 0) {
Toast.makeText(mContext,
"Could not find application to handle invite",
Toast.LENGTH_SHORT).show();
diff --git a/src/edu/stanford/mobisocial/dungbeetle/ObjectsActivity.java b/src/edu/stanford/mobisocial/dungbeetle/ObjectsActivity.java
index 7afcd7c7..d0ed46a6 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/ObjectsActivity.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/ObjectsActivity.java
@@ -32,6 +32,7 @@ public class ObjectsActivity extends ListActivity implements OnItemClickListener
protected final BitmapManager mBitmaps = new BitmapManager(10);
private ObjectListCursorAdapter mObjects;
private DBIdentityProvider mIdent;
+ private DBHelper mHelper;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -44,10 +45,9 @@ public void onCreate(Bundle savedInstanceState) {
mObjects = new ObjectListCursorAdapter(this, c);
setListAdapter(mObjects);
getListView().setOnItemClickListener(this);
-
- mIdent = new DBIdentityProvider(new DBHelper(this));
-
- final DBHelper helper = new DBHelper(ObjectsActivity.this);
+
+ mHelper = new DBHelper(ObjectsActivity.this);
+ mIdent = new DBIdentityProvider(mHelper);
Button button = (Button)findViewById(R.id.add_object_button);
button.setOnClickListener(new OnClickListener() {
diff --git a/src/edu/stanford/mobisocial/dungbeetle/ProfileActivity.java b/src/edu/stanford/mobisocial/dungbeetle/ProfileActivity.java
index 63d0076c..ad44f8e7 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/ProfileActivity.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/ProfileActivity.java
@@ -1,37 +1,27 @@
package edu.stanford.mobisocial.dungbeetle;
-
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
-import android.widget.Toast;
+
public class ProfileActivity extends Activity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
setContentView(R.layout.profile);
-
final DBHelper helper = new DBHelper(ProfileActivity.this);
-
final EditText profile_name = (EditText) findViewById(R.id.profile_name);
-
profile_name.setText(helper.getMyName());
-
Button save_button = (Button) findViewById(R.id.save_profile_button);
-
save_button.setOnClickListener(new OnClickListener(){
public void onClick(View v)
{
helper.setMyName(profile_name.getText().toString());
Helpers.updateProfile(ProfileActivity.this, profile_name.getText().toString());
}
- });
-
-
-
-
+ });
+ helper.close();
}
}
diff --git a/src/edu/stanford/mobisocial/dungbeetle/UIHelpers.java b/src/edu/stanford/mobisocial/dungbeetle/UIHelpers.java
index 5d8cbe68..446f8dee 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/UIHelpers.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/UIHelpers.java
@@ -1,18 +1,117 @@
package edu.stanford.mobisocial.dungbeetle;
+import android.app.Activity;
import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
+import android.widget.EditText;
+import android.widget.Toast;
import edu.stanford.mobisocial.dungbeetle.model.Contact;
import edu.stanford.mobisocial.dungbeetle.model.Group;
import edu.stanford.mobisocial.dungbeetle.model.GroupMember;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
public class UIHelpers {
+ public static void sendMessageToContact(final Context context,
+ final Collection contacts){
+ AlertDialog.Builder alert = new AlertDialog.Builder(context);
+ alert.setMessage("Enter message:");
+ final EditText input = new EditText(context);
+ alert.setView(input);
+ alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ Helpers.sendIM(
+ context,
+ contacts,
+ input.getText().toString());
+ }
+ });
+ alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ }
+ });
+ alert.show();
+ }
+
+ public static void startApplicationWithContact(final Context context,
+ final Collection contacts){
+ final PackageManager mgr = context.getPackageManager();
+ Intent i = new Intent("android.intent.action.CONFIGURE");
+ i.addCategory("android.intent.category.P2P");
+ final List infos = mgr.queryBroadcastReceivers(i, 0);
+ if(infos.size() > 0){
+ ArrayList names = new ArrayList();
+ for(ResolveInfo info : infos){
+ names.add(info.loadLabel(mgr).toString());
+ }
+ final CharSequence[] items = names.toArray(new CharSequence[]{});
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle("Share application:");
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ final ResolveInfo info = infos.get(item);
+ Intent i = new Intent();
+ i.setClassName(info.activityInfo.packageName,
+ info.activityInfo.name);
+ i.setAction("android.intent.action.CONFIGURE");
+ i.addCategory("android.intent.category.P2P");
+ BroadcastReceiver rec = new BroadcastReceiver(){
+ public void onReceive(Context c, Intent i){
+ Intent launch = new Intent();
+ launch.setAction(Intent.ACTION_MAIN);
+ launch.addCategory(Intent.CATEGORY_LAUNCHER);
+ launch.setPackage(info.activityInfo.packageName);
+ List resolved =
+ mgr.queryIntentActivities(launch, 0);
+ if (resolved.size() > 0) {
+ ActivityInfo info = resolved.get(0).activityInfo;
+ String arg = getResultData();
+ launch.setComponent(new ComponentName(
+ info.packageName,
+ info.name));
+ launch.putExtra("creator", true);
+ launch.putExtra(
+ "android.intent.extra.APPLICATION_ARGUMENT",
+ arg);
+ context.startActivity(launch);
+ Helpers.sendApplicationInvite(
+ context,
+ contacts,
+ info.packageName, arg);
+ }
+ else{
+ Toast.makeText(context,
+ "Sorry, no response from applications.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+ context.sendOrderedBroadcast(i, null, rec, null, Activity.RESULT_OK, null, null);
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+ else{
+ Toast.makeText(context.getApplicationContext(),
+ "Sorry, couldn't find any compatible apps.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
public static void showGroupPicker(final Context context,
final Contact contact) {
Cursor c = context.getContentResolver().query(
diff --git a/src/edu/stanford/mobisocial/dungbeetle/group_providers/GroupProviders.java b/src/edu/stanford/mobisocial/dungbeetle/group_providers/GroupProviders.java
new file mode 100644
index 00000000..a489cb31
--- /dev/null
+++ b/src/edu/stanford/mobisocial/dungbeetle/group_providers/GroupProviders.java
@@ -0,0 +1,96 @@
+package edu.stanford.mobisocial.dungbeetle.group_providers;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.util.Log;
+import edu.stanford.mobisocial.dungbeetle.DBIdentityProvider;
+import edu.stanford.mobisocial.dungbeetle.HandleGroupSessionActivity;
+import edu.stanford.mobisocial.dungbeetle.Helpers;
+import edu.stanford.mobisocial.dungbeetle.IdentityProvider;
+import edu.stanford.mobisocial.dungbeetle.util.Util;
+import edu.stanford.mobisocial.dungbeetle.GroupManagerThread.GroupRefreshHandler;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.PublicKey;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class GroupProviders{
+
+ public static final String TAG = "GroupProviders";
+
+ public static String groupName(Uri uri){
+ return uri.getQueryParameter("groupName");
+ }
+
+ public static Uri newSessionUri(IdentityProvider ident, String groupName){
+ Uri.Builder builder = new Uri.Builder();
+ builder.scheme(HandleGroupSessionActivity.SCHEME);
+ builder.authority("suif.stanford.edu");
+ builder.appendPath("dungbeetle");
+ builder.appendPath("index.php");
+ builder.appendQueryParameter("session", Util.MD5("session" + Math.random()));
+ builder.appendQueryParameter("groupName", groupName);
+ Uri uri = builder.build();
+ return uri;
+ }
+
+ public static class PrplGroupRefreshHandler extends GroupRefreshHandler{
+ public boolean willHandle(Uri uri){
+ return uri.getAuthority().equals("suif.stanford.edu");
+ }
+ public void handle(final long groupId, final Uri uriIn,
+ final Context context, final IdentityProvider ident){
+ Uri.Builder b = uriIn.buildUpon();
+ b.appendQueryParameter(
+ "public_key",
+ DBIdentityProvider.publicKeyToString(ident.userPublicKey()));
+ b.appendQueryParameter("email", ident.userEmail());
+ b.scheme("http");
+ Uri uri = b.build();
+ Log.i(TAG, "Doing dynamic group update for " + uri);
+ StringBuffer sb = new StringBuffer();
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpGet httpGet = new HttpGet(uri.toString());
+ try {
+ HttpResponse execute = client.execute(httpGet);
+ InputStream content = execute.getEntity().getContent();
+ BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
+ String s = "";
+ while ((s = buffer.readLine()) != null) {
+ sb.append(s);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ String response = sb.toString();
+ Log.i(TAG, "Got response: " + response);
+ try{
+ JSONArray arr = new JSONArray(response);
+ for(int i = 0; i < arr.length(); i++){
+ String objStr = arr.getString(i);
+ JSONObject o = new JSONObject(objStr);
+ final String pubKeyStr = o.getString("public_key");
+ final String email = o.getString("email");
+ final String profile = o.getString("profile");
+ final String groupSession = o.getString("group_session");
+ final String idInGroup = o.getString("group_id");
+ (new Handler(context.getMainLooper())).post(new Runnable(){
+ public void run(){
+ Uri u = Helpers.insertContact(context, pubKeyStr, email, email);
+ Long cid = Long.valueOf(u.getLastPathSegment());
+ Helpers.insertGroupMember(context, groupId, cid, idInGroup);
+ }
+ });
+ }
+ } catch(JSONException e){Log.e(TAG, e.getStackTrace().toString());}
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/edu/stanford/mobisocial/dungbeetle/model/ContactCollection.java b/src/edu/stanford/mobisocial/dungbeetle/model/ContactCollection.java
new file mode 100644
index 00000000..485a7341
--- /dev/null
+++ b/src/edu/stanford/mobisocial/dungbeetle/model/ContactCollection.java
@@ -0,0 +1,42 @@
+package edu.stanford.mobisocial.dungbeetle.model;
+import android.database.Cursor;
+import edu.stanford.mobisocial.dungbeetle.DBHelper;
+import java.util.AbstractCollection;
+import java.util.Iterator;
+
+/**
+ * Lazy iterator of contacts in a group.
+ */
+public class ContactCollection extends AbstractCollection {
+ public final Cursor mCursor;
+
+ public ContactCollection(long groupId, DBHelper helper){
+ mCursor = helper.queryGroupContacts(groupId);
+ }
+
+ @Override
+ public int size(){
+ return mCursor.getCount();
+ }
+
+ @Override
+ public Iterator iterator(){
+ mCursor.moveToFirst();
+ return new Iterator(){
+ @Override
+ public Contact next(){
+ Contact c = new Contact(mCursor);
+ mCursor.moveToNext();
+ return c;
+ }
+ @Override
+ public boolean hasNext(){
+ return !mCursor.isAfterLast();
+ }
+ @Override
+ public void remove(){
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
diff --git a/src/edu/stanford/mobisocial/dungbeetle/model/Group.java b/src/edu/stanford/mobisocial/dungbeetle/model/Group.java
index 300e4b16..f60b1de4 100644
--- a/src/edu/stanford/mobisocial/dungbeetle/model/Group.java
+++ b/src/edu/stanford/mobisocial/dungbeetle/model/Group.java
@@ -1,5 +1,7 @@
package edu.stanford.mobisocial.dungbeetle.model;
import android.database.Cursor;
+import edu.stanford.mobisocial.dungbeetle.DBHelper;
+import java.util.Collection;
public class Group{
public static final String TABLE = "groups";
@@ -17,4 +19,8 @@ public Group(Cursor c){
dynUpdateUri = c.getString(c.getColumnIndexOrThrow(DYN_UPDATE_URI));
}
+ public Collection contactCollection(DBHelper helper){
+ return new ContactCollection(id, helper);
+ }
+
}