chat bubbles. yeah

This commit is contained in:
Daniel Gultsch 2014-01-26 03:27:55 +01:00
parent 665ef7511f
commit 898b0ca8c4
14 changed files with 273 additions and 79 deletions

View file

@ -9,6 +9,7 @@
android:targetSdkVersion="19" /> android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

View file

@ -32,29 +32,33 @@ public final class R {
public static final int ic_action_unsecure=0x7f020004; public static final int ic_action_unsecure=0x7f020004;
public static final int ic_launcher=0x7f020005; public static final int ic_launcher=0x7f020005;
public static final int ic_profile=0x7f020006; public static final int ic_profile=0x7f020006;
public static final int section_header=0x7f020007; public static final int message_border=0x7f020007;
public static final int profilemock=0x7f020008;
public static final int section_header=0x7f020009;
} }
public static final class id { public static final class id {
public static final int action_accounts=0x7f0a001b; public static final int action_accounts=0x7f0a001e;
public static final int action_add=0x7f0a0017; public static final int action_add=0x7f0a001a;
public static final int action_archive=0x7f0a001a; public static final int action_archive=0x7f0a001d;
public static final int action_details=0x7f0a0019; public static final int action_details=0x7f0a001c;
public static final int action_security=0x7f0a0018; public static final int action_security=0x7f0a001b;
public static final int action_settings=0x7f0a001c; public static final int action_settings=0x7f0a001f;
public static final int contact_display_name=0x7f0a0009; public static final int contact_display_name=0x7f0a0009;
public static final int contact_divider=0x7f0a000b; public static final int contact_divider=0x7f0a000b;
public static final int contact_jid=0x7f0a000a; public static final int contact_jid=0x7f0a000a;
public static final int contact_photo=0x7f0a0008; public static final int contact_photo=0x7f0a0008;
public static final int conversation_image=0x7f0a000c; public static final int conversation_image=0x7f0a000c;
public static final int conversation_lastmsg=0x7f0a000e; public static final int conversation_lastmsg=0x7f0a000e;
public static final int conversation_lastupdate=0x7f0a000f;
public static final int conversation_name=0x7f0a000d; public static final int conversation_name=0x7f0a000d;
public static final int create_new_contact=0x7f0a0007; public static final int create_new_contact=0x7f0a0007;
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=0x7f0a0005;
public static final int jabber_contacts_header=0x7f0a0004; public static final int jabber_contacts_header=0x7f0a0004;
public static final int list=0x7f0a0015; public static final int list=0x7f0a0015;
public static final int message_body=0x7f0a0018;
public static final int message_photo=0x7f0a0017;
public static final int message_time=0x7f0a0019;
public static final int messages_view=0x7f0a0013;
public static final int new_contact_header=0x7f0a0006; public static final int new_contact_header=0x7f0a0006;
public static final int new_conversation_search=0x7f0a0000; public static final int new_conversation_search=0x7f0a0000;
public static final int phone_contacts=0x7f0a0003; public static final int phone_contacts=0x7f0a0003;
@ -62,7 +66,8 @@ public final class R {
public static final int scrollView1=0x7f0a0001; public static final int scrollView1=0x7f0a0001;
public static final int selected_conversation=0x7f0a0016; public static final int selected_conversation=0x7f0a0016;
public static final int slidingpanelayout=0x7f0a0014; public static final int slidingpanelayout=0x7f0a0014;
public static final int textView1=0x7f0a0013; public static final int textSendButton=0x7f0a0012;
public static final int textinput=0x7f0a0011;
public static final int textsend=0x7f0a0010; public static final int textsend=0x7f0a0010;
} }
public static final class layout { public static final class layout {
@ -71,6 +76,7 @@ public final class R {
public static final int conversation_list_row=0x7f030002; public static final int conversation_list_row=0x7f030002;
public static final int fragment_conversation=0x7f030003; public static final int fragment_conversation=0x7f030003;
public static final int fragment_conversations_overview=0x7f030004; public static final int fragment_conversations_overview=0x7f030004;
public static final int message_sent=0x7f030005;
} }
public static final class menu { public static final class menu {
public static final int conversations=0x7f090000; public static final int conversations=0x7f090000;
@ -84,6 +90,8 @@ public final class R {
public static final int action_secure=0x7f070006; public static final int action_secure=0x7f070006;
public static final int action_settings=0x7f070001; public static final int action_settings=0x7f070001;
public static final int app_name=0x7f070000; public static final int app_name=0x7f070000;
public static final int just_now=0x7f070008;
public static final int sending=0x7f070009;
public static final int title_activity_new_conversation=0x7f070007; public static final int title_activity_new_conversation=0x7f070007;
} }
public static final class style { public static final class style {

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="2dp"/>
<padding android:left="1.5dp" android:right="1.5dp" android:top="1.5dp" android:bottom="1.5dp"/>
<solid android:color="#cecece"/>
</shape>

View file

@ -38,7 +38,7 @@
android:paddingTop="3dp"/> android:paddingTop="3dp"/>
<TextView <TextView
android:id="@+id/duration" android:id="@+id/conversation_lastupdate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/conversation_name" android:layout_alignBaseline="@+id/conversation_name"

View file

@ -1,64 +1,55 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#f9f9f9"> android:background="#e5e5e5" >
<RelativeLayout <RelativeLayout
android:background="#eee"
android:id="@+id/textsend" android:id="@+id/textsend"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" > android:layout_alignParentLeft="true" >
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/imageButton1"
android:ems="10"
android:inputType="textMultiLine"
android:minLines="1"
android:background="#ffffff"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp">
</EditText>
<ImageButton <EditText
android:id="@+id/imageButton1" android:id="@+id/textinput"
android:layout_width="48dp" android:layout_width="wrap_content"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:minHeight="48dp"
android:layout_centerVertical="true" android:layout_alignParentLeft="true"
android:background="?android:selectableItemBackground" android:paddingBottom="12dp"
android:src="@drawable/ic_action_send_now" /> android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
android:layout_toLeftOf="@+id/textSendButton"
android:background="#eee"
android:ems="10"
android:inputType="textMultiLine"
android:minLines="1" >
</EditText>
<ImageButton
android:id="@+id/textSendButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="?android:selectableItemBackground"
android:src="@drawable/ic_action_send_now" />
</RelativeLayout> </RelativeLayout>
<ScrollView <ListView
android:id="@+id/scrollView1" android:id="@+id/messages_view"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="wrap_content"
android:layout_above="@+id/textsend" android:layout_above="@+id/textsend"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" android:background="#e5e5e5"
android:background="#e5e5e5" > tools:listitem="@layout/message_sent"
android:divider="@null"
android:dividerHeight="0dp">
</ListView>
<LinearLayout </RelativeLayout>
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#e5e5e5"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
</ScrollView>
</RelativeLayout>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/message_border"
android:layout_toLeftOf="@+id/message_photo"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:minHeight="48dp"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#ededed"
android:padding="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hi, how are you?"
android:textSize="16sp"
android:id="@+id/message_body"
android:textColor="#333333"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="1dp"
android:text="@string/sending"
android:textColor="#8e8e8e"
android:textSize="12sp"
android:id="@+id/message_time"/>
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/message_photo"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="-1.5dp"
android:padding="0dp"
android:src="@drawable/ic_profile"
android:scaleType="fitXY"/>
</RelativeLayout>

View file

@ -9,4 +9,6 @@
<string name="action_details">Show details</string> <string name="action_details">Show details</string>
<string name="action_secure">Secure conversation</string> <string name="action_secure">Secure conversation</string>
<string name="title_activity_new_conversation">New Conversation</string> <string name="title_activity_new_conversation">New Conversation</string>
<string name="just_now">just now</string>
<string name="sending">sending&#8230;</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
package de.gultsch.chat.entities; package de.gultsch.chat.entities;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import android.content.ContentValues; import android.content.ContentValues;
@ -9,6 +10,9 @@ import android.net.Uri;
public class Conversation extends AbstractEntity { public class Conversation extends AbstractEntity {
private static final long serialVersionUID = -6727528868973996739L; private static final long serialVersionUID = -6727528868973996739L;
public static final String TABLENAME = "conversations";
public static final int STATUS_AVAILABLE = 0; public static final int STATUS_AVAILABLE = 0;
public static final int STATUS_ARCHIVED = 1; public static final int STATUS_ARCHIVED = 1;
public static final int STATUS_DELETED = 2; public static final int STATUS_DELETED = 2;
@ -27,7 +31,7 @@ public class Conversation extends AbstractEntity {
private int status; private int status;
private long created; private long created;
private transient List<Message> messages; private transient List<Message> messages = null;
public Conversation(String name, Uri profilePhoto, Account account, public Conversation(String name, Uri profilePhoto, Account account,
String contactJid) { String contactJid) {
@ -48,6 +52,7 @@ public class Conversation extends AbstractEntity {
} }
public List<Message> getMessages() { public List<Message> getMessages() {
if (messages == null) this.messages = new ArrayList<Message>(); //prevent null pointer
return messages; return messages;
} }
@ -81,6 +86,10 @@ public class Conversation extends AbstractEntity {
public int getStatus() { public int getStatus() {
return this.status; return this.status;
} }
public long getCreated() {
return this.created;
}
public ContentValues getContentValues() { public ContentValues getContentValues() {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();

View file

@ -6,6 +6,8 @@ import android.database.Cursor;
public class Message extends AbstractEntity { public class Message extends AbstractEntity {
private static final long serialVersionUID = 7222081895167103025L; private static final long serialVersionUID = 7222081895167103025L;
public static final String TABLENAME = "messages";
public static final int STATUS_RECIEVED = 0; public static final int STATUS_RECIEVED = 0;
public static final int STATUS_UNSEND = 1; public static final int STATUS_UNSEND = 1;

View file

@ -4,15 +4,16 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.entities.Message;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseBackend extends SQLiteOpenHelper { public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null; private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history"; private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 1; private static final int DATABASE_VERSION = 1;
@ -22,7 +23,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
@Override @Override
public void onCreate(SQLiteDatabase db) { 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 @Override
@ -30,23 +37,23 @@ public class DatabaseBackend extends SQLiteOpenHelper {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
public static synchronized DatabaseBackend getInstance(Context context) { public static synchronized DatabaseBackend getInstance(Context context) {
if (instance == null) { if (instance == null) {
instance = new DatabaseBackend(context); instance = new DatabaseBackend(context);
} }
return instance; return instance;
} }
public void addConversation(Conversation conversation) { public void addConversation(Conversation conversation) {
SQLiteDatabase db = this.getWritableDatabase(); SQLiteDatabase db = this.getWritableDatabase();
db.insert("conversations", null, conversation.getContentValues()); db.insert("conversations", null, conversation.getContentValues());
} }
public int getConversationCount() { public int getConversationCount() {
SQLiteDatabase db = this.getReadableDatabase(); 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(); cursor.moveToFirst();
return cursor.getInt(0); return cursor.getInt(0);
} }
@ -54,9 +61,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<Conversation> getConversations(int status) { public List<Conversation> getConversations(int status) {
List<Conversation> list = new ArrayList<Conversation>(); List<Conversation> list = new ArrayList<Conversation>();
SQLiteDatabase db = this.getReadableDatabase(); SQLiteDatabase db = this.getReadableDatabase();
String[] selectionArgs = {""+status}; String[] selectionArgs = { "" + status };
Cursor cursor = db.rawQuery("select * from conversations where status = ? order by created desc", selectionArgs); Cursor cursor = db
while(cursor.moveToNext()) { .rawQuery(
"select * from conversations where status = ? order by created desc",
selectionArgs);
while (cursor.moveToNext()) {
list.add(Conversation.fromCursor(cursor)); list.add(Conversation.fromCursor(cursor));
} }
return list; return list;

View file

@ -9,6 +9,7 @@ import de.gultsch.chat.entities.Account;
import de.gultsch.chat.entities.Contact; import de.gultsch.chat.entities.Contact;
import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.persistance.DatabaseBackend; import de.gultsch.chat.persistance.DatabaseBackend;
import de.gultsch.chat.utils.Beautifier;
import android.os.Bundle; import android.os.Bundle;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.content.Context; import android.content.Context;
@ -60,6 +61,8 @@ public class ConversationActivity extends XmppActivity {
} }
((TextView) view.findViewById(R.id.conversation_name)) ((TextView) view.findViewById(R.id.conversation_name))
.setText(getItem(position).getName()); .setText(getItem(position).getName());
((TextView) view.findViewById(R.id.conversation_lastupdate))
.setText(Beautifier.readableTimeDifference(getItem(position).getCreated()));
((ImageView) view.findViewById(R.id.conversation_image)) ((ImageView) view.findViewById(R.id.conversation_image))
.setImageURI(getItem(position).getProfilePhotoUri()); .setImageURI(getItem(position).getProfilePhotoUri());
return view; return view;

View file

@ -2,24 +2,108 @@ package de.gultsch.chat.ui;
import de.gultsch.chat.R; import de.gultsch.chat.R;
import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.entities.Message;
import de.gultsch.chat.utils.Beautifier;
import android.app.Fragment; 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.os.Bundle;
import android.provider.ContactsContract.Profile;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup; 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 { public class ConversationFragment extends Fragment {
Conversation conversation; Conversation conversation;
public void setConversation(Conversation conv) { public void setConversation(Conversation conv) {
this.conversation = conv; this.conversation = conv;
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(final LayoutInflater inflater, ViewGroup container,
return inflater.inflate(R.layout.fragment_conversation, container, false); 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<Message> adapter = (ArrayAdapter<Message>) messagesView.getAdapter();
adapter.notifyDataSetChanged();
messagesView.setSelection(conversation.getMessages().size() -1);
}
});
ListView messagesView = (ListView) view
.findViewById(R.id.messages_view);
messagesView.setAdapter(new ArrayAdapter<Message>(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() { public Conversation getConversation() {
return conversation; return conversation;

View file

@ -10,8 +10,8 @@ import android.content.ServiceConnection;
import android.os.IBinder; import android.os.IBinder;
public abstract class XmppActivity extends Activity { public abstract class XmppActivity extends Activity {
protected XmppConnectionService xmppConnectionService; public XmppConnectionService xmppConnectionService;
protected boolean xmppConnectionServiceBound = false; public boolean xmppConnectionServiceBound = false;
protected boolean handledViewIntent = false; protected boolean handledViewIntent = false;
protected ServiceConnection mConnection = new ServiceConnection() { protected ServiceConnection mConnection = new ServiceConnection() {

View file

@ -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);
}
}
}