1package de.gultsch.chat.ui;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.List;
7
8import org.openintents.openpgp.OpenPgpSignatureResult;
9import org.openintents.openpgp.util.OpenPgpConstants;
10
11import de.gultsch.chat.R;
12import de.gultsch.chat.R.id;
13import de.gultsch.chat.crypto.PgpEngine;
14import de.gultsch.chat.crypto.PgpEngine.UserInputRequiredException;
15import de.gultsch.chat.entities.Account;
16import de.gultsch.chat.entities.Contact;
17import de.gultsch.chat.entities.Conversation;
18import de.gultsch.chat.entities.Message;
19import de.gultsch.chat.utils.UIHelper;
20import android.net.Uri;
21import android.os.Bundle;
22import android.app.AlertDialog;
23import android.app.FragmentTransaction;
24import android.app.NotificationManager;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.IntentSender.SendIntentException;
29import android.graphics.Typeface;
30import android.support.v4.widget.SlidingPaneLayout;
31import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
32import android.util.Log;
33import android.view.KeyEvent;
34import android.view.LayoutInflater;
35import android.view.Menu;
36import android.view.MenuItem;
37import android.view.View;
38import android.view.ViewGroup;
39import android.view.inputmethod.InputMethodManager;
40import android.widget.AdapterView;
41import android.widget.AdapterView.OnItemClickListener;
42import android.widget.ArrayAdapter;
43import android.widget.ListView;
44import android.widget.PopupMenu;
45import android.widget.PopupMenu.OnMenuItemClickListener;
46import android.widget.TextView;
47import android.widget.ImageView;
48
49public class ConversationActivity extends XmppActivity {
50
51 public static final String VIEW_CONVERSATION = "viewConversation";
52 public static final String CONVERSATION = "conversationUuid";
53
54 public static final int REQUEST_SEND_MESSAGE = 0x75441;
55 public static final int REQUEST_DECRYPT_PGP = 0x76783;
56
57 protected SlidingPaneLayout spl;
58
59 private List<Conversation> conversationList = new ArrayList<Conversation>();
60 private Conversation selectedConversation = null;
61 private ListView listView;
62
63 private boolean paneShouldBeOpen = true;
64 private ArrayAdapter<Conversation> listAdapter;
65
66 private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() {
67
68 @Override
69 public void onConversationListChanged() {
70 final Conversation currentConv = getSelectedConversation();
71 conversationList.clear();
72 conversationList.addAll(xmppConnectionService
73 .getConversations());
74 runOnUiThread(new Runnable() {
75
76 @Override
77 public void run() {
78 updateConversationList();
79 if(paneShouldBeOpen) {
80 if (conversationList.size() >= 1) {
81 swapConversationFragment();
82 } else {
83 startActivity(new Intent(getApplicationContext(), NewConversationActivity.class));
84 finish();
85 }
86 }
87 ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
88 if (selectedFragment!=null) {
89 selectedFragment.updateMessages();
90 }
91 }
92 });
93 }
94 };
95
96 private DialogInterface.OnClickListener addToRoster = new DialogInterface.OnClickListener() {
97
98 @Override
99 public void onClick(DialogInterface dialog, int which) {
100 String jid = getSelectedConversation().getContactJid();
101 Account account = getSelectedConversation().getAccount();
102 String name = jid.split("@")[0];
103 Contact contact = new Contact(account, name, jid, null);
104 xmppConnectionService.createContact(contact);
105 }
106 };
107 private boolean contactInserted = false;
108
109
110 public List<Conversation> getConversationList() {
111 return this.conversationList;
112 }
113
114 public Conversation getSelectedConversation() {
115 return this.selectedConversation;
116 }
117
118 public ListView getConversationListView() {
119 return this.listView;
120 }
121
122 public SlidingPaneLayout getSlidingPaneLayout() {
123 return this.spl;
124 }
125
126 public boolean shouldPaneBeOpen() {
127 return paneShouldBeOpen;
128 }
129
130 public void updateConversationList() {
131 if (conversationList.size() >= 1) {
132 Collections.sort(this.conversationList, new Comparator<Conversation>() {
133 @Override
134 public int compare(Conversation lhs, Conversation rhs) {
135 return (int) (rhs.getLatestMessage().getTimeSent() - lhs.getLatestMessage().getTimeSent());
136 }
137 });
138 }
139 this.listView.invalidateViews();
140 }
141
142 @Override
143 protected void onCreate(Bundle savedInstanceState) {
144
145 super.onCreate(savedInstanceState);
146
147 setContentView(R.layout.fragment_conversations_overview);
148
149 listView = (ListView) findViewById(R.id.list);
150
151 this.listAdapter = new ArrayAdapter<Conversation>(this,
152 R.layout.conversation_list_row, conversationList) {
153 @Override
154 public View getView(int position, View view, ViewGroup parent) {
155 if (view == null) {
156 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
157 view = (View) inflater.inflate(
158 R.layout.conversation_list_row, null);
159 }
160 Conversation conv = getItem(position);
161 TextView convName = (TextView) view.findViewById(R.id.conversation_name);
162 convName.setText(conv.getName());
163 TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg);
164 convLastMsg.setText(conv.getLatestMessage().getBody());
165
166 if(!conv.isRead()) {
167 convName.setTypeface(null,Typeface.BOLD);
168 convLastMsg.setTypeface(null,Typeface.BOLD);
169 } else {
170 convName.setTypeface(null,Typeface.NORMAL);
171 convLastMsg.setTypeface(null,Typeface.NORMAL);
172 }
173
174 ((TextView) view.findViewById(R.id.conversation_lastupdate))
175 .setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessage().getTimeSent()));
176
177 Uri profilePhoto = getItem(position).getProfilePhotoUri();
178 ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
179 if (profilePhoto!=null) {
180 imageView.setImageURI(profilePhoto);
181 } else {
182 imageView.setImageBitmap(UIHelper.getUnknownContactPicture(getItem(position).getName(),200));
183 }
184
185
186 ((ImageView) view.findViewById(R.id.conversation_image))
187 .setImageURI(getItem(position).getProfilePhotoUri());
188 return view;
189 }
190
191 };
192
193 listView.setAdapter(this.listAdapter);
194
195 listView.setOnItemClickListener(new OnItemClickListener() {
196
197 @Override
198 public void onItemClick(AdapterView<?> arg0, View clickedView,
199 int position, long arg3) {
200 paneShouldBeOpen = false;
201 if (selectedConversation != conversationList.get(position)) {
202 selectedConversation = conversationList.get(position);
203 swapConversationFragment(); //.onBackendConnected(conversationList.get(position));
204 } else {
205 spl.closePane();
206 }
207 }
208 });
209 spl = (SlidingPaneLayout) findViewById(id.slidingpanelayout);
210 spl.setParallaxDistance(150);
211 spl.setShadowResource(R.drawable.es_slidingpane_shadow);
212 spl.setSliderFadeColor(0);
213 spl.setPanelSlideListener(new PanelSlideListener() {
214
215 @Override
216 public void onPanelOpened(View arg0) {
217 paneShouldBeOpen = true;
218 getActionBar().setDisplayHomeAsUpEnabled(false);
219 getActionBar().setTitle(R.string.app_name);
220 invalidateOptionsMenu();
221
222 InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
223
224 View focus = getCurrentFocus();
225
226 if (focus != null) {
227
228 inputManager.hideSoftInputFromWindow(
229 focus.getWindowToken(),
230 InputMethodManager.HIDE_NOT_ALWAYS);
231 }
232 }
233
234 @Override
235 public void onPanelClosed(View arg0) {
236 paneShouldBeOpen = false;
237 if (conversationList.size() > 0) {
238 getActionBar().setDisplayHomeAsUpEnabled(true);
239 getActionBar().setTitle(getSelectedConversation().getName());
240 invalidateOptionsMenu();
241 if (!getSelectedConversation().isRead()) {
242 getSelectedConversation().markRead();
243 updateConversationList();
244 }
245 }
246 }
247
248 @Override
249 public void onPanelSlide(View arg0, float arg1) {
250 // TODO Auto-generated method stub
251
252 }
253 });
254 }
255
256 @Override
257 public boolean onCreateOptionsMenu(Menu menu) {
258 getMenuInflater().inflate(R.menu.conversations, menu);
259 MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
260
261 if (spl.isOpen()) {
262 ((MenuItem) menu.findItem(R.id.action_archive)).setVisible(false);
263 ((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
264 menuSecure.setVisible(false);
265 } else {
266 ((MenuItem) menu.findItem(R.id.action_add)).setVisible(false);
267 if (this.getSelectedConversation()!=null) {
268 if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
269 ((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
270 menuSecure.setVisible(false);
271 ((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference");
272 } else {
273 if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) {
274 menuSecure.setIcon(R.drawable.ic_action_secure);
275 }
276 }
277 }
278 }
279 return true;
280 }
281
282 @Override
283 public boolean onOptionsItemSelected(MenuItem item) {
284 switch (item.getItemId()) {
285 case android.R.id.home:
286 spl.openPane();
287 break;
288 case R.id.action_settings:
289 startActivity(new Intent(this, SettingsActivity.class));
290 break;
291 case R.id.action_accounts:
292 startActivity(new Intent(this, ManageAccountActivity.class));
293 break;
294 case R.id.action_add:
295 startActivity(new Intent(this, NewConversationActivity.class));
296 break;
297 case R.id.action_archive:
298 Conversation conv = getSelectedConversation();
299 conv.setStatus(Conversation.STATUS_ARCHIVED);
300 paneShouldBeOpen = true;
301 spl.openPane();
302 xmppConnectionService.archiveConversation(conv);
303 selectedConversation = conversationList.get(0);
304 break;
305 case R.id.action_details:
306 DialogContactDetails details = new DialogContactDetails();
307 Contact contact = this.getSelectedConversation().getContact();
308 if (contact != null) {
309 contact.setAccount(this.selectedConversation.getAccount());
310 details.setContact(contact);
311 details.show(getFragmentManager(), "details");
312 } else {
313 String jid = getSelectedConversation().getContactJid();
314 AlertDialog.Builder builder = new AlertDialog.Builder(this);
315 builder.setTitle(jid);
316 builder.setMessage("The contact is not in your roster. Would you like to add it.");
317 builder.setNegativeButton("Cancel", null);
318 builder.setPositiveButton("Add",addToRoster);
319 builder.create().show();
320 }
321 break;
322 case R.id.action_security:
323 final Conversation selConv = getSelectedConversation();
324 View menuItemView = findViewById(R.id.action_security);
325 PopupMenu popup = new PopupMenu(this, menuItemView);
326 final ConversationFragment fragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
327 if (fragment!=null) {
328 popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
329
330 @Override
331 public boolean onMenuItemClick(MenuItem item) {
332 switch (item.getItemId()) {
333 case R.id.encryption_choice_none:
334 selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
335 item.setChecked(true);
336 break;
337 case R.id.encryption_choice_otr:
338 selConv.nextMessageEncryption = Message.ENCRYPTION_OTR;
339 item.setChecked(true);
340 break;
341 case R.id.encryption_choice_pgp:
342 selConv.nextMessageEncryption = Message.ENCRYPTION_PGP;
343 item.setChecked(true);
344 break;
345 default:
346 selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
347 break;
348 }
349 fragment.updateChatMsgHint();
350 return true;
351 }
352 });
353 popup.inflate(R.menu.encryption_choices);
354 switch (selConv.nextMessageEncryption) {
355 case Message.ENCRYPTION_NONE:
356 popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
357 break;
358 case Message.ENCRYPTION_OTR:
359 popup.getMenu().findItem(R.id.encryption_choice_otr).setChecked(true);
360 break;
361 case Message.ENCRYPTION_PGP:
362 popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
363 break;
364 case Message.ENCRYPTION_DECRYPTED:
365 popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
366 break;
367 default:
368 popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
369 break;
370 }
371 popup.show();
372 }
373
374 break;
375 default:
376 break;
377 }
378 return super.onOptionsItemSelected(item);
379 }
380
381 protected ConversationFragment swapConversationFragment() {
382 ConversationFragment selectedFragment = new ConversationFragment();
383
384 FragmentTransaction transaction = getFragmentManager()
385 .beginTransaction();
386 transaction.replace(R.id.selected_conversation, selectedFragment,"conversation");
387 transaction.commit();
388 return selectedFragment;
389 }
390
391 @Override
392 public boolean onKeyDown(int keyCode, KeyEvent event) {
393 if (keyCode == KeyEvent.KEYCODE_BACK) {
394 if (!spl.isOpen()) {
395 spl.openPane();
396 return false;
397 }
398 }
399 return super.onKeyDown(keyCode, event);
400 }
401
402 public void onStart() {
403 super.onStart();
404 NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
405 nm.cancelAll();
406 if (conversationList.size()>=1) {
407 onConvChanged.onConversationListChanged();
408 }
409 }
410
411 @Override
412 protected void onStop() {
413 Log.d("gultsch","called on stop in conversation activity");
414 if (xmppConnectionServiceBound) {
415 xmppConnectionService.removeOnConversationListChangedListener();
416 }
417 super.onStop();
418 }
419
420 @Override
421 void onBackendConnected() {
422
423 xmppConnectionService.setOnConversationListChangedListener(this.onConvChanged);
424
425 if (conversationList.size()==0) {
426 conversationList.clear();
427 conversationList.addAll(xmppConnectionService
428 .getConversations());
429
430 this.updateConversationList();
431 }
432
433 if ((getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
434 if (getIntent().getType().equals(
435 ConversationActivity.VIEW_CONVERSATION)) {
436 handledViewIntent = true;
437
438 String convToView = (String) getIntent().getExtras().get(CONVERSATION);
439
440 for(int i = 0; i < conversationList.size(); ++i) {
441 if (conversationList.get(i).getUuid().equals(convToView)) {
442 selectedConversation = conversationList.get(i);
443 }
444 }
445 paneShouldBeOpen = false;
446 swapConversationFragment();
447 }
448 } else {
449 if (xmppConnectionService.getAccounts().size() == 0) {
450 startActivity(new Intent(this, ManageAccountActivity.class));
451 finish();
452 } else if (conversationList.size() <= 0) {
453 //add no history
454 startActivity(new Intent(this, NewConversationActivity.class));
455 finish();
456 } else {
457 spl.openPane();
458 //find currently loaded fragment
459 ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
460 if (selectedFragment!=null) {
461 Log.d("gultsch","ConversationActivity. found old fragment.");
462 selectedFragment.onBackendConnected();
463 } else {
464 Log.d("gultsch","conversationactivity. no old fragment found. creating new one");
465 selectedConversation = conversationList.get(0);
466 Log.d("gultsch","selected conversation is #"+selectedConversation);
467 swapConversationFragment();
468 }
469 }
470 }
471 }
472 @Override
473 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
474 super.onActivityResult(requestCode, resultCode, data);
475 if (resultCode == RESULT_OK) {
476 if (requestCode == REQUEST_DECRYPT_PGP) {
477 ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
478 if (selectedFragment!=null) {
479 selectedFragment.hidePgpPassphraseBox();
480 }
481 }
482 }
483 }
484}