diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 5ca5295f1..e7d5f2c04 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -17,7 +17,8 @@ android:theme="@android:style/Theme.Holo.Light" > + android:label="Secure Conversations" + android:windowSoftInputMode="stateHidden"> diff --git a/gen/de/gultsch/chat/R.java b/gen/de/gultsch/chat/R.java index c4a924bec..fb44a0af8 100644 --- a/gen/de/gultsch/chat/R.java +++ b/gen/de/gultsch/chat/R.java @@ -35,36 +35,35 @@ public final class R { public static final int section_header=0x7f020007; } public static final class id { - public static final int action_accounts=0x7f0a001c; - public static final int action_add=0x7f0a0018; - public static final int action_archive=0x7f0a001b; - public static final int action_details=0x7f0a001a; - public static final int action_security=0x7f0a0019; - public static final int action_settings=0x7f0a001d; + public static final int action_accounts=0x7f0a001b; + public static final int action_add=0x7f0a0017; + public static final int action_archive=0x7f0a001a; + public static final int action_details=0x7f0a0019; + public static final int action_security=0x7f0a0018; + public static final int action_settings=0x7f0a001c; public static final int contact_display_name=0x7f0a0009; public static final int contact_divider=0x7f0a000b; public static final int contact_jid=0x7f0a000a; public static final int contact_photo=0x7f0a0008; - public static final int conversation_lastmsg=0x7f0a000f; - public static final int conversation_name=0x7f0a000e; + public static final int conversation_image=0x7f0a000c; + public static final int conversation_lastmsg=0x7f0a000e; + public static final int conversation_name=0x7f0a000d; public static final int create_new_contact=0x7f0a0007; - public static final int duration=0x7f0a0010; - public static final int editText1=0x7f0a0012; - public static final int imageButton1=0x7f0a0013; - public static final int imageView1=0x7f0a000d; + public static final int duration=0x7f0a000f; + public static final int editText1=0x7f0a0011; + public static final int imageButton1=0x7f0a0012; public static final int jabber_contacts=0x7f0a0005; public static final int jabber_contacts_header=0x7f0a0004; - public static final int list=0x7f0a0016; + public static final int list=0x7f0a0015; public static final int new_contact_header=0x7f0a0006; public static final int new_conversation_search=0x7f0a0000; public static final int phone_contacts=0x7f0a0003; public static final int phone_contacts_header=0x7f0a0002; public static final int scrollView1=0x7f0a0001; - public static final int selected_conversation=0x7f0a0017; - public static final int slidingpanelayout=0x7f0a0015; - public static final int textView1=0x7f0a0014; - public static final int textsend=0x7f0a0011; - public static final int thumbnail=0x7f0a000c; + public static final int selected_conversation=0x7f0a0016; + public static final int slidingpanelayout=0x7f0a0014; + public static final int textView1=0x7f0a0013; + public static final int textsend=0x7f0a0010; } public static final class layout { public static final int activity_new_conversation=0x7f030000; @@ -84,7 +83,6 @@ public final class R { public static final int action_secure=0x7f070006; public static final int action_settings=0x7f070001; public static final int app_name=0x7f070000; - public static final int hello_world=0x7f070008; public static final int title_activity_new_conversation=0x7f070007; } public static final class style { diff --git a/res/layout/conversation_list_row.xml b/res/layout/conversation_list_row.xml index 5b3959c38..cfac71755 100644 --- a/res/layout/conversation_list_row.xml +++ b/res/layout/conversation_list_row.xml @@ -2,61 +2,53 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:padding="5dip"> + android:padding="8dp"> - - - - - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 053dfa5c1..d6849e022 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5,10 +5,8 @@ Settings New conversation Manage accounts - Archive + Archive conversation Show details Secure conversation New Conversation - Hello world! - diff --git a/src/de/gultsch/chat/Conversation.java b/src/de/gultsch/chat/Conversation.java deleted file mode 100644 index cec96a355..000000000 --- a/src/de/gultsch/chat/Conversation.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.gultsch.chat; - -import java.util.ArrayList; - - -public class Conversation { - private String name; - private ArrayList msgs = new ArrayList(); - - public Conversation(String name) { - this.name = name; - } - - public ArrayList getLastMessages(int count, int offset) { - msgs.add(new Message("this is my last message")); - return msgs; - } - public String getName() { - return this.name; - } -} diff --git a/src/de/gultsch/chat/ConversationCursor.java b/src/de/gultsch/chat/ConversationCursor.java index 1ce431b48..5ab3ef563 100644 --- a/src/de/gultsch/chat/ConversationCursor.java +++ b/src/de/gultsch/chat/ConversationCursor.java @@ -3,6 +3,9 @@ package de.gultsch.chat; import java.util.ArrayList; +import de.gultsch.chat.entities.Conversation; +import de.gultsch.chat.entities.Message; + import android.database.AbstractCursor; diff --git a/src/de/gultsch/chat/ConversationList.java b/src/de/gultsch/chat/ConversationList.java index 205d3549f..ac5f92301 100644 --- a/src/de/gultsch/chat/ConversationList.java +++ b/src/de/gultsch/chat/ConversationList.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import de.gultsch.chat.entities.Conversation; + public class ConversationList extends ArrayList { private static final long serialVersionUID = 3661496589984289968L; @@ -24,21 +26,6 @@ public class ConversationList extends ArrayList { this.selectedConversationPosition = selectedConversation; } - public void sort() { - Conversation selectedConversation = this.get(selectedConversationPosition); - //sort this - Collections.sort(this, new Comparator() { - - @Override - public int compare(Conversation lhs, Conversation rhs) { - // TODO Auto-generated method stub - return 0; - } - }); - - this.selectedConversationPosition = this.indexOf(selectedConversation); - } - public synchronized int addAndReturnPosition(Conversation conversation) { this.add(conversation); return size() - 1; diff --git a/src/de/gultsch/chat/entities/Account.java b/src/de/gultsch/chat/entities/Account.java new file mode 100644 index 000000000..f1e15f0b3 --- /dev/null +++ b/src/de/gultsch/chat/entities/Account.java @@ -0,0 +1,11 @@ +package de.gultsch.chat.entities; + +public class Account { + + private String uuid; + + public String getUuid() { + return this.uuid; + } + +} diff --git a/src/de/gultsch/chat/Contact.java b/src/de/gultsch/chat/entities/Contact.java similarity index 95% rename from src/de/gultsch/chat/Contact.java rename to src/de/gultsch/chat/entities/Contact.java index 642044616..30dfc05bc 100644 --- a/src/de/gultsch/chat/Contact.java +++ b/src/de/gultsch/chat/entities/Contact.java @@ -1,4 +1,4 @@ -package de.gultsch.chat; +package de.gultsch.chat.entities; import java.io.Serializable; diff --git a/src/de/gultsch/chat/entities/Conversation.java b/src/de/gultsch/chat/entities/Conversation.java new file mode 100644 index 000000000..92d5bcba6 --- /dev/null +++ b/src/de/gultsch/chat/entities/Conversation.java @@ -0,0 +1,99 @@ +package de.gultsch.chat.entities; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.UUID; + +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class Conversation implements Serializable { + + private static final long serialVersionUID = -6727528868973996739L; + public static final int STATUS_AVAILABLE = 0; + public static final int STATUS_ARCHIVED = 1; + public static final int STATUS_DELETED = 2; + private String uuid; + private String name; + private String profilePhotoUri; + private String accountUuid; + private String contactJid; + private int status; + + // legacy. to be removed + private ArrayList msgs = new ArrayList(); + + public Conversation(String name, Uri profilePhoto, Account account, + String contactJid) { + this(UUID.randomUUID().toString(), name, profilePhoto.toString(), + account.getUuid(), contactJid, STATUS_AVAILABLE); + } + + public Conversation(String uuid, String name, String profilePhoto, + String accountUuid, String contactJid, int status) { + this.uuid = uuid; + this.name = name; + this.profilePhotoUri = profilePhoto; + this.accountUuid = accountUuid; + this.contactJid = contactJid; + this.status = status; + } + + public ArrayList getLastMessages(int count, int offset) { + msgs.add(new Message("this is my last message")); + return msgs; + } + + public String getName() { + return this.name; + } + + public String getUuid() { + return this.uuid; + } + + public String getProfilePhotoString() { + return this.profilePhotoUri; + } + + public String getAccountUuid() { + return this.accountUuid; + } + + public String getContactJid() { + return this.contactJid; + } + + public Uri getProfilePhotoUri() { + if (this.profilePhotoUri != null) { + return Uri.parse(profilePhotoUri); + } + return null; + } + + public int getStatus() { + return this.status; + } + + public ContentValues getContentValues() { + ContentValues values = new ContentValues(); + values.put("uuid", this.uuid); + values.put("name", this.name); + values.put("profilePhotoUri", this.profilePhotoUri); + values.put("accountUuid", this.accountUuid); + values.put("contactJid", this.contactJid); + values.put("status", this.status); + return values; + } + + public static Conversation fromCursor(Cursor cursor) { + return new Conversation( + cursor.getString(cursor.getColumnIndex("uuid")), + cursor.getString(cursor.getColumnIndex("name")), + cursor.getString(cursor.getColumnIndex("profilePhotoUri")), + cursor.getString(cursor.getColumnIndex("accountUuid")), + cursor.getString(cursor.getColumnIndex("contactJid")), + cursor.getInt(cursor.getColumnIndex("status"))); + } +} diff --git a/src/de/gultsch/chat/Message.java b/src/de/gultsch/chat/entities/Message.java similarity index 85% rename from src/de/gultsch/chat/Message.java rename to src/de/gultsch/chat/entities/Message.java index b4e5e7ca5..3b4e21150 100644 --- a/src/de/gultsch/chat/Message.java +++ b/src/de/gultsch/chat/entities/Message.java @@ -1,4 +1,4 @@ -package de.gultsch.chat; +package de.gultsch.chat.entities; public class Message { diff --git a/src/de/gultsch/chat/persistance/DatabaseBackend.java b/src/de/gultsch/chat/persistance/DatabaseBackend.java new file mode 100644 index 000000000..5ce7f7e03 --- /dev/null +++ b/src/de/gultsch/chat/persistance/DatabaseBackend.java @@ -0,0 +1,68 @@ +package de.gultsch.chat.persistance; + +import java.util.ArrayList; +import java.util.List; + +import de.gultsch.chat.entities.Conversation; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +public class DatabaseBackend extends SQLiteOpenHelper { + + private static DatabaseBackend instance = null; + + private static final String DATABASE_NAME = "history"; + private static final int DATABASE_VERSION = 1; + + public DatabaseBackend(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("create table conversations (uuid TEXT, name TEXT, profilePhotoUri TEXT, accountUuid TEXT, contactJid TEXT)"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { + // TODO Auto-generated method stub + + } + + public static synchronized DatabaseBackend getInstance(Context context) { + if (instance == null) { + instance = new DatabaseBackend(context.getApplicationContext()); + } + return instance; + } + + public void addConversation(Conversation conversation) { + SQLiteDatabase db = this.getWritableDatabase(); + db.insert("conversations", null, conversation.getContentValues()); + } + + + public int getConversationCount() { + SQLiteDatabase db = this.getReadableDatabase(); + Cursor cursor = db.rawQuery("select count(uuid) as count from conversations",null); + cursor.moveToFirst(); + return cursor.getInt(0); + } + + public List getConversations(int status) { + List list = new ArrayList(); + SQLiteDatabase db = this.getReadableDatabase(); + String[] selectionArgs = {""+status}; + Cursor cursor = db.rawQuery("select * from conversations where status = ?", selectionArgs); + Log.d("gultsch","getConversations has found "+cursor.getCount()+" rows"); + while(cursor.moveToNext()) { + Log.d("gultsch","converting row #"+cursor.getPosition()); + list.add(Conversation.fromCursor(cursor)); + } + return list; + } + +} diff --git a/src/de/gultsch/chat/ui/ConversationActivity.java b/src/de/gultsch/chat/ui/ConversationActivity.java index 0abfb3e63..f26d89821 100644 --- a/src/de/gultsch/chat/ui/ConversationActivity.java +++ b/src/de/gultsch/chat/ui/ConversationActivity.java @@ -1,15 +1,20 @@ package de.gultsch.chat.ui; import java.util.HashMap; +import java.util.List; -import de.gultsch.chat.Contact; -import de.gultsch.chat.Conversation; import de.gultsch.chat.ConversationCursor; import de.gultsch.chat.ConversationList; import de.gultsch.chat.R; import de.gultsch.chat.R.id; +import de.gultsch.chat.entities.Account; +import de.gultsch.chat.entities.Contact; +import de.gultsch.chat.entities.Conversation; +import de.gultsch.chat.persistance.DatabaseBackend; +import android.net.Uri; import android.os.Bundle; import android.app.Activity; +import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; import android.content.Intent; @@ -17,14 +22,18 @@ import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; import android.util.Log; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; import android.widget.ListView; -import android.widget.SimpleCursorAdapter; +import android.widget.TextView; +import android.widget.ImageView; public class ConversationActivity extends Activity { @@ -34,34 +43,51 @@ public class ConversationActivity extends Activity { protected SlidingPaneLayout spl; protected HashMap conversationFragments = new HashMap(); + private DatabaseBackend dbb; @Override protected void onCreate(Bundle savedInstanceState) { + + dbb = DatabaseBackend.getInstance(this); + super.onCreate(savedInstanceState); + final List conversationList = dbb.getConversations(Conversation.STATUS_AVAILABLE); + + if (getIntent().getAction().equals(Intent.ACTION_MAIN)) { + if (conversationList.size() < 0) { + Log.d("gultsch", + "no conversations detected. redirect to new conversation activity"); + startActivity(new Intent(this, NewConversationActivity.class)); + finish(); + } + } + setContentView(R.layout.fragment_conversations_overview); - final ConversationList conversationList = new ConversationList(); - - String[] fromColumns = { ConversationCursor.NAME, - ConversationCursor.LAST_MSG }; - int[] toViews = { R.id.conversation_name, R.id.conversation_lastmsg }; - - final SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, - R.layout.conversation_list_row, conversationList.getCursor(), - fromColumns, toViews, 0); final ListView listView = (ListView) findViewById(R.id.list); - listView.setAdapter(adapter); - + + listView.setAdapter(new ArrayAdapter(this, R.layout.conversation_list_row, conversationList) { + @Override + public View getView (int position, View view, ViewGroup parent) { + if (view == null) { + LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = (View) inflater.inflate(R.layout.conversation_list_row,null); + ((TextView) view.findViewById(R.id.conversation_name)).setText(getItem(position).getName()); + ((ImageView) view.findViewById(R.id.conversation_image)).setImageURI(getItem(position).getProfilePhotoUri()); + } + return view; + } + + }); + listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView arg0, View clickedView, int position, long arg3) { - conversationList.setSelectedConversationPosition(position); - swapConversationFragment(conversationList); - getActionBar().setTitle( - conversationList.getSelectedConversation().getName()); + swapConversationFragment(conversationList.get(position)); + getActionBar().setTitle(conversationList.get(position).getName()); spl.closePane(); } }); @@ -95,9 +121,8 @@ public class ConversationActivity extends Activity { public void onPanelClosed(View arg0) { if (conversationList.size() > 0) { getActionBar().setDisplayHomeAsUpEnabled(true); - getActionBar().setTitle( - conversationList.getSelectedConversation() - .getName()); + ConversationFragment convFrag = (ConversationFragment) getFragmentManager().findFragmentById(R.id.selected_conversation); + getActionBar().setTitle(convFrag.getConversation().getName()); invalidateOptionsMenu(); } } @@ -116,25 +141,26 @@ public class ConversationActivity extends Activity { ConversationActivity.CONVERSATION_CONTACT); Log.d("gultsch", "start conversation with " + contact.getDisplayName()); - int pos = conversationList - .addAndReturnPosition(new Conversation(contact - .getDisplayName())); - conversationList.setSelectedConversationPosition(pos); - swapConversationFragment(conversationList); + + // start new conversation + Conversation conversation = new Conversation( + contact.getDisplayName(), contact.getProfilePhoto(), + new Account(), contact.getJid()); + + //@TODO don't write to database here; always go through service + dbb.addConversation(conversation); + conversationList.add(0, conversation); + swapConversationFragment(conversationList.get(0)); spl.closePane(); // why do i even need this getActionBar().setDisplayHomeAsUpEnabled(true); - getActionBar().setTitle( - conversationList.getSelectedConversation().getName()); + getActionBar().setTitle(conversationList.get(0).getName()); } } else { // normal startup if (conversationList.size() >= 1) { - conversationList.setSelectedConversationPosition(0); - swapConversationFragment(conversationList); - } else { - startActivity(new Intent(this, NewConversationActivity.class)); + swapConversationFragment(conversationList.get(0)); } } } @@ -168,24 +194,22 @@ public class ConversationActivity extends Activity { break; case R.id.action_add: startActivity(new Intent(this, NewConversationActivity.class)); + case R.id.action_archive: + default: break; } return super.onOptionsItemSelected(item); } - protected void swapConversationFragment( - final ConversationList conversationList) { + protected void swapConversationFragment(Conversation conv) { ConversationFragment selectedFragment; - if (conversationFragments.containsKey(conversationList - .getSelectedConversation())) { - selectedFragment = conversationFragments.get(conversationList - .getSelectedConversation()); + if (conversationFragments.containsKey(conv)) { + selectedFragment = conversationFragments.get(conv); } else { selectedFragment = new ConversationFragment(); - conversationFragments.put( - conversationList.getSelectedConversation(), - selectedFragment); + selectedFragment.setConversation(conv); + conversationFragments.put(conv,selectedFragment); } FragmentTransaction transaction = getFragmentManager() .beginTransaction(); diff --git a/src/de/gultsch/chat/ui/ConversationFragment.java b/src/de/gultsch/chat/ui/ConversationFragment.java index 5b8acd98b..70ceb07c8 100644 --- a/src/de/gultsch/chat/ui/ConversationFragment.java +++ b/src/de/gultsch/chat/ui/ConversationFragment.java @@ -1,6 +1,7 @@ package de.gultsch.chat.ui; import de.gultsch.chat.R; +import de.gultsch.chat.entities.Conversation; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; @@ -9,8 +10,18 @@ import android.view.ViewGroup; public class ConversationFragment extends Fragment { + Conversation conversation; + + public void setConversation(Conversation conv) { + this.conversation = conv; + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_conversation, container, false); } + + public Conversation getConversation() { + return conversation; + } } diff --git a/src/de/gultsch/chat/ui/NewConversationActivity.java b/src/de/gultsch/chat/ui/NewConversationActivity.java index 47bdd0ba8..9dc385a42 100644 --- a/src/de/gultsch/chat/ui/NewConversationActivity.java +++ b/src/de/gultsch/chat/ui/NewConversationActivity.java @@ -5,8 +5,9 @@ import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; -import de.gultsch.chat.Contact; import de.gultsch.chat.R; +import de.gultsch.chat.entities.Contact; +import de.gultsch.chat.persistance.DatabaseBackend; import android.os.Bundle; import android.provider.ContactsContract; import android.text.Editable; @@ -48,15 +49,14 @@ public class NewConversationActivity extends Activity { + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER + "\")"; + protected static final String DEFAULT_PROFILE_PHOTO = "android.resource://de.gultsch.chat/" + R.drawable.ic_profile; protected View getViewForContact(Contact contact) { LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = (View) inflater.inflate(R.layout.contact,null); ((TextView) view.findViewById(R.id.contact_display_name)).setText(contact.getDisplayName()); ((TextView) view.findViewById(R.id.contact_jid)).setText(contact.getJid()); - if (contact.getProfilePhoto() != null) { ((ImageView) view.findViewById(R.id.contact_photo)).setImageURI(contact.getProfilePhoto()); - } view.setOnClickListener(new OnClickListener() { @Override @@ -93,6 +93,12 @@ public class NewConversationActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (DatabaseBackend.getInstance(this).getConversationCount() < 1) { + getActionBar().setDisplayHomeAsUpEnabled(false); + getActionBar().setHomeButtonEnabled(false); + } + setContentView(R.layout.activity_new_conversation); CursorLoader mCursorLoader = new CursorLoader(this, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null, @@ -102,13 +108,17 @@ public class NewConversationActivity extends Activity { @Override public void onLoadComplete(Loader arg0, Cursor cursor) { while (cursor.moveToNext()) { + String profilePhoto = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI)); + if (profilePhoto == null) { + profilePhoto = DEFAULT_PROFILE_PHOTO; + } Contact contact = new Contact( cursor.getString(cursor .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)), cursor.getString(cursor .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)), - cursor.getString(cursor - .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI))); + profilePhoto + ); View contactView = getViewForContact(contact); availablePhoneContacts.put(contact, getViewForContact(contact)); ((LinearLayout) findViewById(R.id.phone_contacts)).addView(contactView); @@ -166,7 +176,7 @@ public class NewConversationActivity extends Activity { if (matcher.find()) { createNewContact.removeAllViews(); String name = search.split("@")[0]; - newContact = new Contact(name,search,null); + newContact = new Contact(name,search,DEFAULT_PROFILE_PHOTO); newContactView = getViewForContact(newContact); newContactView.findViewById(R.id.contact_divider).setVisibility(View.GONE); createNewContact.addView(newContactView);