1package eu.siacs.conversations.ui;
2
3import android.annotation.SuppressLint;
4import android.support.v7.app.AlertDialog;
5import android.app.FragmentTransaction;
6import android.content.ActivityNotFoundException;
7import android.content.Intent;
8import android.net.Uri;
9import android.os.Build;
10import android.os.Bundle;
11import android.provider.Settings;
12import android.support.v4.widget.SlidingPaneLayout;
13import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
14import android.support.v7.app.ActionBar;
15import android.util.Log;
16import android.util.Pair;
17import android.view.KeyEvent;
18import android.view.Menu;
19import android.view.MenuItem;
20import android.view.Surface;
21import android.view.View;
22import android.widget.AdapterView;
23import android.widget.AdapterView.OnItemClickListener;
24import android.widget.ArrayAdapter;
25import android.widget.Toast;
26
27
28import java.util.ArrayList;
29import java.util.List;
30import java.util.concurrent.atomic.AtomicBoolean;
31
32import de.timroes.android.listview.EnhancedListView;
33import eu.siacs.conversations.Config;
34import eu.siacs.conversations.R;
35import eu.siacs.conversations.entities.Account;
36import eu.siacs.conversations.entities.Conversation;
37import eu.siacs.conversations.entities.Message;
38import eu.siacs.conversations.services.XmppConnectionService;
39import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
40import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
41import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
42import eu.siacs.conversations.ui.adapter.ConversationAdapter;
43import eu.siacs.conversations.ui.service.EmojiService;
44import eu.siacs.conversations.utils.ExceptionHelper;
45import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
46import eu.siacs.conversations.xmpp.jid.InvalidJidException;
47import eu.siacs.conversations.xmpp.jid.Jid;
48
49public class ConversationLegacyActivity extends XmppActivity
50 implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast {
51
52 public static final String ACTION_VIEW_CONVERSATION = "eu.siacs.conversations.action.VIEW";
53 public static final String CONVERSATION = "conversationUuid";
54 public static final String EXTRA_DOWNLOAD_UUID = "eu.siacs.conversations.download_uuid";
55 public static final String TEXT = "text";
56 public static final String NICK = "nick";
57 public static final String PRIVATE_MESSAGE = "pm";
58
59 private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
60 private static final String STATE_PANEL_OPEN = "state_panel_open";
61 private static final String STATE_PENDING_URI = "state_pending_uri";
62 private static final String STATE_FIRST_VISIBLE = "first_visible";
63 private static final String STATE_OFFSET_FROM_TOP = "offset_from_top";
64
65 private String mOpenConversation = null;
66 private boolean mPanelOpen = true;
67 private AtomicBoolean mShouldPanelBeOpen = new AtomicBoolean(false);
68 private Pair<Integer, Integer> mScrollPosition = null;
69 private boolean forbidProcessingPendings = false;
70
71 private boolean conversationWasSelectedByKeyboard = false;
72
73 private View mContentView;
74
75 private List<Conversation> conversationList = new ArrayList<>();
76 private Conversation swipedConversation = null;
77 private Conversation mSelectedConversation = null;
78 private EnhancedListView listView;
79 private ConversationFragment mConversationFragment;
80
81 private ArrayAdapter<Conversation> listAdapter;
82
83 private boolean mActivityPaused = false;
84 private AtomicBoolean mRedirected = new AtomicBoolean(false);
85 private boolean mUnprocessedNewIntent = false;
86
87 public Conversation getSelectedConversation() {
88 return this.mSelectedConversation;
89 }
90
91 public void setSelectedConversation(Conversation conversation) {
92 this.mSelectedConversation = conversation;
93 }
94
95 public void showConversationsOverview() {
96 if (mConversationFragment != null) {
97 mConversationFragment.stopScrolling();
98 }
99 if (mContentView instanceof SlidingPaneLayout) {
100 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
101 mShouldPanelBeOpen.set(true);
102 mSlidingPaneLayout.openPane();
103 }
104 }
105
106 @Override
107 protected String getShareableUri() {
108 Conversation conversation = getSelectedConversation();
109 if (conversation != null) {
110 return conversation.getAccount().getShareableUri();
111 } else {
112 return "";
113 }
114 }
115
116 public void hideConversationsOverview() {
117 if (mContentView instanceof SlidingPaneLayout) {
118 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
119 mShouldPanelBeOpen.set(false);
120 mSlidingPaneLayout.closePane();
121 }
122 }
123
124 public boolean isConversationsOverviewHideable() {
125 return mContentView instanceof SlidingPaneLayout;
126 }
127
128 public boolean isConversationsOverviewVisable() {
129 if (mContentView instanceof SlidingPaneLayout) {
130 return mShouldPanelBeOpen.get();
131 } else {
132 return true;
133 }
134 }
135
136 @Override
137 protected void onCreate(final Bundle savedInstanceState) {
138 super.onCreate(savedInstanceState);
139 new EmojiService(this).init();
140 if (savedInstanceState != null) {
141 mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
142 mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
143 int pos = savedInstanceState.getInt(STATE_FIRST_VISIBLE, -1);
144 int offset = savedInstanceState.getInt(STATE_OFFSET_FROM_TOP, 1);
145 if (pos >= 0 && offset <= 0) {
146 Log.d(Config.LOGTAG, "retrieved scroll position from instanceState " + pos + ":" + offset);
147 mScrollPosition = new Pair<>(pos, offset);
148 } else {
149 mScrollPosition = null;
150 }
151 }
152
153 setContentView(R.layout.fragment_conversations_overview);
154
155 this.mConversationFragment = new ConversationFragment();
156 FragmentTransaction transaction = getFragmentManager().beginTransaction();
157 //transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
158 transaction.commit();
159
160 this.listView = findViewById(R.id.list);
161 this.listAdapter = new ConversationAdapter(this, conversationList);
162 this.listView.setAdapter(this.listAdapter);
163 this.listView.setSwipeDirection(EnhancedListView.SwipeDirection.END);
164
165 final ActionBar actionBar = getSupportActionBar();
166 if (actionBar != null) {
167 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
168 }
169
170 listView.setOnItemClickListener(new OnItemClickListener() {
171
172 @Override
173 public void onItemClick(AdapterView<?> arg0, View clickedView,
174 int position, long arg3) {
175 if (getSelectedConversation() != conversationList.get(position)) {
176 ConversationLegacyActivity.this.mConversationFragment.stopScrolling();
177 setSelectedConversation(conversationList.get(position));
178 ConversationLegacyActivity.this.mConversationFragment.reInit(getSelectedConversation());
179 conversationWasSelectedByKeyboard = false;
180 }
181 hideConversationsOverview();
182 openConversation();
183 }
184 });
185
186 listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
187
188 @Override
189 public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
190
191 final int index = listView.getFirstVisiblePosition();
192 View v = listView.getChildAt(0);
193 final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
194
195 try {
196 swipedConversation = listAdapter.getItem(position);
197 } catch (IndexOutOfBoundsException e) {
198 return null;
199 }
200 listAdapter.remove(swipedConversation);
201 xmppConnectionService.markRead(swipedConversation);
202
203 final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
204 if (position == 0 && listAdapter.getCount() == 0) {
205 endConversation(swipedConversation, false, true);
206 return null;
207 } else if (formerlySelected) {
208 setSelectedConversation(listAdapter.getItem(0));
209 ConversationLegacyActivity.this.mConversationFragment
210 .reInit(getSelectedConversation());
211 }
212
213 return new EnhancedListView.Undoable() {
214
215 @Override
216 public void undo() {
217 listAdapter.insert(swipedConversation, position);
218 if (formerlySelected) {
219 setSelectedConversation(swipedConversation);
220 ConversationLegacyActivity.this.mConversationFragment
221 .reInit(getSelectedConversation());
222 }
223 swipedConversation = null;
224 listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
225 }
226
227 @Override
228 public void discard() {
229 if (!swipedConversation.isRead()
230 && swipedConversation.getMode() == Conversation.MODE_SINGLE) {
231 swipedConversation = null;
232 return;
233 }
234 endConversation(swipedConversation, false, false);
235 swipedConversation = null;
236 }
237
238 @Override
239 public String getTitle() {
240 if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
241 return getResources().getString(R.string.title_undo_swipe_out_muc);
242 } else {
243 return getResources().getString(R.string.title_undo_swipe_out_conversation);
244 }
245 }
246 };
247 }
248 });
249 listView.enableSwipeToDismiss();
250 listView.setSwipingLayout(R.id.swipeable_item);
251 listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
252 listView.setUndoHideDelay(5000);
253 listView.setRequireTouchBeforeDismiss(false);
254
255 //mContentView = findViewById(R.id.content_view_spl);
256 if (mContentView == null) {
257 //mContentView = findViewById(R.id.content_view_ll);
258 }
259 if (mContentView instanceof SlidingPaneLayout) {
260 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
261 mSlidingPaneLayout.setShadowResource(R.drawable.es_slidingpane_shadow);
262 mSlidingPaneLayout.setSliderFadeColor(0);
263 mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
264
265 @Override
266 public void onPanelOpened(View arg0) {
267 mShouldPanelBeOpen.set(true);
268 updateActionBarTitle();
269 invalidateOptionsMenu();
270 hideKeyboard();
271 if (xmppConnectionServiceBound) {
272 xmppConnectionService.getNotificationService().setOpenConversation(null);
273 }
274 closeContextMenu();
275 }
276
277 @Override
278 public void onPanelClosed(View arg0) {
279 mShouldPanelBeOpen.set(false);
280 listView.discardUndo();
281 openConversation();
282 }
283
284 @Override
285 public void onPanelSlide(View arg0, float arg1) {
286 // TODO Auto-generated method stub
287
288 }
289 });
290 }
291 }
292
293 @Override
294 public void switchToConversation(Conversation conversation) {
295 setSelectedConversation(conversation);
296 runOnUiThread(() -> {
297 ConversationLegacyActivity.this.mConversationFragment.reInit(getSelectedConversation());
298 openConversation();
299 });
300 }
301
302 private void updateActionBarTitle() {
303 updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
304 }
305
306 private void updateActionBarTitle(boolean titleShouldBeName) {
307 final ActionBar ab = getSupportActionBar();
308 final Conversation conversation = getSelectedConversation();
309 if (ab != null) {
310 if (titleShouldBeName && conversation != null) {
311 if ((ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != ActionBar.DISPLAY_HOME_AS_UP) {
312 ab.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
313 }
314 if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
315 ab.setTitle(conversation.getName());
316 } else {
317 ab.setTitle(conversation.getJid().toBareJid().toString());
318 }
319 } else {
320 if ((ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP) {
321 ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
322 }
323 ab.setTitle(R.string.app_name);
324 }
325 }
326 }
327
328 private void openConversation() {
329 this.updateActionBarTitle();
330 this.invalidateOptionsMenu();
331 if (xmppConnectionServiceBound) {
332 final Conversation conversation = getSelectedConversation();
333 xmppConnectionService.getNotificationService().setOpenConversation(conversation);
334 sendReadMarkerIfNecessary(conversation);
335 }
336 listAdapter.notifyDataSetChanged();
337 }
338
339 public void sendReadMarkerIfNecessary(final Conversation conversation) {
340 if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
341 xmppConnectionService.sendReadMarker(conversation);
342 }
343 }
344
345 @Override
346 public boolean onCreateOptionsMenu(Menu menu) {
347 getMenuInflater().inflate(R.menu.activity_conversations, menu);
348 return super.onCreateOptionsMenu(menu);
349 }
350
351 @Override
352 public boolean onOptionsItemSelected(final MenuItem item) {
353 if (item.getItemId() == android.R.id.home) {
354 showConversationsOverview();
355 return true;
356 } else if (item.getItemId() == R.id.action_add) {
357 startActivity(new Intent(this, StartConversationActivity.class));
358 return true;
359 } else {
360 return super.onOptionsItemSelected(item);
361 }
362 }
363
364 public void endConversation(Conversation conversation) {
365 endConversation(conversation, true, true);
366 }
367
368 public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
369 if (showOverview) {
370 showConversationsOverview();
371 }
372 xmppConnectionService.archiveConversation(conversation);
373 if (reinit) {
374 if (conversationList.size() > 0) {
375 setSelectedConversation(conversationList.get(0));
376 this.mConversationFragment.reInit(getSelectedConversation());
377 } else {
378 setSelectedConversation(null);
379 if (mRedirected.compareAndSet(false, true)) {
380 Intent intent = new Intent(this, StartConversationActivity.class);
381 intent.putExtra("init", true);
382 startActivity(intent);
383 finish();
384 }
385 }
386 }
387 }
388
389 @Override
390 public void onBackPressed() {
391 if (!isConversationsOverviewVisable()) {
392 showConversationsOverview();
393 } else {
394 super.onBackPressed();
395 }
396 }
397
398 @Override
399 public boolean onKeyUp(int key, KeyEvent event) {
400 int rotation = getWindowManager().getDefaultDisplay().getRotation();
401 final int upKey;
402 final int downKey;
403 switch (rotation) {
404 case Surface.ROTATION_90:
405 upKey = KeyEvent.KEYCODE_DPAD_LEFT;
406 downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
407 break;
408 case Surface.ROTATION_180:
409 upKey = KeyEvent.KEYCODE_DPAD_DOWN;
410 downKey = KeyEvent.KEYCODE_DPAD_UP;
411 break;
412 case Surface.ROTATION_270:
413 upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
414 downKey = KeyEvent.KEYCODE_DPAD_LEFT;
415 break;
416 case Surface.ROTATION_0:
417 default:
418 upKey = KeyEvent.KEYCODE_DPAD_UP;
419 downKey = KeyEvent.KEYCODE_DPAD_DOWN;
420 }
421 final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
422 if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
423 toggleConversationsOverview();
424 return true;
425 } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
426 startActivity(new Intent(this, StartConversationActivity.class));
427 return true;
428 } else if (modifier && key == downKey) {
429 if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
430 showConversationsOverview();
431 ;
432 }
433 return selectDownConversation();
434 } else if (modifier && key == upKey) {
435 if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
436 showConversationsOverview();
437 }
438 return selectUpConversation();
439 } else if (modifier && key == KeyEvent.KEYCODE_1) {
440 return openConversationByIndex(0);
441 } else if (modifier && key == KeyEvent.KEYCODE_2) {
442 return openConversationByIndex(1);
443 } else if (modifier && key == KeyEvent.KEYCODE_3) {
444 return openConversationByIndex(2);
445 } else if (modifier && key == KeyEvent.KEYCODE_4) {
446 return openConversationByIndex(3);
447 } else if (modifier && key == KeyEvent.KEYCODE_5) {
448 return openConversationByIndex(4);
449 } else if (modifier && key == KeyEvent.KEYCODE_6) {
450 return openConversationByIndex(5);
451 } else if (modifier && key == KeyEvent.KEYCODE_7) {
452 return openConversationByIndex(6);
453 } else if (modifier && key == KeyEvent.KEYCODE_8) {
454 return openConversationByIndex(7);
455 } else if (modifier && key == KeyEvent.KEYCODE_9) {
456 return openConversationByIndex(8);
457 } else if (modifier && key == KeyEvent.KEYCODE_0) {
458 return openConversationByIndex(9);
459 } else {
460 return super.onKeyUp(key, event);
461 }
462 }
463
464 private void toggleConversationsOverview() {
465 if (isConversationsOverviewVisable()) {
466 hideConversationsOverview();
467 if (mConversationFragment != null) {
468 mConversationFragment.setFocusOnInputField();
469 }
470 } else {
471 showConversationsOverview();
472 }
473 }
474
475 private boolean selectUpConversation() {
476 if (this.mSelectedConversation != null) {
477 int index = this.conversationList.indexOf(this.mSelectedConversation);
478 if (index > 0) {
479 return openConversationByIndex(index - 1);
480 }
481 }
482 return false;
483 }
484
485 private boolean selectDownConversation() {
486 if (this.mSelectedConversation != null) {
487 int index = this.conversationList.indexOf(this.mSelectedConversation);
488 if (index != -1 && index < this.conversationList.size() - 1) {
489 return openConversationByIndex(index + 1);
490 }
491 }
492 return false;
493 }
494
495 private boolean openConversationByIndex(int index) {
496 try {
497 this.conversationWasSelectedByKeyboard = true;
498 this.mConversationFragment.stopScrolling();
499 setSelectedConversation(this.conversationList.get(index));
500 this.mConversationFragment.reInit(getSelectedConversation());
501 if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
502 this.listView.setSelection(index);
503 }
504 openConversation();
505 return true;
506 } catch (IndexOutOfBoundsException e) {
507 return false;
508 }
509 }
510
511 @Override
512 protected void onNewIntent(final Intent intent) {
513 if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
514 mOpenConversation = null;
515 mUnprocessedNewIntent = true;
516 if (xmppConnectionServiceBound) {
517 handleViewConversationIntent(intent);
518 intent.setAction(Intent.ACTION_MAIN);
519 } else {
520 setIntent(intent);
521 }
522 }
523 }
524
525 @Override
526 public void onStart() {
527 super.onStart();
528 this.mRedirected.set(false);
529 if (this.xmppConnectionServiceBound) {
530 this.onBackendConnected();
531 }
532 if (conversationList.size() >= 1) {
533 this.onConversationUpdate();
534 }
535 }
536
537 @Override
538 public void onPause() {
539 listView.discardUndo();
540 super.onPause();
541 this.mActivityPaused = true;
542 }
543
544 @Override
545 public void onResume() {
546 super.onResume();
547 final int theme = findTheme();
548 final boolean usingEnterKey = usingEnterKey();
549 if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
550 recreate();
551 }
552 this.mActivityPaused = false;
553 if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
554 sendReadMarkerIfNecessary(getSelectedConversation());
555 }
556 }
557
558 @Override
559 public void onSaveInstanceState(final Bundle savedInstanceState) {
560 Conversation conversation = getSelectedConversation();
561 if (conversation != null) {
562 savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
563 Pair<Integer, Integer> scrollPosition = mConversationFragment.getScrollPosition();
564 if (scrollPosition != null) {
565 savedInstanceState.putInt(STATE_FIRST_VISIBLE, scrollPosition.first);
566 savedInstanceState.putInt(STATE_OFFSET_FROM_TOP, scrollPosition.second);
567 }
568 } else {
569 savedInstanceState.remove(STATE_OPEN_CONVERSATION);
570 }
571 savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
572 /*if (this.mPendingImageUris.size() >= 1) {
573 Log.d(Config.LOGTAG, "ConversationsActivity.onSaveInstanceState() - saving pending image uri");
574 savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString());
575 } else {
576 savedInstanceState.remove(STATE_PENDING_URI);
577 }*/
578 super.onSaveInstanceState(savedInstanceState);
579 }
580
581 private void clearPending() {
582 mConversationFragment.clearPending();
583 }
584
585 private void redirectToStartConversationActivity(boolean noAnimation) {
586 Account pendingAccount = xmppConnectionService.getPendingAccount();
587 if (pendingAccount == null) {
588 Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
589 startConversationActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
590 if (noAnimation) {
591 startConversationActivity.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
592 }
593 startConversationActivity.putExtra("init", true);
594 startActivity(startConversationActivity);
595 if (noAnimation) {
596 overridePendingTransition(0, 0);
597 }
598 } else {
599 switchToAccount(pendingAccount, true);
600 }
601 }
602
603 @Override
604 void onBackendConnected() {
605 this.xmppConnectionService.getNotificationService().setIsInForeground(true);
606 updateConversationList();
607
608 if (mPendingConferenceInvite != null) {
609 if (mPendingConferenceInvite.execute(this)) {
610 mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
611 mToast.show();
612 }
613 mPendingConferenceInvite = null;
614 }
615
616 final Intent intent = getIntent();
617
618 if (xmppConnectionService.getAccounts().size() == 0) {
619 if (mRedirected.compareAndSet(false, true)) {
620 if (Config.X509_VERIFICATION) {
621 Intent redirectionIntent = new Intent(this, ManageAccountActivity.class);
622 redirectionIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
623 startActivity(redirectionIntent);
624 overridePendingTransition(0, 0);
625 } else if (Config.MAGIC_CREATE_DOMAIN != null) {
626 WelcomeActivity.launch(this);
627 } else {
628 Intent editAccount = new Intent(this, EditAccountActivity.class);
629 editAccount.putExtra("init", true);
630 editAccount.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
631 startActivity(editAccount);
632 overridePendingTransition(0, 0);
633 }
634 }
635 } else if (conversationList.size() <= 0) {
636 if (mRedirected.compareAndSet(false, true)) {
637 redirectToStartConversationActivity(true);
638 }
639 } else if (selectConversationByUuid(mOpenConversation)) {
640 if (mPanelOpen) {
641 showConversationsOverview();
642 } else {
643 if (isConversationsOverviewHideable()) {
644 openConversation();
645 updateActionBarTitle(true);
646 }
647 }
648 if (this.mConversationFragment.reInit(getSelectedConversation())) {
649 Log.d(Config.LOGTAG, "setting scroll position on fragment");
650 this.mConversationFragment.setScrollPosition(mScrollPosition);
651 }
652 mOpenConversation = null;
653 } else if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
654 clearPending();
655 handleViewConversationIntent(intent);
656 intent.setAction(Intent.ACTION_MAIN);
657 } else if (getSelectedConversation() == null) {
658 reInitLatestConversation();
659 } else {
660 this.mConversationFragment.messageListAdapter.updatePreferences();
661 //this.mConversationFragment.messagesView.invalidateViews();
662 this.mConversationFragment.setupIme();
663 }
664
665 mConversationFragment.onBackendConnected();
666
667 /*if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService) && !mRedirected.get()) {
668 openBatteryOptimizationDialogIfNeeded();
669 }*/
670 if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
671 xmppConnectionService.getNotificationService().setOpenConversation(null);
672 } else {
673 xmppConnectionService.getNotificationService().setOpenConversation(getSelectedConversation());
674 }
675 }
676
677 private boolean isStopping() {
678 if (Build.VERSION.SDK_INT >= 17) {
679 return isFinishing() || isDestroyed();
680 } else {
681 return isFinishing();
682 }
683 }
684
685 private void reInitLatestConversation() {
686 showConversationsOverview();
687 clearPending();
688 setSelectedConversation(conversationList.get(0));
689 this.mConversationFragment.reInit(getSelectedConversation());
690 }
691
692 private void handleViewConversationIntent(final Intent intent) {
693 final String uuid = intent.getStringExtra(CONVERSATION);
694 final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
695 final String text = intent.getStringExtra(TEXT);
696 final String nick = intent.getStringExtra(NICK);
697 final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
698 this.mConversationFragment.stopScrolling();
699 if (selectConversationByUuid(uuid)) {
700 this.mConversationFragment.reInit(getSelectedConversation());
701 if (nick != null) {
702 if (pm) {
703 Jid jid = getSelectedConversation().getJid();
704 try {
705 Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
706 this.mConversationFragment.privateMessageWith(next);
707 } catch (final InvalidJidException ignored) {
708 //do nothing
709 }
710 } else {
711 this.mConversationFragment.highlightInConference(nick);
712 }
713 } else {
714 this.mConversationFragment.appendText(text);
715 }
716 hideConversationsOverview();
717 mUnprocessedNewIntent = false;
718 openConversation();
719 if (mContentView instanceof SlidingPaneLayout) {
720 updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
721 }
722 if (downloadUuid != null) {
723 final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
724 if (message != null) {
725 //startDownloadable(message);
726 }
727 }
728 } else {
729 mUnprocessedNewIntent = false;
730 }
731 }
732
733 private boolean selectConversationByUuid(String uuid) {
734 if (uuid == null) {
735 return false;
736 }
737 for (Conversation aConversationList : conversationList) {
738 if (aConversationList.getUuid().equals(uuid)) {
739 setSelectedConversation(aConversationList);
740 return true;
741 }
742 }
743 return false;
744 }
745
746 @Override
747 protected void unregisterListeners() {
748 super.unregisterListeners();
749 xmppConnectionService.getNotificationService().setOpenConversation(null);
750 }
751
752 @Override
753 protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
754 super.onActivityResult(requestCode, resultCode, data);
755 if (resultCode != RESULT_OK) {
756 if (requestCode == REQUEST_BATTERY_OP) {
757 setNeverAskForBatteryOptimizationsAgain();
758 }
759 }
760 }
761
762 private String getBatteryOptimizationPreferenceKey() {
763 @SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
764 return "show_battery_optimization" + (device == null ? "" : device);
765 }
766
767 private void setNeverAskForBatteryOptimizationsAgain() {
768 getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
769 }
770
771 private void openBatteryOptimizationDialogIfNeeded() {
772 if (hasAccountWithoutPush()
773 && isOptimizingBattery()
774 && getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
775 AlertDialog.Builder builder = new AlertDialog.Builder(this);
776 builder.setTitle(R.string.battery_optimizations_enabled);
777 builder.setMessage(R.string.battery_optimizations_enabled_dialog);
778 builder.setPositiveButton(R.string.next, (dialog, which) -> {
779 Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
780 Uri uri = Uri.parse("package:" + getPackageName());
781 intent.setData(uri);
782 try {
783 startActivityForResult(intent, REQUEST_BATTERY_OP);
784 } catch (ActivityNotFoundException e) {
785 Toast.makeText(this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
786 }
787 });
788 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
789 builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
790 }
791 AlertDialog dialog = builder.create();
792 dialog.setCanceledOnTouchOutside(false);
793 dialog.show();
794 }
795 }
796
797 private boolean hasAccountWithoutPush() {
798 for (Account account : xmppConnectionService.getAccounts()) {
799 if (account.getStatus() == Account.State.ONLINE && !xmppConnectionService.getPushManagementService().available(account)) {
800 return true;
801 }
802 }
803 return false;
804 }
805
806 public void updateConversationList() {
807 xmppConnectionService.populateWithOrderedConversations(conversationList);
808 if (!conversationList.contains(mSelectedConversation)) {
809 mSelectedConversation = null;
810 }
811 if (swipedConversation != null) {
812 if (swipedConversation.isRead()) {
813 conversationList.remove(swipedConversation);
814 } else {
815 listView.discardUndo();
816 }
817 }
818 listAdapter.notifyDataSetChanged();
819 }
820
821 @Override
822 protected void refreshUiReal() {
823 updateConversationList();
824 if (conversationList.size() > 0) {
825 if (!this.mConversationFragment.isAdded()) {
826 Log.d(Config.LOGTAG, "fragment NOT added to activity. detached=" + Boolean.toString(mConversationFragment.isDetached()));
827 }
828 if (getSelectedConversation() == null) {
829 reInitLatestConversation();
830 } else {
831 ConversationLegacyActivity.this.mConversationFragment.refresh();
832 updateActionBarTitle();
833 invalidateOptionsMenu();
834 }
835 } else {
836 if (!isStopping() && mRedirected.compareAndSet(false, true)) {
837 redirectToStartConversationActivity(false);
838 }
839 Log.d(Config.LOGTAG, "not updating conversations fragment because conversations list size was 0");
840 }
841 }
842
843 @Override
844 public void onAccountUpdate() {
845 this.refreshUi();
846 }
847
848 @Override
849 public void onConversationUpdate() {
850 this.refreshUi();
851 }
852
853 @Override
854 public void onRosterUpdate() {
855 this.refreshUi();
856 }
857
858 @Override
859 public void OnUpdateBlocklist(Status status) {
860 this.refreshUi();
861 }
862
863 @Override
864 public void onShowErrorToast(final int resId) {
865 runOnUiThread(() -> Toast.makeText(ConversationLegacyActivity.this, resId, Toast.LENGTH_SHORT).show());
866 }
867
868 public boolean highlightSelectedConversations() {
869 return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
870 }
871}