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					switch (type) {
161					case SENT:
162						viewHolder = new ViewHolder();
163						view = (View) inflater.inflate(R.layout.message_sent,
164								null);
165						viewHolder.imageView = (ImageView) view
166								.findViewById(R.id.message_photo);
167						viewHolder.messageBody = (TextView) view
168								.findViewById(R.id.message_body);
169						viewHolder.time = (TextView) view
170								.findViewById(R.id.message_time);
171						view.setTag(viewHolder);
172						break;
173					case RECIEVED:
174						viewHolder = new ViewHolder();
175						view = (View) inflater.inflate(
176								R.layout.message_recieved, null);
177						viewHolder.imageView = (ImageView) view
178								.findViewById(R.id.message_photo);
179						viewHolder.messageBody = (TextView) view
180								.findViewById(R.id.message_body);
181						viewHolder.time = (TextView) view
182								.findViewById(R.id.message_time);
183						view.setTag(viewHolder);
184						break;
185					default:
186						viewHolder = null;
187						break;
188					}
189				} else {
190					viewHolder = (ViewHolder) view.getTag();
191				}
192				if (type == RECIEVED) {
193					if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
194						Uri uri = item.getConversation().getProfilePhotoUri();
195						if (uri != null) {
196							viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getConversation().getName(), uri));
197						} else {
198							viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getConversation().getName(),null));
199						}
200					} else if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
201						if (item.getCounterpart() != null) {
202							viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getCounterpart(),null));
203						} else {
204							viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getConversation().getName(),null));
205						}
206					}
207				} else {
208					viewHolder.imageView.setImageBitmap(selfBitmap);
209				}
210				String body = item.getBody();
211				if (body != null) {
212					viewHolder.messageBody.setText(body.trim());
213				}
214				if (item.getStatus() == Message.STATUS_UNSEND) {
215					viewHolder.time.setTypeface(null, Typeface.ITALIC);
216					viewHolder.time.setText("sending\u2026");
217				} else {
218					viewHolder.time.setTypeface(null, Typeface.NORMAL);
219					if ((item.getConversation().getMode() == Conversation.MODE_SINGLE)
220							|| (type != RECIEVED)) {
221						viewHolder.time.setText(UIHelper
222								.readableTimeDifference(item.getTimeSent()));
223					} else {
224						viewHolder.time.setText(item.getCounterpart()
225								+ " \u00B7 "
226								+ UIHelper.readableTimeDifference(item
227										.getTimeSent()));
228					}
229				}
230				return view;
231			}
232		};
233		messagesView.setAdapter(messageListAdapter);
234
235		return view;
236	}
237
238	@Override
239	public void onStart() {
240		super.onStart();
241		final ConversationActivity activity = (ConversationActivity) getActivity();
242
243		if (activity.xmppConnectionServiceBound) {
244			this.conversation = activity.getSelectedConversation();
245			updateMessages();
246			// rendering complete. now go tell activity to close pane
247			if (!activity.shouldPaneBeOpen()) {
248				activity.getSlidingPaneLayout().closePane();
249				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
250				activity.getActionBar().setTitle(conversation.getName());
251				activity.invalidateOptionsMenu();
252				if (!conversation.isRead()) {
253					conversation.markRead();
254					activity.updateConversationList();
255				}
256			}
257		}
258	}
259
260	public void onBackendConnected() {
261		final ConversationActivity activity = (ConversationActivity) getActivity();
262		this.conversation = activity.getSelectedConversation();
263		updateMessages();
264		// rendering complete. now go tell activity to close pane
265		if (!activity.shouldPaneBeOpen()) {
266			activity.getSlidingPaneLayout().closePane();
267			activity.getActionBar().setDisplayHomeAsUpEnabled(true);
268			activity.getActionBar().setTitle(conversation.getName());
269			activity.invalidateOptionsMenu();
270			if (!conversation.isRead()) {
271				conversation.markRead();
272				activity.updateConversationList();
273			}
274		}
275	}
276
277	public void updateMessages() {
278		ConversationActivity activity = (ConversationActivity) getActivity();
279		this.messageList.clear();
280		this.messageList.addAll(this.conversation.getMessages());
281		this.messageListAdapter.notifyDataSetChanged();
282		if (messageList.size() >= 1) {
283			int latestEncryption = this.conversation.getLatestMessage()
284					.getEncryption();
285			conversation.nextMessageEncryption = latestEncryption;
286			makeFingerprintWarning(latestEncryption);
287		}
288		getActivity().invalidateOptionsMenu();
289		updateChatMsgHint();
290		int size = this.messageList.size();
291		if (size >= 1)
292			messagesView.setSelection(size - 1);
293		if (!activity.shouldPaneBeOpen()) {
294			conversation.markRead();
295			activity.updateConversationList();
296		}
297	}
298
299	protected void makeFingerprintWarning(int latestEncryption) {
300		final LinearLayout fingerprintWarning = (LinearLayout) getView()
301				.findViewById(R.id.new_fingerprint);
302		if (conversation.getContact() != null) {
303			Set<String> knownFingerprints = conversation.getContact()
304					.getOtrFingerprints();
305			if ((latestEncryption == Message.ENCRYPTION_OTR)
306					&& (conversation.hasValidOtrSession()
307							&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
308								.contains(conversation.getOtrFingerprint())))) {
309				fingerprintWarning.setVisibility(View.VISIBLE);
310				TextView fingerprint = (TextView) getView().findViewById(
311						R.id.otr_fingerprint);
312				fingerprint.setText(conversation.getOtrFingerprint());
313				fingerprintWarning.setOnClickListener(new OnClickListener() {
314
315					@Override
316					public void onClick(View v) {
317						AlertDialog dialog = UIHelper
318								.getVerifyFingerprintDialog(
319										(ConversationActivity) getActivity(),
320										conversation, fingerprintWarning);
321						dialog.show();
322					}
323				});
324			} else {
325				fingerprintWarning.setVisibility(View.GONE);
326			}
327		} else {
328			fingerprintWarning.setVisibility(View.GONE);
329		}
330	}
331
332	protected void sendPlainTextMessage(Message message) {
333		ConversationActivity activity = (ConversationActivity) getActivity();
334		activity.xmppConnectionService.sendMessage(conversation.getAccount(),
335				message, null);
336		chatMsg.setText("");
337	}
338
339	protected void sendOtrMessage(final Message message) {
340		ConversationActivity activity = (ConversationActivity) getActivity();
341		final XmppConnectionService xmppService = activity.xmppConnectionService;
342		if (conversation.hasValidOtrSession()) {
343			activity.xmppConnectionService.sendMessage(
344					conversation.getAccount(), message, null);
345			chatMsg.setText("");
346		} else {
347			Hashtable<String, Integer> presences;
348			if (conversation.getContact() != null) {
349				presences = conversation.getContact().getPresences();
350			} else {
351				presences = null;
352			}
353			if ((presences != null) && (presences.size() == 0)) {
354				AlertDialog.Builder builder = new AlertDialog.Builder(
355						getActivity());
356				builder.setTitle("Contact is offline");
357				builder.setIconAttribute(android.R.attr.alertDialogIcon);
358				builder.setMessage("Sending OTR encrypted messages to an offline contact is impossible.");
359				builder.setPositiveButton("Send plain text",
360						new DialogInterface.OnClickListener() {
361
362							@Override
363							public void onClick(DialogInterface dialog,
364									int which) {
365								conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
366								message.setEncryption(Message.ENCRYPTION_NONE);
367								xmppService.sendMessage(
368										conversation.getAccount(), message,
369										null);
370								chatMsg.setText("");
371							}
372						});
373				builder.setNegativeButton("Cancel", null);
374				builder.create().show();
375			} else if (presences.size() == 1) {
376				xmppService.sendMessage(conversation.getAccount(), message,
377						(String) presences.keySet().toArray()[0]);
378				chatMsg.setText("");
379			} else {
380				AlertDialog.Builder builder = new AlertDialog.Builder(
381						getActivity());
382				builder.setTitle("Choose Presence");
383				final String[] presencesArray = new String[presences.size()];
384				presences.keySet().toArray(presencesArray);
385				builder.setItems(presencesArray,
386						new DialogInterface.OnClickListener() {
387
388							@Override
389							public void onClick(DialogInterface dialog,
390									int which) {
391								xmppService.sendMessage(
392										conversation.getAccount(), message,
393										presencesArray[which]);
394								chatMsg.setText("");
395							}
396						});
397				builder.create().show();
398			}
399		}
400	}
401
402	private static class ViewHolder {
403
404		protected TextView time;
405		protected TextView messageBody;
406		protected ImageView imageView;
407
408	}
409	
410	private class BitmapCache {
411		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
412		public Bitmap get(String name, Uri uri) {
413			if (bitmaps.containsKey(name)) {
414				return bitmaps.get(name);
415			} else {
416				Bitmap bm;
417				if (uri!=null) {
418					try {
419						bm = BitmapFactory.decodeStream(getActivity()
420								.getContentResolver().openInputStream(uri));
421					} catch (FileNotFoundException e) {
422						bm = UIHelper.getUnknownContactPicture(name, 200);
423					}
424				} else {
425					bm = UIHelper.getUnknownContactPicture(name, 200);
426				}
427				bitmaps.put(name, bm);
428				return bm;
429			}
430		}
431	}
432}