ConversationFragment.java

  1package eu.siacs.conversations.ui;
  2
  3import java.util.ArrayList;
  4import java.util.HashMap;
  5import java.util.Hashtable;
  6import java.util.LinkedList;
  7import java.util.List;
  8import java.util.Set;
  9
 10import net.java.otr4j.session.SessionStatus;
 11
 12import eu.siacs.conversations.R;
 13import eu.siacs.conversations.crypto.PgpEngine.OpenPgpException;
 14import eu.siacs.conversations.crypto.PgpEngine.UserInputRequiredException;
 15import eu.siacs.conversations.entities.Account;
 16import eu.siacs.conversations.entities.Contact;
 17import eu.siacs.conversations.entities.Conversation;
 18import eu.siacs.conversations.entities.Message;
 19import eu.siacs.conversations.entities.MucOptions;
 20import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
 21import eu.siacs.conversations.services.XmppConnectionService;
 22import eu.siacs.conversations.utils.UIHelper;
 23import android.app.AlertDialog;
 24import android.app.Fragment;
 25import android.content.Context;
 26import android.content.DialogInterface;
 27import android.content.Intent;
 28import android.content.IntentSender;
 29import android.content.SharedPreferences;
 30import android.content.IntentSender.SendIntentException;
 31import android.graphics.Bitmap;
 32import android.graphics.Typeface;
 33import android.os.AsyncTask;
 34import android.os.Bundle;
 35import android.preference.PreferenceManager;
 36import android.util.Log;
 37import android.view.LayoutInflater;
 38import android.view.View;
 39import android.view.View.OnClickListener;
 40import android.view.ViewGroup;
 41import android.widget.ArrayAdapter;
 42import android.widget.EditText;
 43import android.widget.LinearLayout;
 44import android.widget.ListView;
 45import android.widget.ImageButton;
 46import android.widget.ImageView;
 47import android.widget.TextView;
 48import android.widget.Toast;
 49
 50public class ConversationFragment extends Fragment {
 51
 52	protected Conversation conversation;
 53	protected ListView messagesView;
 54	protected LayoutInflater inflater;
 55	protected List<Message> messageList = new ArrayList<Message>();
 56	protected ArrayAdapter<Message> messageListAdapter;
 57	protected Contact contact;
 58	protected BitmapCache mBitmapCache = new BitmapCache();
 59
 60	protected String queuedPqpMessage = null;
 61
 62	private EditText chatMsg;
 63	private String pastedText = null;
 64
 65	protected Bitmap selfBitmap;
 66	
 67	private boolean useSubject = true;
 68
 69	private IntentSender askForPassphraseIntent = null;
 70
 71	private OnClickListener sendMsgListener = new OnClickListener() {
 72
 73		@Override
 74		public void onClick(View v) {
 75			if (chatMsg.getText().length() < 1)
 76				return;
 77			Message message = new Message(conversation, chatMsg.getText()
 78					.toString(), conversation.nextMessageEncryption);
 79			if (conversation.nextMessageEncryption == Message.ENCRYPTION_OTR) {
 80				sendOtrMessage(message);
 81			} else if (conversation.nextMessageEncryption == Message.ENCRYPTION_PGP) {
 82				sendPgpMessage(message);
 83			} else {
 84				sendPlainTextMessage(message);
 85			}
 86		}
 87	};
 88	protected OnClickListener clickToDecryptListener = new OnClickListener() {
 89
 90		@Override
 91		public void onClick(View v) {
 92			if (askForPassphraseIntent != null) {
 93				try {
 94					getActivity().startIntentSenderForResult(
 95							askForPassphraseIntent,
 96							ConversationActivity.REQUEST_DECRYPT_PGP, null, 0,
 97							0, 0);
 98				} catch (SendIntentException e) {
 99					Log.d("xmppService", "couldnt fire intent");
100				}
101			}
102		}
103	};
104
105	private LinearLayout pgpInfo;
106	private LinearLayout mucError;
107	private TextView mucErrorText;
108	private OnClickListener clickToMuc = new OnClickListener() {
109
110		@Override
111		public void onClick(View v) {
112			Intent intent = new Intent(getActivity(), MucDetailsActivity.class);
113			intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC);
114			intent.putExtra("uuid", conversation.getUuid());
115			startActivity(intent);
116		}
117	};
118	private ConversationActivity activity;
119
120	public void hidePgpPassphraseBox() {
121		pgpInfo.setVisibility(View.GONE);
122	}
123
124	public void updateChatMsgHint() {
125		if (conversation.getMode() == Conversation.MODE_MULTI) {
126			chatMsg.setHint("Send message to conference");
127		} else {
128			switch (conversation.nextMessageEncryption) {
129			case Message.ENCRYPTION_NONE:
130				chatMsg.setHint("Send plain text message");
131				break;
132			case Message.ENCRYPTION_OTR:
133				chatMsg.setHint("Send OTR encrypted message");
134				break;
135			case Message.ENCRYPTION_PGP:
136				chatMsg.setHint("Send openPGP encryted messeage");
137				break;
138			case Message.ENCRYPTION_DECRYPTED:
139				chatMsg.setHint("Send openPGP encryted messeage");
140				break;
141			default:
142				break;
143			}
144		}
145	}
146
147	@Override
148	public View onCreateView(final LayoutInflater inflater,
149			ViewGroup container, Bundle savedInstanceState) {
150
151		this.inflater = inflater;
152
153		final View view = inflater.inflate(R.layout.fragment_conversation,
154				container, false);
155		chatMsg = (EditText) view.findViewById(R.id.textinput);
156		
157		if (pastedText!=null) {
158			chatMsg.setText(pastedText);
159		}
160		
161		ImageButton sendButton = (ImageButton) view
162				.findViewById(R.id.textSendButton);
163		sendButton.setOnClickListener(this.sendMsgListener);
164
165		pgpInfo = (LinearLayout) view.findViewById(R.id.pgp_keyentry);
166		pgpInfo.setOnClickListener(clickToDecryptListener);
167		mucError = (LinearLayout) view.findViewById(R.id.muc_error);
168		mucError.setOnClickListener(clickToMuc);
169		mucErrorText = (TextView) view.findViewById(R.id.muc_error_msg);
170
171		messagesView = (ListView) view.findViewById(R.id.messages_view);
172
173		messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
174				.getApplicationContext(), R.layout.message_sent,
175				this.messageList) {
176
177			private static final int SENT = 0;
178			private static final int RECIEVED = 1;
179			private static final int ERROR = 2;
180
181			@Override
182			public int getViewTypeCount() {
183				return 3;
184			}
185
186			@Override
187			public int getItemViewType(int position) {
188				if (getItem(position).getStatus() == Message.STATUS_RECIEVED) {
189					return RECIEVED;
190				} else if (getItem(position).getStatus() == Message.STATUS_ERROR) {
191					return ERROR;
192				} else {
193					return SENT;
194				}
195			}
196
197			@Override
198			public View getView(int position, View view, ViewGroup parent) {
199				Message item = getItem(position);
200				int type = getItemViewType(position);
201				ViewHolder viewHolder;
202				if (view == null) {
203					viewHolder = new ViewHolder();
204					switch (type) {
205					case SENT:
206						view = (View) inflater.inflate(R.layout.message_sent,
207								null);
208						viewHolder.imageView = (ImageView) view
209								.findViewById(R.id.message_photo);
210						viewHolder.imageView.setImageBitmap(selfBitmap);
211						viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
212						viewHolder.image = (ImageView) view.findViewById(R.id.message_image);
213						break;
214					case RECIEVED:
215						view = (View) inflater.inflate(
216								R.layout.message_recieved, null);
217						viewHolder.imageView = (ImageView) view
218								.findViewById(R.id.message_photo);
219						viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
220						if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
221
222							viewHolder.imageView.setImageBitmap(mBitmapCache
223									.get(item.getConversation().getName(useSubject), item
224											.getConversation().getContact(),
225											getActivity()
226													.getApplicationContext()));
227
228						}
229						break;
230					case ERROR:
231						view = (View) inflater.inflate(R.layout.message_error,
232								null);
233						viewHolder.imageView = (ImageView) view
234								.findViewById(R.id.message_photo);
235						viewHolder.imageView.setImageBitmap(mBitmapCache
236								.getError());
237						break;
238					default:
239						viewHolder = null;
240						break;
241					}
242					viewHolder.messageBody = (TextView) view
243							.findViewById(R.id.message_body);
244					viewHolder.time = (TextView) view
245							.findViewById(R.id.message_time);
246					view.setTag(viewHolder);
247				} else {
248					viewHolder = (ViewHolder) view.getTag();
249				}
250				if (type == RECIEVED) {
251					if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
252						if (item.getCounterpart() != null) {
253							viewHolder.imageView.setImageBitmap(mBitmapCache
254									.get(item.getCounterpart(), null,
255											getActivity()
256													.getApplicationContext()));
257						} else {
258							viewHolder.imageView.setImageBitmap(mBitmapCache
259									.get(item.getConversation().getName(useSubject),
260											null, getActivity()
261													.getApplicationContext()));
262						}
263					}
264				}
265				if (item.getType() == Message.TYPE_IMAGE) {
266					viewHolder.image.setVisibility(View.VISIBLE);
267					viewHolder.image.setImageBitmap(activity.xmppConnectionService.getFileBackend().getImageFromMessage(item));
268					viewHolder.messageBody.setVisibility(View.GONE);
269				} else {
270					if (viewHolder.image != null) viewHolder.image.setVisibility(View.GONE);
271					viewHolder.messageBody.setVisibility(View.VISIBLE);
272					String body = item.getBody();
273					if (body != null) {
274						if (item.getEncryption() == Message.ENCRYPTION_PGP) {
275							viewHolder.messageBody
276									.setText(getString(R.string.encrypted_message));
277							viewHolder.messageBody.setTextColor(0xff33B5E5);
278							viewHolder.messageBody.setTypeface(null,
279									Typeface.ITALIC);
280							viewHolder.indicator.setVisibility(View.VISIBLE);
281						} else if ((item.getEncryption() == Message.ENCRYPTION_OTR)||(item.getEncryption() == Message.ENCRYPTION_DECRYPTED)) {
282							viewHolder.messageBody.setText(body.trim());
283							viewHolder.messageBody.setTextColor(0xff000000);
284							viewHolder.messageBody.setTypeface(null,
285									Typeface.NORMAL);
286							viewHolder.indicator.setVisibility(View.VISIBLE);
287						} else {
288							viewHolder.messageBody.setText(body.trim());
289							viewHolder.messageBody.setTextColor(0xff000000);
290							viewHolder.messageBody.setTypeface(null,
291									Typeface.NORMAL);
292							if (item.getStatus() != Message.STATUS_ERROR) {
293								viewHolder.indicator.setVisibility(View.GONE);
294							}
295						}
296					} else {
297						viewHolder.indicator.setVisibility(View.GONE);
298					}
299				}
300				if (item.getStatus() == Message.STATUS_UNSEND) {
301					viewHolder.time.setTypeface(null, Typeface.ITALIC);
302					viewHolder.time.setText("sending\u2026");
303				} else {
304					viewHolder.time.setTypeface(null, Typeface.NORMAL);
305					if ((item.getConversation().getMode() == Conversation.MODE_SINGLE)
306							|| (type != RECIEVED)) {
307						viewHolder.time.setText(UIHelper
308								.readableTimeDifference(item.getTimeSent()));
309					} else {
310						viewHolder.time.setText(item.getCounterpart()
311								+ " \u00B7 "
312								+ UIHelper.readableTimeDifference(item
313										.getTimeSent()));
314					}
315				}
316				return view;
317			}
318		};
319		messagesView.setAdapter(messageListAdapter);
320
321		return view;
322	}
323
324	protected Bitmap findSelfPicture() {
325		SharedPreferences sharedPref = PreferenceManager
326				.getDefaultSharedPreferences(getActivity()
327						.getApplicationContext());
328		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
329				"show_phone_selfcontact_picture", true);
330
331		return UIHelper.getSelfContactPicture(conversation.getAccount(), 200,
332				showPhoneSelfContactPicture, getActivity());
333	}
334
335	@Override
336	public void onStart() {
337		super.onStart();
338		this.activity = (ConversationActivity) getActivity();
339		SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
340		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
341		if (activity.xmppConnectionServiceBound) {
342			this.onBackendConnected();
343		}
344	}
345
346	public void onBackendConnected() {
347		this.conversation = activity.getSelectedConversation();
348		if (this.conversation == null) {
349			return;
350		}
351		this.selfBitmap = findSelfPicture();
352		updateMessages();
353		// rendering complete. now go tell activity to close pane
354		if (activity.getSlidingPaneLayout().isSlideable()) {
355			if (!activity.shouldPaneBeOpen()) {
356				activity.getSlidingPaneLayout().closePane();
357				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
358				activity.getActionBar().setTitle(conversation.getName(useSubject));
359				activity.invalidateOptionsMenu();
360
361			}
362		}
363		if (queuedPqpMessage != null) {
364			this.conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
365			Message message = new Message(conversation, queuedPqpMessage,
366					Message.ENCRYPTION_PGP);
367			sendPgpMessage(message);
368		}
369		if (conversation.getMode() == Conversation.MODE_MULTI) {
370			activity.xmppConnectionService
371					.setOnRenameListener(new OnRenameListener() {
372
373						@Override
374						public void onRename(final boolean success) {
375							activity.xmppConnectionService.updateConversation(conversation);
376							getActivity().runOnUiThread(new Runnable() {
377
378								@Override
379								public void run() {
380									if (success) {
381										Toast.makeText(
382												getActivity(),
383												"Your nickname has been changed",
384												Toast.LENGTH_SHORT).show();
385									} else {
386										Toast.makeText(getActivity(),
387												"Nichname is already in use",
388												Toast.LENGTH_SHORT).show();
389									}
390								}
391							});
392						}
393					});
394		}
395	}
396
397	public void updateMessages() {
398		ConversationActivity activity = (ConversationActivity) getActivity();
399		if (this.conversation != null) {
400			List<Message> encryptedMessages = new LinkedList<Message>();
401			for (Message message : this.conversation.getMessages()) {
402				if (message.getEncryption() == Message.ENCRYPTION_PGP) {
403					encryptedMessages.add(message);
404				}
405			}
406			if (encryptedMessages.size() > 0) {
407				DecryptMessage task = new DecryptMessage();
408				Message[] msgs = new Message[encryptedMessages.size()];
409				task.execute(encryptedMessages.toArray(msgs));
410			}
411			this.messageList.clear();
412			this.messageList.addAll(this.conversation.getMessages());
413			this.messageListAdapter.notifyDataSetChanged();
414			if (conversation.getMode() == Conversation.MODE_SINGLE) {
415				if (messageList.size() >= 1) {
416					int latestEncryption = this.conversation.getLatestMessage()
417							.getEncryption();
418					if (latestEncryption == Message.ENCRYPTION_DECRYPTED) {
419						conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
420					} else {
421						conversation.nextMessageEncryption = latestEncryption;
422					}
423					makeFingerprintWarning(latestEncryption);
424				}
425			} else {
426				if (conversation.getMucOptions().getError() != 0) {
427					mucError.setVisibility(View.VISIBLE);
428					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
429						mucErrorText.setText(getString(R.string.nick_in_use));
430					}
431				} else {
432					mucError.setVisibility(View.GONE);
433				}
434			}
435			getActivity().invalidateOptionsMenu();
436			updateChatMsgHint();
437			int size = this.messageList.size();
438			if (size >= 1)
439				messagesView.setSelection(size - 1);
440			if (!activity.shouldPaneBeOpen()) {
441				conversation.markRead();
442				// TODO update notifications
443				UIHelper.updateNotification(getActivity(),
444						activity.getConversationList(), null, false);
445				activity.updateConversationList();
446			}
447		}
448	}
449
450	protected void makeFingerprintWarning(int latestEncryption) {
451		final LinearLayout fingerprintWarning = (LinearLayout) getView()
452				.findViewById(R.id.new_fingerprint);
453		if (conversation.getContact() != null) {
454			Set<String> knownFingerprints = conversation.getContact()
455					.getOtrFingerprints();
456			if ((latestEncryption == Message.ENCRYPTION_OTR)
457					&& (conversation.hasValidOtrSession()
458							&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
459								.contains(conversation.getOtrFingerprint())))) {
460				fingerprintWarning.setVisibility(View.VISIBLE);
461				TextView fingerprint = (TextView) getView().findViewById(
462						R.id.otr_fingerprint);
463				fingerprint.setText(conversation.getOtrFingerprint());
464				fingerprintWarning.setOnClickListener(new OnClickListener() {
465
466					@Override
467					public void onClick(View v) {
468						AlertDialog dialog = UIHelper
469								.getVerifyFingerprintDialog(
470										(ConversationActivity) getActivity(),
471										conversation, fingerprintWarning);
472						dialog.show();
473					}
474				});
475			} else {
476				fingerprintWarning.setVisibility(View.GONE);
477			}
478		} else {
479			fingerprintWarning.setVisibility(View.GONE);
480		}
481	}
482
483	protected void sendPlainTextMessage(Message message) {
484		ConversationActivity activity = (ConversationActivity) getActivity();
485		activity.xmppConnectionService.sendMessage(message, null);
486		chatMsg.setText("");
487	}
488
489	protected void sendPgpMessage(final Message message) {
490		ConversationActivity activity = (ConversationActivity) getActivity();
491		final XmppConnectionService xmppService = activity.xmppConnectionService;
492		Contact contact = message.getConversation().getContact();
493		Account account = message.getConversation().getAccount();
494		if (activity.hasPgp()) {
495			if (contact.getPgpKeyId() != 0) {
496				try {
497					message.setEncryptedBody(xmppService.getPgpEngine().encrypt(account, contact.getPgpKeyId(), message.getBody()));
498					xmppService.sendMessage(message, null);
499					chatMsg.setText("");
500				} catch (UserInputRequiredException e) {
501					try {
502						getActivity().startIntentSenderForResult(e.getPendingIntent().getIntentSender(),
503								ConversationActivity.REQUEST_SEND_MESSAGE, null, 0,
504								0, 0);
505					} catch (SendIntentException e1) {
506						Log.d("xmppService","failed to start intent to send message");
507					}
508				} catch (OpenPgpException e) {
509					Log.d("xmppService","error encrypting with pgp: "+e.getOpenPgpError().getMessage());
510				}
511			} else {
512				AlertDialog.Builder builder = new AlertDialog.Builder(
513						getActivity());
514				builder.setTitle("No openPGP key found");
515				builder.setIconAttribute(android.R.attr.alertDialogIcon);
516				builder.setMessage("There is no openPGP key assoziated with this contact");
517				builder.setNegativeButton("Cancel", null);
518				builder.setPositiveButton("Send plain text",
519						new DialogInterface.OnClickListener() {
520
521							@Override
522							public void onClick(DialogInterface dialog,
523									int which) {
524								conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
525								message.setEncryption(Message.ENCRYPTION_NONE);
526								xmppService.sendMessage(message, null);
527								chatMsg.setText("");
528							}
529						});
530				builder.create().show();
531			}
532		}
533	}
534
535	protected void sendOtrMessage(final Message message) {
536		ConversationActivity activity = (ConversationActivity) getActivity();
537		final XmppConnectionService xmppService = activity.xmppConnectionService;
538		if (conversation.hasValidOtrSession()) {
539			activity.xmppConnectionService.sendMessage(message, null);
540			chatMsg.setText("");
541		} else {
542			Hashtable<String, Integer> presences;
543			if (conversation.getContact() != null) {
544				presences = conversation.getContact().getPresences();
545			} else {
546				presences = null;
547			}
548			if ((presences == null) || (presences.size() == 0)) {
549				AlertDialog.Builder builder = new AlertDialog.Builder(
550						getActivity());
551				builder.setTitle("Contact is offline");
552				builder.setIconAttribute(android.R.attr.alertDialogIcon);
553				builder.setMessage("Sending OTR encrypted messages to an offline contact is impossible.");
554				builder.setPositiveButton("Send plain text",
555						new DialogInterface.OnClickListener() {
556
557							@Override
558							public void onClick(DialogInterface dialog,
559									int which) {
560								conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
561								message.setEncryption(Message.ENCRYPTION_NONE);
562								xmppService.sendMessage(message, null);
563								chatMsg.setText("");
564							}
565						});
566				builder.setNegativeButton("Cancel", null);
567				builder.create().show();
568			} else if (presences.size() == 1) {
569				xmppService.sendMessage(message, (String) presences.keySet()
570						.toArray()[0]);
571				chatMsg.setText("");
572			} else {
573				AlertDialog.Builder builder = new AlertDialog.Builder(
574						getActivity());
575				builder.setTitle("Choose Presence");
576				final String[] presencesArray = new String[presences.size()];
577				presences.keySet().toArray(presencesArray);
578				builder.setItems(presencesArray,
579						new DialogInterface.OnClickListener() {
580
581							@Override
582							public void onClick(DialogInterface dialog,
583									int which) {
584								xmppService.sendMessage(message,
585										presencesArray[which]);
586								chatMsg.setText("");
587							}
588						});
589				builder.create().show();
590			}
591		}
592	}
593
594	private static class ViewHolder {
595
596		protected ImageView image;
597		protected ImageView indicator;
598		protected TextView time;
599		protected TextView messageBody;
600		protected ImageView imageView;
601
602	}
603
604	private class BitmapCache {
605		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
606		private Bitmap error = null;
607
608		public Bitmap get(String name, Contact contact, Context context) {
609			if (bitmaps.containsKey(name)) {
610				return bitmaps.get(name);
611			} else {
612				Bitmap bm = UIHelper.getContactPicture(contact, name, 200, context);
613				bitmaps.put(name, bm);
614				return bm;
615			}
616		}
617
618		public Bitmap getError() {
619			if (error == null) {
620				error = UIHelper.getErrorPicture(200);
621			}
622			return error;
623		}
624	}
625
626	class DecryptMessage extends AsyncTask<Message, Void, Boolean> {
627
628		@Override
629		protected Boolean doInBackground(Message... params) {
630			final ConversationActivity activity = (ConversationActivity) getActivity();
631			askForPassphraseIntent = null;
632			for (int i = 0; i < params.length; ++i) {
633				if (params[i].getEncryption() == Message.ENCRYPTION_PGP) {
634					String body = params[i].getBody();
635					String decrypted = null;
636					if (activity == null) {
637						return false;
638					} else if (!activity.xmppConnectionServiceBound) {
639						return false;
640					}
641					try {
642						decrypted = activity.xmppConnectionService
643								.getPgpEngine().decrypt(conversation.getAccount(),body);
644					} catch (UserInputRequiredException e) {
645						askForPassphraseIntent = e.getPendingIntent()
646								.getIntentSender();
647						activity.runOnUiThread(new Runnable() {
648
649							@Override
650							public void run() {
651								pgpInfo.setVisibility(View.VISIBLE);
652							}
653						});
654
655						return false;
656
657					} catch (OpenPgpException e) {
658						Log.d("gultsch", "error decrypting pgp");
659					}
660					if (decrypted != null) {
661						params[i].setBody(decrypted);
662						params[i].setEncryption(Message.ENCRYPTION_DECRYPTED);
663						activity.xmppConnectionService.updateMessage(params[i]);
664					}
665					if (activity != null) {
666						activity.runOnUiThread(new Runnable() {
667
668							@Override
669							public void run() {
670								messageListAdapter.notifyDataSetChanged();
671							}
672						});
673					}
674				}
675				if (activity != null) {
676					activity.runOnUiThread(new Runnable() {
677
678						@Override
679						public void run() {
680							activity.updateConversationList();
681						}
682					});
683				}
684			}
685			return true;
686		}
687
688	}
689
690	public void setText(String text) {
691		this.pastedText = text;
692	}
693}