From 898b0ca8c485888e06e2b5b1c798eebce1a6dabc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 26 Jan 2014 03:27:55 +0100 Subject: [PATCH] chat bubbles. yeah --- AndroidManifest.xml | 1 + gen/de/gultsch/chat/R.java | 30 +++--- res/drawable/message_border.xml | 6 ++ res/layout/conversation_list_row.xml | 2 +- res/layout/fragment_conversation.xml | 85 ++++++++-------- res/layout/message_sent.xml | 53 ++++++++++ res/values/strings.xml | 2 + .../gultsch/chat/entities/Conversation.java | 11 ++- src/de/gultsch/chat/entities/Message.java | 2 + .../chat/persistance/DatabaseBackend.java | 32 ++++--- .../gultsch/chat/ui/ConversationActivity.java | 3 + .../gultsch/chat/ui/ConversationFragment.java | 96 +++++++++++++++++-- src/de/gultsch/chat/ui/XmppActivity.java | 4 +- src/de/gultsch/chat/utils/Beautifier.java | 25 +++++ 14 files changed, 273 insertions(+), 79 deletions(-) create mode 100644 res/drawable/message_border.xml create mode 100644 res/layout/message_sent.xml create mode 100644 src/de/gultsch/chat/utils/Beautifier.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index be7addb51..e0ebee7f3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -9,6 +9,7 @@ android:targetSdkVersion="19" /> + + + + + + \ No newline at end of file diff --git a/res/layout/conversation_list_row.xml b/res/layout/conversation_list_row.xml index cfac71755..303fdd1d2 100644 --- a/res/layout/conversation_list_row.xml +++ b/res/layout/conversation_list_row.xml @@ -38,7 +38,7 @@ android:paddingTop="3dp"/> - + android:background="#e5e5e5" > + - - - - + + + - - + android:background="#e5e5e5" + tools:listitem="@layout/message_sent" + android:divider="@null" + android:dividerHeight="0dp"> + - - - - - - - + \ No newline at end of file diff --git a/res/layout/message_sent.xml b/res/layout/message_sent.xml new file mode 100644 index 000000000..c690c2173 --- /dev/null +++ b/res/layout/message_sent.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index d6849e022..d48f23cd7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9,4 +9,6 @@ Show details Secure conversation New Conversation + just now + sending… diff --git a/src/de/gultsch/chat/entities/Conversation.java b/src/de/gultsch/chat/entities/Conversation.java index 2dd8a0495..413e3d626 100644 --- a/src/de/gultsch/chat/entities/Conversation.java +++ b/src/de/gultsch/chat/entities/Conversation.java @@ -1,5 +1,6 @@ package de.gultsch.chat.entities; +import java.util.ArrayList; import java.util.List; import android.content.ContentValues; @@ -9,6 +10,9 @@ import android.net.Uri; public class Conversation extends AbstractEntity { private static final long serialVersionUID = -6727528868973996739L; + + public static final String TABLENAME = "conversations"; + public static final int STATUS_AVAILABLE = 0; public static final int STATUS_ARCHIVED = 1; public static final int STATUS_DELETED = 2; @@ -27,7 +31,7 @@ public class Conversation extends AbstractEntity { private int status; private long created; - private transient List messages; + private transient List messages = null; public Conversation(String name, Uri profilePhoto, Account account, String contactJid) { @@ -48,6 +52,7 @@ public class Conversation extends AbstractEntity { } public List getMessages() { + if (messages == null) this.messages = new ArrayList(); //prevent null pointer return messages; } @@ -81,6 +86,10 @@ public class Conversation extends AbstractEntity { public int getStatus() { return this.status; } + + public long getCreated() { + return this.created; + } public ContentValues getContentValues() { ContentValues values = new ContentValues(); diff --git a/src/de/gultsch/chat/entities/Message.java b/src/de/gultsch/chat/entities/Message.java index 5e5cfbe40..26e646ff4 100644 --- a/src/de/gultsch/chat/entities/Message.java +++ b/src/de/gultsch/chat/entities/Message.java @@ -6,6 +6,8 @@ import android.database.Cursor; public class Message extends AbstractEntity { private static final long serialVersionUID = 7222081895167103025L; + + public static final String TABLENAME = "messages"; public static final int STATUS_RECIEVED = 0; public static final int STATUS_UNSEND = 1; diff --git a/src/de/gultsch/chat/persistance/DatabaseBackend.java b/src/de/gultsch/chat/persistance/DatabaseBackend.java index dac83f586..06b9af16f 100644 --- a/src/de/gultsch/chat/persistance/DatabaseBackend.java +++ b/src/de/gultsch/chat/persistance/DatabaseBackend.java @@ -4,15 +4,16 @@ import java.util.ArrayList; import java.util.List; import de.gultsch.chat.entities.Conversation; +import de.gultsch.chat.entities.Message; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DatabaseBackend extends SQLiteOpenHelper { - + private static DatabaseBackend instance = null; - + private static final String DATABASE_NAME = "history"; private static final int DATABASE_VERSION = 1; @@ -22,7 +23,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { - db.execSQL("create table conversations (uuid TEXT, name TEXT, profilePhotoUri TEXT, accountUuid TEXT, contactJid TEXT, created NUMBER, status NUMBER)"); + db.execSQL("create table " + Conversation.TABLENAME + " (" + + Conversation.UUID + " TEXT, " + Conversation.NAME + " TEXT, " + + Conversation.PHOTO_URI + " TEXT, " + Conversation.ACCOUNT + + " TEXT, " + Conversation.CONTACT + " TEXT, " + + Conversation.CREATED + " NUMBER, " + Conversation.STATUS + + " NUMBER)"); + db.execSQL("create table "+Message.TABLENAME+ "()"); } @Override @@ -30,23 +37,23 @@ public class DatabaseBackend extends SQLiteOpenHelper { // TODO Auto-generated method stub } - + public static synchronized DatabaseBackend getInstance(Context context) { if (instance == null) { instance = new DatabaseBackend(context); } 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 cursor = db.rawQuery( + "select count(uuid) as count from conversations", null); cursor.moveToFirst(); return cursor.getInt(0); } @@ -54,9 +61,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { 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 = ? order by created desc", selectionArgs); - while(cursor.moveToNext()) { + String[] selectionArgs = { "" + status }; + Cursor cursor = db + .rawQuery( + "select * from conversations where status = ? order by created desc", + selectionArgs); + while (cursor.moveToNext()) { 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 97b937c35..4f76e9c12 100644 --- a/src/de/gultsch/chat/ui/ConversationActivity.java +++ b/src/de/gultsch/chat/ui/ConversationActivity.java @@ -9,6 +9,7 @@ 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 de.gultsch.chat.utils.Beautifier; import android.os.Bundle; import android.app.FragmentTransaction; import android.content.Context; @@ -60,6 +61,8 @@ public class ConversationActivity extends XmppActivity { } ((TextView) view.findViewById(R.id.conversation_name)) .setText(getItem(position).getName()); + ((TextView) view.findViewById(R.id.conversation_lastupdate)) + .setText(Beautifier.readableTimeDifference(getItem(position).getCreated())); ((ImageView) view.findViewById(R.id.conversation_image)) .setImageURI(getItem(position).getProfilePhotoUri()); return view; diff --git a/src/de/gultsch/chat/ui/ConversationFragment.java b/src/de/gultsch/chat/ui/ConversationFragment.java index 70ceb07c8..fca5202a7 100644 --- a/src/de/gultsch/chat/ui/ConversationFragment.java +++ b/src/de/gultsch/chat/ui/ConversationFragment.java @@ -2,24 +2,108 @@ package de.gultsch.chat.ui; import de.gultsch.chat.R; import de.gultsch.chat.entities.Conversation; +import de.gultsch.chat.entities.Message; +import de.gultsch.chat.utils.Beautifier; import android.app.Fragment; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import android.provider.ContactsContract.Profile; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; 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 View onCreateView(final LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + String[] mProjection = new String[] + { + Profile._ID, + Profile.PHOTO_THUMBNAIL_URI + }; + Cursor mProfileCursor = getActivity().getContentResolver().query( + Profile.CONTENT_URI, + mProjection , + null, + null, + null); + + mProfileCursor.moveToFirst(); + final Uri profilePicture = Uri.parse(mProfileCursor.getString(1)); + + Log.d("gultsch","found user profile pic "+profilePicture.toString()); + + final View view = inflater.inflate(R.layout.fragment_conversation, container, + false); + ((ImageButton) view.findViewById(R.id.textSendButton)) + .setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + EditText chatMsg = (EditText) view.findViewById(R.id.textinput); + if (chatMsg.getText().length() < 1) return; + Message message = new Message(conversation,chatMsg.getText().toString(), + Message.ENCRYPTION_NONE); + XmppActivity activity = (XmppActivity) getActivity(); + activity.xmppConnectionService.sendMessage(message); + conversation.getMessages().add(message); + chatMsg.setText(""); + + ListView messagesView = (ListView) view.findViewById(R.id.messages_view); + ArrayAdapter adapter = (ArrayAdapter) messagesView.getAdapter(); + adapter.notifyDataSetChanged(); + + messagesView.setSelection(conversation.getMessages().size() -1); + } + }); + + ListView messagesView = (ListView) view + .findViewById(R.id.messages_view); + messagesView.setAdapter(new ArrayAdapter(this.getActivity() + .getApplicationContext(), R.layout.message_sent, + this.conversation.getMessages()) { + + @Override + public View getView(int position, View view, ViewGroup parent) { + Message item = getItem(position); + if ((item.getStatus() != Message.STATUS_RECIEVED) + || (item.getStatus() == Message.STATUS_SEND)) { + view = (View) inflater.inflate(R.layout.message_sent, null); + ((ImageView) view.findViewById(R.id.message_photo)).setImageURI(profilePicture); + } + ((TextView) view.findViewById(R.id.message_body)).setText(item.getBody()); + TextView time = (TextView) view.findViewById(R.id.message_time); + if (item.getStatus() == Message.STATUS_UNSEND) { + time.setTypeface(null, Typeface.ITALIC); + } else { + time.setText(Beautifier.readableTimeDifference(item.getTimeSent())); + } + return view; + } + }); + + return view; + } public Conversation getConversation() { return conversation; diff --git a/src/de/gultsch/chat/ui/XmppActivity.java b/src/de/gultsch/chat/ui/XmppActivity.java index 991730413..c15482ac4 100644 --- a/src/de/gultsch/chat/ui/XmppActivity.java +++ b/src/de/gultsch/chat/ui/XmppActivity.java @@ -10,8 +10,8 @@ import android.content.ServiceConnection; import android.os.IBinder; public abstract class XmppActivity extends Activity { - protected XmppConnectionService xmppConnectionService; - protected boolean xmppConnectionServiceBound = false; + public XmppConnectionService xmppConnectionService; + public boolean xmppConnectionServiceBound = false; protected boolean handledViewIntent = false; protected ServiceConnection mConnection = new ServiceConnection() { diff --git a/src/de/gultsch/chat/utils/Beautifier.java b/src/de/gultsch/chat/utils/Beautifier.java new file mode 100644 index 000000000..43b7acc2c --- /dev/null +++ b/src/de/gultsch/chat/utils/Beautifier.java @@ -0,0 +1,25 @@ +package de.gultsch.chat.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class Beautifier { + public static String readableTimeDifference(long time) { + if (time==0) { + return "just now"; + } + Date date = new Date(time); + long difference = (System.currentTimeMillis() - time) / 1000; + if (difference<60) { + return "just now"; + } else if (difference<60*10) { + return difference / 60 + " min ago"; + } else if (difference<60*60*24) { + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); + return sdf.format(date); + } else { + SimpleDateFormat sdf = new SimpleDateFormat("M/D"); + return sdf.format(date); + } + } +}