ConversationFragment.java

  1package de.gultsch.chat.ui;
  2
  3import java.io.FileNotFoundException;
  4import java.util.ArrayList;
  5import java.util.HashMap;
  6import java.util.HashSet;
  7import java.util.Hashtable;
  8import java.util.List;
  9import java.util.Set;
 10
 11import net.java.otr4j.OtrException;
 12import net.java.otr4j.session.SessionStatus;
 13
 14import de.gultsch.chat.R;
 15import de.gultsch.chat.entities.Contact;
 16import de.gultsch.chat.entities.Conversation;
 17import de.gultsch.chat.entities.Message;
 18import de.gultsch.chat.services.XmppConnectionService;
 19import de.gultsch.chat.utils.PhoneHelper;
 20import de.gultsch.chat.utils.UIHelper;
 21import android.app.AlertDialog;
 22import android.app.Fragment;
 23import android.content.DialogInterface;
 24import android.content.SharedPreferences;
 25import android.graphics.Bitmap;
 26import android.graphics.BitmapFactory;
 27import android.graphics.Typeface;
 28import android.net.Uri;
 29import android.os.Bundle;
 30import android.preference.PreferenceManager;
 31import android.util.Log;
 32import android.view.LayoutInflater;
 33import android.view.View;
 34import android.view.View.OnClickListener;
 35import android.view.View.OnLayoutChangeListener;
 36import android.view.ViewGroup;
 37import android.widget.ArrayAdapter;
 38import android.widget.EditText;
 39import android.widget.LinearLayout;
 40import android.widget.ListView;
 41import android.widget.ImageButton;
 42import android.widget.ImageView;
 43import android.widget.TextView;
 44
 45public class ConversationFragment extends Fragment {
 46
 47	protected Conversation conversation;
 48	protected ListView messagesView;
 49	protected LayoutInflater inflater;
 50	protected List<Message> messageList = new ArrayList<Message>();
 51	protected ArrayAdapter<Message> messageListAdapter;
 52	protected Contact contact;
 53	protected BitmapCache mBitmapCache = new BitmapCache();
 54
 55	private EditText chatMsg;
 56
 57	private OnClickListener sendMsgListener = new OnClickListener() {
 58
 59		@Override
 60		public void onClick(View v) {
 61			ConversationActivity activity = (ConversationActivity) getActivity();
 62			final XmppConnectionService xmppService = activity.xmppConnectionService;
 63			if (chatMsg.getText().length() < 1)
 64				return;
 65			Message message = new Message(conversation, chatMsg.getText()
 66					.toString(), conversation.nextMessageEncryption);
 67			if (conversation.nextMessageEncryption == Message.ENCRYPTION_OTR) {
 68				sendOtrMessage(message);
 69			} else {
 70				sendPlainTextMessage(message);
 71			}
 72		}
 73	};
 74
 75	public void updateChatMsgHint() {
 76		if (conversation.getMode() == Conversation.MODE_MULTI) {
 77			chatMsg.setHint("Send message to conference");
 78		} else {
 79			switch (conversation.nextMessageEncryption) {
 80			case Message.ENCRYPTION_NONE:
 81				chatMsg.setHint("Send plain text message");
 82				break;
 83			case Message.ENCRYPTION_OTR:
 84				chatMsg.setHint("Send OTR encrypted message");
 85				break;
 86			case Message.ENCRYPTION_PGP:
 87				chatMsg.setHint("Send openPGP encryted messeage");
 88			default:
 89				break;
 90			}
 91		}
 92	}
 93
 94	@Override
 95	public View onCreateView(final LayoutInflater inflater,
 96			ViewGroup container, Bundle savedInstanceState) {
 97
 98		this.inflater = inflater;
 99
100		final View view = inflater.inflate(R.layout.fragment_conversation,
101				container, false);
102		chatMsg = (EditText) view.findViewById(R.id.textinput);
103		ImageButton sendButton = (ImageButton) view
104				.findViewById(R.id.textSendButton);
105		sendButton.setOnClickListener(this.sendMsgListener);
106
107		messagesView = (ListView) view.findViewById(R.id.messages_view);
108
109		SharedPreferences sharedPref = PreferenceManager
110				.getDefaultSharedPreferences(getActivity()
111						.getApplicationContext());
112		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
113				"show_phone_selfcontact_picture", true);
114
115		Bitmap self;
116
117		if (showPhoneSelfContactPicture) {
118			Uri selfiUri = PhoneHelper.getSefliUri(getActivity());
119			try {
120				self = BitmapFactory.decodeStream(getActivity()
121						.getContentResolver().openInputStream(selfiUri));
122			} catch (FileNotFoundException e) {
123				self = UIHelper.getUnknownContactPicture(conversation
124						.getAccount().getJid(), 200);
125			}
126		} else {
127			self = UIHelper.getUnknownContactPicture(conversation.getAccount()
128					.getJid(), 200);
129		}
130
131		final Bitmap selfBitmap = self;
132
133		messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
134				.getApplicationContext(), R.layout.message_sent,
135				this.messageList) {
136
137			private static final int SENT = 0;
138			private static final int RECIEVED = 1;
139
140			@Override
141			public int getViewTypeCount() {
142				return 2;
143			}
144
145			@Override
146			public int getItemViewType(int position) {
147				if (getItem(position).getStatus() == Message.STATUS_RECIEVED) {
148					return RECIEVED;
149				} else {
150					return SENT;
151				}
152			}
153
154			@Override
155			public View getView(int position, View view, ViewGroup parent) {
156				Message item = getItem(position);
157				int type = getItemViewType(position);
158				ViewHolder viewHolder;
159				if (view == null) {
160					viewHolder = new ViewHolder();
161					switch (type) {
162					case SENT:
163						view = (View) inflater.inflate(R.layout.message_sent,
164								null);
165						viewHolder.imageView = (ImageView) view
166								.findViewById(R.id.message_photo);
167						viewHolder.imageView.setImageBitmap(selfBitmap);
168						break;
169					case RECIEVED:
170						viewHolder = new ViewHolder();
171						view = (View) inflater.inflate(
172								R.layout.message_recieved, null);
173						viewHolder.imageView = (ImageView) view
174								.findViewById(R.id.message_photo);
175						if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
176							Uri uri = item.getConversation().getProfilePhotoUri();
177							if (uri != null) {
178								viewHolder.imageView
179										.setImageBitmap(mBitmapCache.get(item
180												.getConversation().getName(), uri));
181							} else {
182								viewHolder.imageView
183										.setImageBitmap(mBitmapCache.get(item
184												.getConversation().getName(), null));
185							}
186						}
187						break;
188					default:
189						viewHolder = null;
190						break;
191					}
192					viewHolder.messageBody = (TextView) view
193							.findViewById(R.id.message_body);
194					viewHolder.time = (TextView) view
195							.findViewById(R.id.message_time);
196					
197					view.setTag(viewHolder);
198				} else {
199					viewHolder = (ViewHolder) view.getTag();
200				}
201				if (type == RECIEVED) {
202					 if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
203						if (item.getCounterpart() != null) {
204							viewHolder.imageView.setImageBitmap(mBitmapCache
205									.get(item.getCounterpart(), null));
206						} else {
207							viewHolder.imageView
208									.setImageBitmap(mBitmapCache.get(item
209											.getConversation().getName(), null));
210						}
211					}
212				}
213				String body = item.getBody();
214				if (body != null) {
215					viewHolder.messageBody.setText(body.trim());
216				}
217				if (item.getStatus() == Message.STATUS_UNSEND) {
218					viewHolder.time.setTypeface(null, Typeface.ITALIC);
219					viewHolder.time.setText("sending\u2026");
220				} else {
221					viewHolder.time.setTypeface(null, Typeface.NORMAL);
222					if ((item.getConversation().getMode() == Conversation.MODE_SINGLE)
223							|| (type != RECIEVED)) {
224						viewHolder.time.setText(UIHelper
225								.readableTimeDifference(item.getTimeSent()));
226					} else {
227						viewHolder.time.setText(item.getCounterpart()
228								+ " \u00B7 "
229								+ UIHelper.readableTimeDifference(item
230										.getTimeSent()));
231					}
232				}
233				return view;
234			}
235		};
236		messagesView.setAdapter(messageListAdapter);
237
238		return view;
239	}
240
241	@Override
242	public void onStart() {
243		super.onStart();
244		final ConversationActivity activity = (ConversationActivity) getActivity();
245
246		if (activity.xmppConnectionServiceBound) {
247			this.conversation = activity.getSelectedConversation();
248			updateMessages();
249			// rendering complete. now go tell activity to close pane
250			if (!activity.shouldPaneBeOpen()) {
251				activity.getSlidingPaneLayout().closePane();
252				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
253				activity.getActionBar().setTitle(conversation.getName());
254				activity.invalidateOptionsMenu();
255				if (!conversation.isRead()) {
256					conversation.markRead();
257					activity.updateConversationList();
258				}
259			}
260		}
261	}
262
263	public void onBackendConnected() {
264		final ConversationActivity activity = (ConversationActivity) getActivity();
265		this.conversation = activity.getSelectedConversation();
266		updateMessages();
267		// rendering complete. now go tell activity to close pane
268		if (!activity.shouldPaneBeOpen()) {
269			activity.getSlidingPaneLayout().closePane();
270			activity.getActionBar().setDisplayHomeAsUpEnabled(true);
271			activity.getActionBar().setTitle(conversation.getName());
272			activity.invalidateOptionsMenu();
273			if (!conversation.isRead()) {
274				conversation.markRead();
275				activity.updateConversationList();
276			}
277		}
278	}
279
280	public void updateMessages() {
281		ConversationActivity activity = (ConversationActivity) getActivity();
282		this.messageList.clear();
283		this.messageList.addAll(this.conversation.getMessages());
284		this.messageListAdapter.notifyDataSetChanged();
285		if (messageList.size() >= 1) {
286			int latestEncryption = this.conversation.getLatestMessage()
287					.getEncryption();
288			conversation.nextMessageEncryption = latestEncryption;
289			makeFingerprintWarning(latestEncryption);
290		}
291		getActivity().invalidateOptionsMenu();
292		updateChatMsgHint();
293		int size = this.messageList.size();
294		if (size >= 1)
295			messagesView.setSelection(size - 1);
296		if (!activity.shouldPaneBeOpen()) {
297			conversation.markRead();
298			activity.updateConversationList();
299		}
300	}
301
302	protected void makeFingerprintWarning(int latestEncryption) {
303		final LinearLayout fingerprintWarning = (LinearLayout) getView()
304				.findViewById(R.id.new_fingerprint);
305		if (conversation.getContact() != null) {
306			Set<String> knownFingerprints = conversation.getContact()
307					.getOtrFingerprints();
308			if ((latestEncryption == Message.ENCRYPTION_OTR)
309					&& (conversation.hasValidOtrSession()
310							&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
311								.contains(conversation.getOtrFingerprint())))) {
312				fingerprintWarning.setVisibility(View.VISIBLE);
313				TextView fingerprint = (TextView) getView().findViewById(
314						R.id.otr_fingerprint);
315				fingerprint.setText(conversation.getOtrFingerprint());
316				fingerprintWarning.setOnClickListener(new OnClickListener() {
317
318					@Override
319					public void onClick(View v) {
320						AlertDialog dialog = UIHelper
321								.getVerifyFingerprintDialog(
322										(ConversationActivity) getActivity(),
323										conversation, fingerprintWarning);
324						dialog.show();
325					}
326				});
327			} else {
328				fingerprintWarning.setVisibility(View.GONE);
329			}
330		} else {
331			fingerprintWarning.setVisibility(View.GONE);
332		}
333	}
334
335	protected void sendPlainTextMessage(Message message) {
336		ConversationActivity activity = (ConversationActivity) getActivity();
337		activity.xmppConnectionService.sendMessage(conversation.getAccount(),
338				message, null);
339		chatMsg.setText("");
340	}
341
342	protected void sendOtrMessage(final Message message) {
343		ConversationActivity activity = (ConversationActivity) getActivity();
344		final XmppConnectionService xmppService = activity.xmppConnectionService;
345		if (conversation.hasValidOtrSession()) {
346			activity.xmppConnectionService.sendMessage(
347					conversation.getAccount(), message, null);
348			chatMsg.setText("");
349		} else {
350			Hashtable<String, Integer> presences;
351			if (conversation.getContact() != null) {
352				presences = conversation.getContact().getPresences();
353			} else {
354				presences = null;
355			}
356			if ((presences != null) && (presences.size() == 0)) {
357				AlertDialog.Builder builder = new AlertDialog.Builder(
358						getActivity());
359				builder.setTitle("Contact is offline");
360				builder.setIconAttribute(android.R.attr.alertDialogIcon);
361				builder.setMessage("Sending OTR encrypted messages to an offline contact is impossible.");
362				builder.setPositiveButton("Send plain text",
363						new DialogInterface.OnClickListener() {
364
365							@Override
366							public void onClick(DialogInterface dialog,
367									int which) {
368								conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
369								message.setEncryption(Message.ENCRYPTION_NONE);
370								xmppService.sendMessage(
371										conversation.getAccount(), message,
372										null);
373								chatMsg.setText("");
374							}
375						});
376				builder.setNegativeButton("Cancel", null);
377				builder.create().show();
378			} else if (presences.size() == 1) {
379				xmppService.sendMessage(conversation.getAccount(), message,
380						(String) presences.keySet().toArray()[0]);
381				chatMsg.setText("");
382			} else {
383				AlertDialog.Builder builder = new AlertDialog.Builder(
384						getActivity());
385				builder.setTitle("Choose Presence");
386				final String[] presencesArray = new String[presences.size()];
387				presences.keySet().toArray(presencesArray);
388				builder.setItems(presencesArray,
389						new DialogInterface.OnClickListener() {
390
391							@Override
392							public void onClick(DialogInterface dialog,
393									int which) {
394								xmppService.sendMessage(
395										conversation.getAccount(), message,
396										presencesArray[which]);
397								chatMsg.setText("");
398							}
399						});
400				builder.create().show();
401			}
402		}
403	}
404
405	private static class ViewHolder {
406
407		protected TextView time;
408		protected TextView messageBody;
409		protected ImageView imageView;
410
411	}
412
413	private class BitmapCache {
414		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
415
416		public Bitmap get(String name, Uri uri) {
417			if (bitmaps.containsKey(name)) {
418				return bitmaps.get(name);
419			} else {
420				Bitmap bm;
421				if (uri != null) {
422					try {
423						bm = BitmapFactory.decodeStream(getActivity()
424								.getContentResolver().openInputStream(uri));
425					} catch (FileNotFoundException e) {
426						bm = UIHelper.getUnknownContactPicture(name, 200);
427					}
428				} else {
429					bm = UIHelper.getUnknownContactPicture(name, 200);
430				}
431				bitmaps.put(name, bm);
432				return bm;
433			}
434		}
435	}
436}