move new activity back to original name

Daniel Gultsch created

Change summary

src/main/AndroidManifest.xml                                             |   2 
src/main/java/eu/siacs/conversations/services/NotificationService.java   |   3 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java        | 911 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java        |   9 
src/main/java/eu/siacs/conversations/ui/ConversationLegacyActivity.java  | 871 
src/main/java/eu/siacs/conversations/ui/ConversationsMainActivity.java   | 245 
src/main/java/eu/siacs/conversations/ui/SettingsActivity.java            |   5 
src/main/java/eu/siacs/conversations/ui/XmppActivity.java                |  34 
src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java |  11 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java      |   2 
src/main/java/eu/siacs/conversations/ui/util/Color.java                  |  48 
src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java         |   1 
src/main/res/layout-w945dp/activity_conversations.xml                    |   4 
src/main/res/layout/fragment_conversations_overview.xml                  |   1 
14 files changed, 1,092 insertions(+), 1,055 deletions(-)

Detailed changes

src/main/AndroidManifest.xml 🔗

@@ -44,7 +44,7 @@
         </receiver>
 
         <activity
-            android:name=".ui.ConversationsMainActivity"
+            android:name=".ui.ConversationActivity"
             android:label="@string/app_name"
             android:launchMode="singleTask"
             android:minWidth="300dp"

src/main/java/eu/siacs/conversations/services/NotificationService.java 🔗

@@ -46,7 +46,6 @@ import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.ui.ConversationActivity;
 import eu.siacs.conversations.ui.ManageAccountActivity;
-import eu.siacs.conversations.ui.SettingsActivity;
 import eu.siacs.conversations.ui.TimePreference;
 import eu.siacs.conversations.utils.GeoHelper;
 import eu.siacs.conversations.utils.UIHelper;
@@ -618,7 +617,7 @@ public class NotificationService {
 	private PendingIntent createContentIntent(final String conversationUuid, final String downloadMessageUuid) {
 		final Intent viewConversationIntent = new Intent(mXmppConnectionService,ConversationActivity.class);
 		viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
-		viewConversationIntent.putExtra(ConversationActivity.CONVERSATION, conversationUuid);
+		viewConversationIntent.putExtra(ConversationActivity.EXTRA_CONVERSATION, conversationUuid);
 		if (downloadMessageUuid != null) {
 			viewConversationIntent.putExtra(ConversationActivity.EXTRA_DOWNLOAD_UUID, downloadMessageUuid);
 			return PendingIntent.getActivity(mXmppConnectionService,

src/main/java/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -1,135 +1,100 @@
+/*
+ * Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 package eu.siacs.conversations.ui;
 
-import android.annotation.SuppressLint;
-import android.support.v7.app.AlertDialog;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
 import android.app.FragmentTransaction;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
+import android.databinding.DataBindingUtil;
 import android.os.Bundle;
-import android.provider.Settings;
-import android.support.v4.widget.SlidingPaneLayout;
-import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
+import android.support.annotation.IdRes;
 import android.support.v7.app.ActionBar;
 import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.Surface;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
 import android.widget.Toast;
 
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import de.timroes.android.listview.EnhancedListView;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.databinding.ActivityConversationsBinding;
 import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
-import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
-import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
-import eu.siacs.conversations.ui.adapter.ConversationAdapter;
+import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
+import eu.siacs.conversations.ui.interfaces.OnConversationRead;
+import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
+import eu.siacs.conversations.ui.interfaces.OnConversationsListItemUpdated;
 import eu.siacs.conversations.ui.service.EmojiService;
-import eu.siacs.conversations.utils.ExceptionHelper;
 import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
-import eu.siacs.conversations.xmpp.jid.Jid;
 
-public class ConversationActivity extends XmppActivity
-		implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast {
+public class ConversationActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead, XmppConnectionService.OnAccountUpdate, XmppConnectionService.OnConversationUpdate, XmppConnectionService.OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast {
 
 	public static final String ACTION_VIEW_CONVERSATION = "eu.siacs.conversations.action.VIEW";
-	public static final String CONVERSATION = "conversationUuid";
+	public static final String EXTRA_CONVERSATION = "conversationUuid";
 	public static final String EXTRA_DOWNLOAD_UUID = "eu.siacs.conversations.download_uuid";
-	public static final String TEXT = "text";
-	public static final String NICK = "nick";
-	public static final String PRIVATE_MESSAGE = "pm";
-
-	private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
-	private static final String STATE_PANEL_OPEN = "state_panel_open";
-	private static final String STATE_PENDING_URI = "state_pending_uri";
-	private static final String STATE_FIRST_VISIBLE = "first_visible";
-	private static final String STATE_OFFSET_FROM_TOP = "offset_from_top";
-
-	private String mOpenConversation = null;
-	private boolean mPanelOpen = true;
-	private AtomicBoolean mShouldPanelBeOpen = new AtomicBoolean(false);
-	private Pair<Integer, Integer> mScrollPosition = null;
-	private boolean forbidProcessingPendings = false;
-
-	private boolean conversationWasSelectedByKeyboard = false;
+	public static final String EXTRA_TEXT = "text";
+	public static final String EXTRA_NICK = "nick";
+	public static final String EXTRA_IS_PRIVATE_MESSAGE = "pm";
 
-	private View mContentView;
 
-	private List<Conversation> conversationList = new ArrayList<>();
-	private Conversation swipedConversation = null;
-	private Conversation mSelectedConversation = null;
-	private EnhancedListView listView;
-	private ConversationFragment mConversationFragment;
+	//secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment
+	private static final @IdRes int[] FRAGMENT_ID_NOTIFICATION_ORDER = {R.id.secondary_fragment, R.id.main_fragment};
 
-	private ArrayAdapter<Conversation> listAdapter;
+	private ActivityConversationsBinding binding;
 
-	private boolean mActivityPaused = false;
-	private AtomicBoolean mRedirected = new AtomicBoolean(false);
-	private boolean mUnprocessedNewIntent = false;
-
-	public Conversation getSelectedConversation() {
-		return this.mSelectedConversation;
-	}
-
-	public void setSelectedConversation(Conversation conversation) {
-		this.mSelectedConversation = conversation;
-	}
-
-	public void showConversationsOverview() {
-		if (mConversationFragment != null) {
-			mConversationFragment.stopScrolling();
-		}
-		if (mContentView instanceof SlidingPaneLayout) {
-			SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
-			mShouldPanelBeOpen.set(true);
-			mSlidingPaneLayout.openPane();
+	@Override
+	protected void refreshUiReal() {
+		for(@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
+			refreshFragment(id);
 		}
 	}
 
 	@Override
-	protected String getShareableUri() {
-		Conversation conversation = getSelectedConversation();
-		if (conversation != null) {
-			return conversation.getAccount().getShareableUri();
-		} else {
-			return "";
+	void onBackendConnected() {
+		for(@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
+			notifyFragmentOfBackendConnected(id);
 		}
+		invalidateActionBarTitle();
 	}
 
-	public void hideConversationsOverview() {
-		if (mContentView instanceof SlidingPaneLayout) {
-			SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
-			mShouldPanelBeOpen.set(false);
-			mSlidingPaneLayout.closePane();
+	private void notifyFragmentOfBackendConnected(@IdRes int id) {
+		final Fragment fragment = getFragmentManager().findFragmentById(id);
+		if (fragment != null && fragment instanceof XmppFragment) {
+			((XmppFragment) fragment).onBackendConnected();
 		}
 	}
 
-	public boolean isConversationsOverviewHideable() {
-		return mContentView instanceof SlidingPaneLayout;
-	}
-
-	public boolean isConversationsOverviewVisable() {
-		if (mContentView instanceof SlidingPaneLayout) {
-			return mShouldPanelBeOpen.get();
-		} else {
-			return true;
+	private void refreshFragment(@IdRes int id) {
+		final Fragment fragment = getFragmentManager().findFragmentById(id);
+		if (fragment != null && fragment instanceof XmppFragment) {
+			((XmppFragment) fragment).refresh();
 		}
 	}
 
@@ -137,209 +102,10 @@ public class ConversationActivity extends XmppActivity
 	protected void onCreate(final Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
 		new EmojiService(this).init();
-		if (savedInstanceState != null) {
-			mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
-			mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
-			int pos = savedInstanceState.getInt(STATE_FIRST_VISIBLE, -1);
-			int offset = savedInstanceState.getInt(STATE_OFFSET_FROM_TOP, 1);
-			if (pos >= 0 && offset <= 0) {
-				Log.d(Config.LOGTAG, "retrieved scroll position from instanceState " + pos + ":" + offset);
-				mScrollPosition = new Pair<>(pos, offset);
-			} else {
-				mScrollPosition = null;
-			}
-		}
-
-		setContentView(R.layout.fragment_conversations_overview);
-
-		this.mConversationFragment = new ConversationFragment();
-		FragmentTransaction transaction = getFragmentManager().beginTransaction();
-		//transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
-		transaction.commit();
-
-		this.listView = findViewById(R.id.list);
-		this.listAdapter = new ConversationAdapter(this, conversationList);
-		this.listView.setAdapter(this.listAdapter);
-		this.listView.setSwipeDirection(EnhancedListView.SwipeDirection.END);
-
-		final ActionBar actionBar = getSupportActionBar();
-		if (actionBar != null) {
-			actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
-		}
-
-		listView.setOnItemClickListener(new OnItemClickListener() {
-
-			@Override
-			public void onItemClick(AdapterView<?> arg0, View clickedView,
-			                        int position, long arg3) {
-				if (getSelectedConversation() != conversationList.get(position)) {
-					ConversationActivity.this.mConversationFragment.stopScrolling();
-					setSelectedConversation(conversationList.get(position));
-					ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
-					conversationWasSelectedByKeyboard = false;
-				}
-				hideConversationsOverview();
-				openConversation();
-			}
-		});
-
-		listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
-
-			@Override
-			public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
-
-				final int index = listView.getFirstVisiblePosition();
-				View v = listView.getChildAt(0);
-				final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
-
-				try {
-					swipedConversation = listAdapter.getItem(position);
-				} catch (IndexOutOfBoundsException e) {
-					return null;
-				}
-				listAdapter.remove(swipedConversation);
-				xmppConnectionService.markRead(swipedConversation);
-
-				final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
-				if (position == 0 && listAdapter.getCount() == 0) {
-					endConversation(swipedConversation, false, true);
-					return null;
-				} else if (formerlySelected) {
-					setSelectedConversation(listAdapter.getItem(0));
-					ConversationActivity.this.mConversationFragment
-							.reInit(getSelectedConversation());
-				}
-
-				return new EnhancedListView.Undoable() {
-
-					@Override
-					public void undo() {
-						listAdapter.insert(swipedConversation, position);
-						if (formerlySelected) {
-							setSelectedConversation(swipedConversation);
-							ConversationActivity.this.mConversationFragment
-									.reInit(getSelectedConversation());
-						}
-						swipedConversation = null;
-						listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
-					}
-
-					@Override
-					public void discard() {
-						if (!swipedConversation.isRead()
-								&& swipedConversation.getMode() == Conversation.MODE_SINGLE) {
-							swipedConversation = null;
-							return;
-						}
-						endConversation(swipedConversation, false, false);
-						swipedConversation = null;
-					}
-
-					@Override
-					public String getTitle() {
-						if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
-							return getResources().getString(R.string.title_undo_swipe_out_muc);
-						} else {
-							return getResources().getString(R.string.title_undo_swipe_out_conversation);
-						}
-					}
-				};
-			}
-		});
-		listView.enableSwipeToDismiss();
-		listView.setSwipingLayout(R.id.swipeable_item);
-		listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
-		listView.setUndoHideDelay(5000);
-		listView.setRequireTouchBeforeDismiss(false);
-
-		//mContentView = findViewById(R.id.content_view_spl);
-		if (mContentView == null) {
-			//mContentView = findViewById(R.id.content_view_ll);
-		}
-		if (mContentView instanceof SlidingPaneLayout) {
-			SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
-			mSlidingPaneLayout.setShadowResource(R.drawable.es_slidingpane_shadow);
-			mSlidingPaneLayout.setSliderFadeColor(0);
-			mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
-
-				@Override
-				public void onPanelOpened(View arg0) {
-					mShouldPanelBeOpen.set(true);
-					updateActionBarTitle();
-					invalidateOptionsMenu();
-					hideKeyboard();
-					if (xmppConnectionServiceBound) {
-						xmppConnectionService.getNotificationService().setOpenConversation(null);
-					}
-					closeContextMenu();
-				}
-
-				@Override
-				public void onPanelClosed(View arg0) {
-					mShouldPanelBeOpen.set(false);
-					listView.discardUndo();
-					openConversation();
-				}
-
-				@Override
-				public void onPanelSlide(View arg0, float arg1) {
-					// TODO Auto-generated method stub
-
-				}
-			});
-		}
-	}
-
-	@Override
-	public void switchToConversation(Conversation conversation) {
-		setSelectedConversation(conversation);
-		runOnUiThread(() -> {
-			ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
-			openConversation();
-		});
-	}
-
-	private void updateActionBarTitle() {
-		updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
-	}
-
-	private void updateActionBarTitle(boolean titleShouldBeName) {
-		final ActionBar ab = getSupportActionBar();
-		final Conversation conversation = getSelectedConversation();
-		if (ab != null) {
-			if (titleShouldBeName && conversation != null) {
-				if ((ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != ActionBar.DISPLAY_HOME_AS_UP) {
-					ab.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
-				}
-				if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
-					ab.setTitle(conversation.getName());
-				} else {
-					ab.setTitle(conversation.getJid().toBareJid().toString());
-				}
-			} else {
-				if ((ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP) {
-					ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
-				}
-				ab.setTitle(R.string.app_name);
-			}
-		}
-	}
-
-	private void openConversation() {
-		this.updateActionBarTitle();
-		this.invalidateOptionsMenu();
-		if (xmppConnectionServiceBound) {
-			final Conversation conversation = getSelectedConversation();
-			xmppConnectionService.getNotificationService().setOpenConversation(conversation);
-			sendReadMarkerIfNecessary(conversation);
-		}
-		listAdapter.notifyDataSetChanged();
-	}
-
-	public void sendReadMarkerIfNecessary(final Conversation conversation) {
-		if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
-			xmppConnectionService.sendReadMarker(conversation);
-		}
+		this.binding = DataBindingUtil.setContentView(this, R.layout.activity_conversations);
+		this.getFragmentManager().addOnBackStackChangedListener(this::invalidateActionBarTitle);
+		this.initializeFragments();
+		this.invalidateActionBarTitle();
 	}
 
 	@Override
@@ -349,495 +115,114 @@ public class ConversationActivity extends XmppActivity
 	}
 
 	@Override
-	public boolean onOptionsItemSelected(final MenuItem item) {
-		if (item.getItemId() == android.R.id.home) {
-			showConversationsOverview();
-			return true;
-		} else if (item.getItemId() == R.id.action_add) {
-			startActivity(new Intent(this, StartConversationActivity.class));
-			return true;
+	public void onConversationSelected(Conversation conversation) {
+		Log.d(Config.LOGTAG, "selected " + conversation.getName());
+		ConversationFragment conversationFragment = (ConversationFragment) getFragmentManager().findFragmentById(R.id.secondary_fragment);
+		final boolean mainNeedsRefresh;
+		if (conversationFragment == null) {
+			mainNeedsRefresh = false;
+			conversationFragment = new ConversationFragment();
+			FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
+			fragmentTransaction.replace(R.id.main_fragment, conversationFragment);
+			fragmentTransaction.addToBackStack(null);
+			fragmentTransaction.commit();
 		} else {
-			return super.onOptionsItemSelected(item);
+			mainNeedsRefresh = true;
 		}
-	}
-
-	public void endConversation(Conversation conversation) {
-		endConversation(conversation, true, true);
-	}
-
-	public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
-		if (showOverview) {
-			showConversationsOverview();
-		}
-		xmppConnectionService.archiveConversation(conversation);
-		if (reinit) {
-			if (conversationList.size() > 0) {
-				setSelectedConversation(conversationList.get(0));
-				this.mConversationFragment.reInit(getSelectedConversation());
-			} else {
-				setSelectedConversation(null);
-				if (mRedirected.compareAndSet(false, true)) {
-					Intent intent = new Intent(this, StartConversationActivity.class);
-					intent.putExtra("init", true);
-					startActivity(intent);
-					finish();
-				}
-			}
+		conversationFragment.reInit(conversation);
+		if (mainNeedsRefresh) {
+			refreshFragment(R.id.main_fragment);
 		}
 	}
 
 	@Override
-	public void onBackPressed() {
-		if (!isConversationsOverviewVisable()) {
-			showConversationsOverview();
-		} else {
-			super.onBackPressed();
-		}
-	}
-
-	@Override
-	public boolean onKeyUp(int key, KeyEvent event) {
-		int rotation = getWindowManager().getDefaultDisplay().getRotation();
-		final int upKey;
-		final int downKey;
-		switch (rotation) {
-			case Surface.ROTATION_90:
-				upKey = KeyEvent.KEYCODE_DPAD_LEFT;
-				downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
-				break;
-			case Surface.ROTATION_180:
-				upKey = KeyEvent.KEYCODE_DPAD_DOWN;
-				downKey = KeyEvent.KEYCODE_DPAD_UP;
-				break;
-			case Surface.ROTATION_270:
-				upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
-				downKey = KeyEvent.KEYCODE_DPAD_LEFT;
+	public boolean onOptionsItemSelected(MenuItem item) {
+		switch (item.getItemId()) {
+			case android.R.id.home:
+				FragmentManager fm = getFragmentManager();
+				if (fm.getBackStackEntryCount() > 0) {
+					fm.popBackStack();
+					return true;
+				}
 				break;
-			case Surface.ROTATION_0:
-			default:
-				upKey = KeyEvent.KEYCODE_DPAD_UP;
-				downKey = KeyEvent.KEYCODE_DPAD_DOWN;
-		}
-		final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
-		if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
-			toggleConversationsOverview();
-			return true;
-		} else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
-			startActivity(new Intent(this, StartConversationActivity.class));
-			return true;
-		} else if (modifier && key == downKey) {
-			if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
-				showConversationsOverview();
-				;
-			}
-			return selectDownConversation();
-		} else if (modifier && key == upKey) {
-			if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
-				showConversationsOverview();
-			}
-			return selectUpConversation();
-		} else if (modifier && key == KeyEvent.KEYCODE_1) {
-			return openConversationByIndex(0);
-		} else if (modifier && key == KeyEvent.KEYCODE_2) {
-			return openConversationByIndex(1);
-		} else if (modifier && key == KeyEvent.KEYCODE_3) {
-			return openConversationByIndex(2);
-		} else if (modifier && key == KeyEvent.KEYCODE_4) {
-			return openConversationByIndex(3);
-		} else if (modifier && key == KeyEvent.KEYCODE_5) {
-			return openConversationByIndex(4);
-		} else if (modifier && key == KeyEvent.KEYCODE_6) {
-			return openConversationByIndex(5);
-		} else if (modifier && key == KeyEvent.KEYCODE_7) {
-			return openConversationByIndex(6);
-		} else if (modifier && key == KeyEvent.KEYCODE_8) {
-			return openConversationByIndex(7);
-		} else if (modifier && key == KeyEvent.KEYCODE_9) {
-			return openConversationByIndex(8);
-		} else if (modifier && key == KeyEvent.KEYCODE_0) {
-			return openConversationByIndex(9);
-		} else {
-			return super.onKeyUp(key, event);
-		}
-	}
-
-	private void toggleConversationsOverview() {
-		if (isConversationsOverviewVisable()) {
-			hideConversationsOverview();
-			if (mConversationFragment != null) {
-				mConversationFragment.setFocusOnInputField();
-			}
-		} else {
-			showConversationsOverview();
-		}
-	}
-
-	private boolean selectUpConversation() {
-		if (this.mSelectedConversation != null) {
-			int index = this.conversationList.indexOf(this.mSelectedConversation);
-			if (index > 0) {
-				return openConversationByIndex(index - 1);
-			}
 		}
-		return false;
+		return super.onOptionsItemSelected(item);
 	}
 
-	private boolean selectDownConversation() {
-		if (this.mSelectedConversation != null) {
-			int index = this.conversationList.indexOf(this.mSelectedConversation);
-			if (index != -1 && index < this.conversationList.size() - 1) {
-				return openConversationByIndex(index + 1);
-			}
-		}
-		return false;
-	}
-
-	private boolean openConversationByIndex(int index) {
-		try {
-			this.conversationWasSelectedByKeyboard = true;
-			this.mConversationFragment.stopScrolling();
-			setSelectedConversation(this.conversationList.get(index));
-			this.mConversationFragment.reInit(getSelectedConversation());
-			if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
-				this.listView.setSelection(index);
-			}
-			openConversation();
-			return true;
-		} catch (IndexOutOfBoundsException e) {
-			return false;
-		}
-	}
-
-	@Override
-	protected void onNewIntent(final Intent intent) {
-		if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
-			mOpenConversation = null;
-			mUnprocessedNewIntent = true;
-			if (xmppConnectionServiceBound) {
-				handleViewConversationIntent(intent);
-				intent.setAction(Intent.ACTION_MAIN);
-			} else {
-				setIntent(intent);
-			}
-		}
-	}
-
-	@Override
-	public void onStart() {
-		super.onStart();
-		this.mRedirected.set(false);
-		if (this.xmppConnectionServiceBound) {
-			this.onBackendConnected();
-		}
-		if (conversationList.size() >= 1) {
-			this.onConversationUpdate();
-		}
-	}
-
-	@Override
-	public void onPause() {
-		listView.discardUndo();
-		super.onPause();
-		this.mActivityPaused = true;
-	}
-
-	@Override
-	public void onResume() {
-		super.onResume();
-		final int theme = findTheme();
-		final boolean usingEnterKey = usingEnterKey();
-		if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
-			recreate();
-		}
-		this.mActivityPaused = false;
-		if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
-			sendReadMarkerIfNecessary(getSelectedConversation());
-		}
-	}
-
-	@Override
-	public void onSaveInstanceState(final Bundle savedInstanceState) {
-		Conversation conversation = getSelectedConversation();
-		if (conversation != null) {
-			savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
-			Pair<Integer, Integer> scrollPosition = mConversationFragment.getScrollPosition();
-			if (scrollPosition != null) {
-				savedInstanceState.putInt(STATE_FIRST_VISIBLE, scrollPosition.first);
-				savedInstanceState.putInt(STATE_OFFSET_FROM_TOP, scrollPosition.second);
-			}
-		} else {
-			savedInstanceState.remove(STATE_OPEN_CONVERSATION);
-		}
-		savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
-		/*if (this.mPendingImageUris.size() >= 1) {
-			Log.d(Config.LOGTAG, "ConversationsActivity.onSaveInstanceState() - saving pending image uri");
-			savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString());
-		} else {
-			savedInstanceState.remove(STATE_PENDING_URI);
-		}*/
-		super.onSaveInstanceState(savedInstanceState);
-	}
-
-	private void clearPending() {
-		mConversationFragment.clearPending();
-	}
-
-	private void redirectToStartConversationActivity(boolean noAnimation) {
-		Account pendingAccount = xmppConnectionService.getPendingAccount();
-		if (pendingAccount == null) {
-			Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
-			startConversationActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-			if (noAnimation) {
-				startConversationActivity.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-			}
-			startConversationActivity.putExtra("init", true);
-			startActivity(startConversationActivity);
-			if (noAnimation) {
-				overridePendingTransition(0, 0);
-			}
-		} else {
-			switchToAccount(pendingAccount, true);
-		}
-	}
-
-	@Override
-	void onBackendConnected() {
-		this.xmppConnectionService.getNotificationService().setIsInForeground(true);
-		updateConversationList();
-
-		if (mPendingConferenceInvite != null) {
-			if (mPendingConferenceInvite.execute(this)) {
-				mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
-				mToast.show();
-			}
-			mPendingConferenceInvite = null;
-		}
-
-		final Intent intent = getIntent();
-
-		if (xmppConnectionService.getAccounts().size() == 0) {
-			if (mRedirected.compareAndSet(false, true)) {
-				if (Config.X509_VERIFICATION) {
-					Intent redirectionIntent = new Intent(this, ManageAccountActivity.class);
-					redirectionIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
-					startActivity(redirectionIntent);
-					overridePendingTransition(0, 0);
-				} else if (Config.MAGIC_CREATE_DOMAIN != null) {
-					WelcomeActivity.launch(this);
-				} else {
-					Intent editAccount = new Intent(this, EditAccountActivity.class);
-					editAccount.putExtra("init", true);
-					editAccount.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
-					startActivity(editAccount);
-					overridePendingTransition(0, 0);
+	private void initializeFragments() {
+		FragmentTransaction transaction = getFragmentManager().beginTransaction();
+		Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
+		Fragment secondaryFragment = getFragmentManager().findFragmentById(R.id.secondary_fragment);
+		if (mainFragment != null) {
+			Log.d(Config.LOGTAG,"initializeFragment(). main fragment exists");
+			if (binding.secondaryFragment != null) {
+				if (mainFragment instanceof ConversationFragment) {
+					Log.d(Config.LOGTAG,"gained secondary fragment. moving...");
+					getFragmentManager().popBackStack();
+					transaction.remove(mainFragment);
+					transaction.commit();
+					getFragmentManager().executePendingTransactions();
+					transaction = getFragmentManager().beginTransaction();
+					transaction.replace(R.id.secondary_fragment, mainFragment);
+					transaction.replace(R.id.main_fragment, new ConversationsOverviewFragment());
+					transaction.commit();
+					return;
 				}
-			}
-		} else if (conversationList.size() <= 0) {
-			if (mRedirected.compareAndSet(false, true)) {
-				redirectToStartConversationActivity(true);
-			}
-		} else if (selectConversationByUuid(mOpenConversation)) {
-			if (mPanelOpen) {
-				showConversationsOverview();
 			} else {
-				if (isConversationsOverviewHideable()) {
-					openConversation();
-					updateActionBarTitle(true);
+				if (secondaryFragment != null && secondaryFragment instanceof ConversationFragment) {
+					Log.d(Config.LOGTAG,"lost secondary fragment. moving...");
+					transaction.remove(secondaryFragment);
+					transaction.commit();
+					getFragmentManager().executePendingTransactions();
+					transaction = getFragmentManager().beginTransaction();
+					transaction.replace(R.id.main_fragment, secondaryFragment);
+					transaction.addToBackStack(null);
+					transaction.commit();
+					return;
 				}
 			}
-			if (this.mConversationFragment.reInit(getSelectedConversation())) {
-				Log.d(Config.LOGTAG, "setting scroll position on fragment");
-				this.mConversationFragment.setScrollPosition(mScrollPosition);
-			}
-			mOpenConversation = null;
-		} else if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
-			clearPending();
-			handleViewConversationIntent(intent);
-			intent.setAction(Intent.ACTION_MAIN);
-		} else if (getSelectedConversation() == null) {
-			reInitLatestConversation();
 		} else {
-			this.mConversationFragment.messageListAdapter.updatePreferences();
-			//this.mConversationFragment.messagesView.invalidateViews();
-			this.mConversationFragment.setupIme();
+			transaction.replace(R.id.main_fragment, new ConversationsOverviewFragment());
 		}
-
-		mConversationFragment.onBackendConnected();
-
-		if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService) && !mRedirected.get()) {
-			openBatteryOptimizationDialogIfNeeded();
-		}
-		if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
-			xmppConnectionService.getNotificationService().setOpenConversation(null);
-		} else {
-			xmppConnectionService.getNotificationService().setOpenConversation(getSelectedConversation());
-		}
-	}
-
-	private boolean isStopping() {
-		if (Build.VERSION.SDK_INT >= 17) {
-			return isFinishing() || isDestroyed();
-		} else {
-			return isFinishing();
+		if (binding.secondaryFragment != null && secondaryFragment == null) {
+			transaction.replace(R.id.secondary_fragment, new ConversationFragment());
 		}
+		transaction.commit();
 	}
 
-	private void reInitLatestConversation() {
-		showConversationsOverview();
-		clearPending();
-		setSelectedConversation(conversationList.get(0));
-		this.mConversationFragment.reInit(getSelectedConversation());
-	}
-
-	private void handleViewConversationIntent(final Intent intent) {
-		final String uuid = intent.getStringExtra(CONVERSATION);
-		final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
-		final String text = intent.getStringExtra(TEXT);
-		final String nick = intent.getStringExtra(NICK);
-		final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
-		this.mConversationFragment.stopScrolling();
-		if (selectConversationByUuid(uuid)) {
-			this.mConversationFragment.reInit(getSelectedConversation());
-			if (nick != null) {
-				if (pm) {
-					Jid jid = getSelectedConversation().getJid();
-					try {
-						Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
-						this.mConversationFragment.privateMessageWith(next);
-					} catch (final InvalidJidException ignored) {
-						//do nothing
-					}
-				} else {
-					this.mConversationFragment.highlightInConference(nick);
-				}
-			} else {
-				this.mConversationFragment.appendText(text);
-			}
-			hideConversationsOverview();
-			mUnprocessedNewIntent = false;
-			openConversation();
-			if (mContentView instanceof SlidingPaneLayout) {
-				updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
-			}
-			if (downloadUuid != null) {
-				final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
-				if (message != null) {
-					//startDownloadable(message);
+	private void invalidateActionBarTitle() {
+		final ActionBar actionBar = getSupportActionBar();
+		if (actionBar != null) {
+			Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
+			if (mainFragment != null && mainFragment instanceof ConversationFragment) {
+				final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
+				if (conversation != null) {
+					actionBar.setTitle(conversation.getName());
+					actionBar.setDisplayHomeAsUpEnabled(true);
+					return;
 				}
 			}
-		} else {
-			mUnprocessedNewIntent = false;
+			actionBar.setTitle(R.string.app_name);
+			actionBar.setDisplayHomeAsUpEnabled(false);
 		}
 	}
 
-	private boolean selectConversationByUuid(String uuid) {
-		if (uuid == null) {
-			return false;
-		}
-		for (Conversation aConversationList : conversationList) {
-			if (aConversationList.getUuid().equals(uuid)) {
-				setSelectedConversation(aConversationList);
-				return true;
-			}
-		}
-		return false;
-	}
-
 	@Override
-	protected void unregisterListeners() {
-		super.unregisterListeners();
-		xmppConnectionService.getNotificationService().setOpenConversation(null);
-	}
+	public void onConversationArchived(Conversation conversation) {
 
-	@Override
-	protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
-		super.onActivityResult(requestCode, resultCode, data);
-		if (resultCode != RESULT_OK) {
-			if (requestCode == REQUEST_BATTERY_OP) {
-				setNeverAskForBatteryOptimizationsAgain();
-			}
-		}
 	}
 
-	private String getBatteryOptimizationPreferenceKey() {
-		@SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
-		return "show_battery_optimization" + (device == null ? "" : device);
-	}
-
-	private void setNeverAskForBatteryOptimizationsAgain() {
-		getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
-	}
-
-	private void openBatteryOptimizationDialogIfNeeded() {
-		if (hasAccountWithoutPush()
-				&& isOptimizingBattery()
-				&& getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
-			AlertDialog.Builder builder = new AlertDialog.Builder(this);
-			builder.setTitle(R.string.battery_optimizations_enabled);
-			builder.setMessage(R.string.battery_optimizations_enabled_dialog);
-			builder.setPositiveButton(R.string.next, (dialog, which) -> {
-				Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
-				Uri uri = Uri.parse("package:" + getPackageName());
-				intent.setData(uri);
-				try {
-					startActivityForResult(intent, REQUEST_BATTERY_OP);
-				} catch (ActivityNotFoundException e) {
-					Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
-				}
-			});
-			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-				builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
-			}
-			AlertDialog dialog = builder.create();
-			dialog.setCanceledOnTouchOutside(false);
-			dialog.show();
-		}
-	}
-
-	private boolean hasAccountWithoutPush() {
-		for (Account account : xmppConnectionService.getAccounts()) {
-			if (account.getStatus() == Account.State.ONLINE && !xmppConnectionService.getPushManagementService().available(account)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	public void updateConversationList() {
-		xmppConnectionService.populateWithOrderedConversations(conversationList);
-		if (!conversationList.contains(mSelectedConversation)) {
-			mSelectedConversation = null;
-		}
-		if (swipedConversation != null) {
-			if (swipedConversation.isRead()) {
-				conversationList.remove(swipedConversation);
-			} else {
-				listView.discardUndo();
-			}
+	@Override
+	public void onConversationsListItemUpdated() {
+		Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment);
+		if (fragment != null && fragment instanceof ConversationsOverviewFragment) {
+			((ConversationsOverviewFragment) fragment).refresh();
 		}
-		listAdapter.notifyDataSetChanged();
 	}
 
 	@Override
-	protected void refreshUiReal() {
-		updateConversationList();
-		if (conversationList.size() > 0) {
-			if (!this.mConversationFragment.isAdded()) {
-				Log.d(Config.LOGTAG, "fragment NOT added to activity. detached=" + Boolean.toString(mConversationFragment.isDetached()));
-			}
-			if (getSelectedConversation() == null) {
-				reInitLatestConversation();
-			} else {
-				ConversationActivity.this.mConversationFragment.refresh();
-				updateActionBarTitle();
-				invalidateOptionsMenu();
-			}
-		} else {
-			if (!isStopping() && mRedirected.compareAndSet(false, true)) {
-				redirectToStartConversationActivity(false);
-			}
-			Log.d(Config.LOGTAG, "not updating conversations fragment because conversations list size was 0");
-		}
+	public void onConversationRead(Conversation conversation) {
+		Log.d(Config.LOGTAG, "read event for " + conversation.getName() + " received");
 	}
 
 	@Override
@@ -856,16 +241,12 @@ public class ConversationActivity extends XmppActivity
 	}
 
 	@Override
-	public void OnUpdateBlocklist(Status status) {
+	public void OnUpdateBlocklist(OnUpdateBlocklist.Status status) {
 		this.refreshUi();
 	}
 
 	@Override
-	public void onShowErrorToast(final int resId) {
-		runOnUiThread(() -> Toast.makeText(ConversationActivity.this, resId, Toast.LENGTH_SHORT).show());
-	}
-
-	public boolean highlightSelectedConversations() {
-		return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
+	public void onShowErrorToast(int resId) {
+		runOnUiThread(() -> Toast.makeText(this, resId, Toast.LENGTH_SHORT).show());
 	}
 }

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -133,7 +133,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
 	private Conversation conversation;
 	private FragmentConversationBinding binding;
 	private Toast messageLoaderToast;
-	private ConversationsMainActivity activity;
+	private ConversationActivity activity;
 
 	private OnClickListener clickToMuc = new OnClickListener() {
 
@@ -719,9 +719,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
 	@Override
 	public void onAttach(Activity activity) {
 		super.onAttach(activity);
-		Log.d(Config.LOGTAG, "onAttach()");
-		if (activity instanceof ConversationsMainActivity) {
-			this.activity = (ConversationsMainActivity) activity;
+		Log.d(Config.LOGTAG, "ConversationFragment.onAttach()");
+		if (activity instanceof ConversationActivity) {
+			this.activity = (ConversationActivity) activity;
 		} else {
 			throw new IllegalStateException("Trying to attach fragment to activity that is not the ConversationActivity");
 		}
@@ -1532,6 +1532,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
 
 	@Override
 	public void onActivityCreated(Bundle savedInstanceState) {
+		Log.d(Config.LOGTAG,"ConversationFragment.onActivityCreated()");
 		super.onActivityCreated(savedInstanceState);
 		if (savedInstanceState == null) {
 			return;

src/main/java/eu/siacs/conversations/ui/ConversationLegacyActivity.java 🔗

@@ -0,0 +1,871 @@
+package eu.siacs.conversations.ui;
+
+import android.annotation.SuppressLint;
+import android.support.v7.app.AlertDialog;
+import android.app.FragmentTransaction;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.widget.SlidingPaneLayout;
+import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
+import android.support.v7.app.ActionBar;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Surface;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import de.timroes.android.listview.EnhancedListView;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
+import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
+import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
+import eu.siacs.conversations.ui.adapter.ConversationAdapter;
+import eu.siacs.conversations.ui.service.EmojiService;
+import eu.siacs.conversations.utils.ExceptionHelper;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class ConversationLegacyActivity extends XmppActivity
+		implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast {
+
+	public static final String ACTION_VIEW_CONVERSATION = "eu.siacs.conversations.action.VIEW";
+	public static final String CONVERSATION = "conversationUuid";
+	public static final String EXTRA_DOWNLOAD_UUID = "eu.siacs.conversations.download_uuid";
+	public static final String TEXT = "text";
+	public static final String NICK = "nick";
+	public static final String PRIVATE_MESSAGE = "pm";
+
+	private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
+	private static final String STATE_PANEL_OPEN = "state_panel_open";
+	private static final String STATE_PENDING_URI = "state_pending_uri";
+	private static final String STATE_FIRST_VISIBLE = "first_visible";
+	private static final String STATE_OFFSET_FROM_TOP = "offset_from_top";
+
+	private String mOpenConversation = null;
+	private boolean mPanelOpen = true;
+	private AtomicBoolean mShouldPanelBeOpen = new AtomicBoolean(false);
+	private Pair<Integer, Integer> mScrollPosition = null;
+	private boolean forbidProcessingPendings = false;
+
+	private boolean conversationWasSelectedByKeyboard = false;
+
+	private View mContentView;
+
+	private List<Conversation> conversationList = new ArrayList<>();
+	private Conversation swipedConversation = null;
+	private Conversation mSelectedConversation = null;
+	private EnhancedListView listView;
+	private ConversationFragment mConversationFragment;
+
+	private ArrayAdapter<Conversation> listAdapter;
+
+	private boolean mActivityPaused = false;
+	private AtomicBoolean mRedirected = new AtomicBoolean(false);
+	private boolean mUnprocessedNewIntent = false;
+
+	public Conversation getSelectedConversation() {
+		return this.mSelectedConversation;
+	}
+
+	public void setSelectedConversation(Conversation conversation) {
+		this.mSelectedConversation = conversation;
+	}
+
+	public void showConversationsOverview() {
+		if (mConversationFragment != null) {
+			mConversationFragment.stopScrolling();
+		}
+		if (mContentView instanceof SlidingPaneLayout) {
+			SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+			mShouldPanelBeOpen.set(true);
+			mSlidingPaneLayout.openPane();
+		}
+	}
+
+	@Override
+	protected String getShareableUri() {
+		Conversation conversation = getSelectedConversation();
+		if (conversation != null) {
+			return conversation.getAccount().getShareableUri();
+		} else {
+			return "";
+		}
+	}
+
+	public void hideConversationsOverview() {
+		if (mContentView instanceof SlidingPaneLayout) {
+			SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+			mShouldPanelBeOpen.set(false);
+			mSlidingPaneLayout.closePane();
+		}
+	}
+
+	public boolean isConversationsOverviewHideable() {
+		return mContentView instanceof SlidingPaneLayout;
+	}
+
+	public boolean isConversationsOverviewVisable() {
+		if (mContentView instanceof SlidingPaneLayout) {
+			return mShouldPanelBeOpen.get();
+		} else {
+			return true;
+		}
+	}
+
+	@Override
+	protected void onCreate(final Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		new EmojiService(this).init();
+		if (savedInstanceState != null) {
+			mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
+			mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
+			int pos = savedInstanceState.getInt(STATE_FIRST_VISIBLE, -1);
+			int offset = savedInstanceState.getInt(STATE_OFFSET_FROM_TOP, 1);
+			if (pos >= 0 && offset <= 0) {
+				Log.d(Config.LOGTAG, "retrieved scroll position from instanceState " + pos + ":" + offset);
+				mScrollPosition = new Pair<>(pos, offset);
+			} else {
+				mScrollPosition = null;
+			}
+		}
+
+		setContentView(R.layout.fragment_conversations_overview);
+
+		this.mConversationFragment = new ConversationFragment();
+		FragmentTransaction transaction = getFragmentManager().beginTransaction();
+		//transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
+		transaction.commit();
+
+		this.listView = findViewById(R.id.list);
+		this.listAdapter = new ConversationAdapter(this, conversationList);
+		this.listView.setAdapter(this.listAdapter);
+		this.listView.setSwipeDirection(EnhancedListView.SwipeDirection.END);
+
+		final ActionBar actionBar = getSupportActionBar();
+		if (actionBar != null) {
+			actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
+		}
+
+		listView.setOnItemClickListener(new OnItemClickListener() {
+
+			@Override
+			public void onItemClick(AdapterView<?> arg0, View clickedView,
+			                        int position, long arg3) {
+				if (getSelectedConversation() != conversationList.get(position)) {
+					ConversationLegacyActivity.this.mConversationFragment.stopScrolling();
+					setSelectedConversation(conversationList.get(position));
+					ConversationLegacyActivity.this.mConversationFragment.reInit(getSelectedConversation());
+					conversationWasSelectedByKeyboard = false;
+				}
+				hideConversationsOverview();
+				openConversation();
+			}
+		});
+
+		listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
+
+			@Override
+			public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
+
+				final int index = listView.getFirstVisiblePosition();
+				View v = listView.getChildAt(0);
+				final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
+
+				try {
+					swipedConversation = listAdapter.getItem(position);
+				} catch (IndexOutOfBoundsException e) {
+					return null;
+				}
+				listAdapter.remove(swipedConversation);
+				xmppConnectionService.markRead(swipedConversation);
+
+				final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
+				if (position == 0 && listAdapter.getCount() == 0) {
+					endConversation(swipedConversation, false, true);
+					return null;
+				} else if (formerlySelected) {
+					setSelectedConversation(listAdapter.getItem(0));
+					ConversationLegacyActivity.this.mConversationFragment
+							.reInit(getSelectedConversation());
+				}
+
+				return new EnhancedListView.Undoable() {
+
+					@Override
+					public void undo() {
+						listAdapter.insert(swipedConversation, position);
+						if (formerlySelected) {
+							setSelectedConversation(swipedConversation);
+							ConversationLegacyActivity.this.mConversationFragment
+									.reInit(getSelectedConversation());
+						}
+						swipedConversation = null;
+						listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
+					}
+
+					@Override
+					public void discard() {
+						if (!swipedConversation.isRead()
+								&& swipedConversation.getMode() == Conversation.MODE_SINGLE) {
+							swipedConversation = null;
+							return;
+						}
+						endConversation(swipedConversation, false, false);
+						swipedConversation = null;
+					}
+
+					@Override
+					public String getTitle() {
+						if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
+							return getResources().getString(R.string.title_undo_swipe_out_muc);
+						} else {
+							return getResources().getString(R.string.title_undo_swipe_out_conversation);
+						}
+					}
+				};
+			}
+		});
+		listView.enableSwipeToDismiss();
+		listView.setSwipingLayout(R.id.swipeable_item);
+		listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
+		listView.setUndoHideDelay(5000);
+		listView.setRequireTouchBeforeDismiss(false);
+
+		//mContentView = findViewById(R.id.content_view_spl);
+		if (mContentView == null) {
+			//mContentView = findViewById(R.id.content_view_ll);
+		}
+		if (mContentView instanceof SlidingPaneLayout) {
+			SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+			mSlidingPaneLayout.setShadowResource(R.drawable.es_slidingpane_shadow);
+			mSlidingPaneLayout.setSliderFadeColor(0);
+			mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
+
+				@Override
+				public void onPanelOpened(View arg0) {
+					mShouldPanelBeOpen.set(true);
+					updateActionBarTitle();
+					invalidateOptionsMenu();
+					hideKeyboard();
+					if (xmppConnectionServiceBound) {
+						xmppConnectionService.getNotificationService().setOpenConversation(null);
+					}
+					closeContextMenu();
+				}
+
+				@Override
+				public void onPanelClosed(View arg0) {
+					mShouldPanelBeOpen.set(false);
+					listView.discardUndo();
+					openConversation();
+				}
+
+				@Override
+				public void onPanelSlide(View arg0, float arg1) {
+					// TODO Auto-generated method stub
+
+				}
+			});
+		}
+	}
+
+	@Override
+	public void switchToConversation(Conversation conversation) {
+		setSelectedConversation(conversation);
+		runOnUiThread(() -> {
+			ConversationLegacyActivity.this.mConversationFragment.reInit(getSelectedConversation());
+			openConversation();
+		});
+	}
+
+	private void updateActionBarTitle() {
+		updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
+	}
+
+	private void updateActionBarTitle(boolean titleShouldBeName) {
+		final ActionBar ab = getSupportActionBar();
+		final Conversation conversation = getSelectedConversation();
+		if (ab != null) {
+			if (titleShouldBeName && conversation != null) {
+				if ((ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != ActionBar.DISPLAY_HOME_AS_UP) {
+					ab.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
+				}
+				if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
+					ab.setTitle(conversation.getName());
+				} else {
+					ab.setTitle(conversation.getJid().toBareJid().toString());
+				}
+			} else {
+				if ((ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP) {
+					ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
+				}
+				ab.setTitle(R.string.app_name);
+			}
+		}
+	}
+
+	private void openConversation() {
+		this.updateActionBarTitle();
+		this.invalidateOptionsMenu();
+		if (xmppConnectionServiceBound) {
+			final Conversation conversation = getSelectedConversation();
+			xmppConnectionService.getNotificationService().setOpenConversation(conversation);
+			sendReadMarkerIfNecessary(conversation);
+		}
+		listAdapter.notifyDataSetChanged();
+	}
+
+	public void sendReadMarkerIfNecessary(final Conversation conversation) {
+		if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
+			xmppConnectionService.sendReadMarker(conversation);
+		}
+	}
+
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		getMenuInflater().inflate(R.menu.activity_conversations, menu);
+		return super.onCreateOptionsMenu(menu);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		if (item.getItemId() == android.R.id.home) {
+			showConversationsOverview();
+			return true;
+		} else if (item.getItemId() == R.id.action_add) {
+			startActivity(new Intent(this, StartConversationActivity.class));
+			return true;
+		} else {
+			return super.onOptionsItemSelected(item);
+		}
+	}
+
+	public void endConversation(Conversation conversation) {
+		endConversation(conversation, true, true);
+	}
+
+	public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
+		if (showOverview) {
+			showConversationsOverview();
+		}
+		xmppConnectionService.archiveConversation(conversation);
+		if (reinit) {
+			if (conversationList.size() > 0) {
+				setSelectedConversation(conversationList.get(0));
+				this.mConversationFragment.reInit(getSelectedConversation());
+			} else {
+				setSelectedConversation(null);
+				if (mRedirected.compareAndSet(false, true)) {
+					Intent intent = new Intent(this, StartConversationActivity.class);
+					intent.putExtra("init", true);
+					startActivity(intent);
+					finish();
+				}
+			}
+		}
+	}
+
+	@Override
+	public void onBackPressed() {
+		if (!isConversationsOverviewVisable()) {
+			showConversationsOverview();
+		} else {
+			super.onBackPressed();
+		}
+	}
+
+	@Override
+	public boolean onKeyUp(int key, KeyEvent event) {
+		int rotation = getWindowManager().getDefaultDisplay().getRotation();
+		final int upKey;
+		final int downKey;
+		switch (rotation) {
+			case Surface.ROTATION_90:
+				upKey = KeyEvent.KEYCODE_DPAD_LEFT;
+				downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+				break;
+			case Surface.ROTATION_180:
+				upKey = KeyEvent.KEYCODE_DPAD_DOWN;
+				downKey = KeyEvent.KEYCODE_DPAD_UP;
+				break;
+			case Surface.ROTATION_270:
+				upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+				downKey = KeyEvent.KEYCODE_DPAD_LEFT;
+				break;
+			case Surface.ROTATION_0:
+			default:
+				upKey = KeyEvent.KEYCODE_DPAD_UP;
+				downKey = KeyEvent.KEYCODE_DPAD_DOWN;
+		}
+		final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
+		if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
+			toggleConversationsOverview();
+			return true;
+		} else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
+			startActivity(new Intent(this, StartConversationActivity.class));
+			return true;
+		} else if (modifier && key == downKey) {
+			if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
+				showConversationsOverview();
+				;
+			}
+			return selectDownConversation();
+		} else if (modifier && key == upKey) {
+			if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
+				showConversationsOverview();
+			}
+			return selectUpConversation();
+		} else if (modifier && key == KeyEvent.KEYCODE_1) {
+			return openConversationByIndex(0);
+		} else if (modifier && key == KeyEvent.KEYCODE_2) {
+			return openConversationByIndex(1);
+		} else if (modifier && key == KeyEvent.KEYCODE_3) {
+			return openConversationByIndex(2);
+		} else if (modifier && key == KeyEvent.KEYCODE_4) {
+			return openConversationByIndex(3);
+		} else if (modifier && key == KeyEvent.KEYCODE_5) {
+			return openConversationByIndex(4);
+		} else if (modifier && key == KeyEvent.KEYCODE_6) {
+			return openConversationByIndex(5);
+		} else if (modifier && key == KeyEvent.KEYCODE_7) {
+			return openConversationByIndex(6);
+		} else if (modifier && key == KeyEvent.KEYCODE_8) {
+			return openConversationByIndex(7);
+		} else if (modifier && key == KeyEvent.KEYCODE_9) {
+			return openConversationByIndex(8);
+		} else if (modifier && key == KeyEvent.KEYCODE_0) {
+			return openConversationByIndex(9);
+		} else {
+			return super.onKeyUp(key, event);
+		}
+	}
+
+	private void toggleConversationsOverview() {
+		if (isConversationsOverviewVisable()) {
+			hideConversationsOverview();
+			if (mConversationFragment != null) {
+				mConversationFragment.setFocusOnInputField();
+			}
+		} else {
+			showConversationsOverview();
+		}
+	}
+
+	private boolean selectUpConversation() {
+		if (this.mSelectedConversation != null) {
+			int index = this.conversationList.indexOf(this.mSelectedConversation);
+			if (index > 0) {
+				return openConversationByIndex(index - 1);
+			}
+		}
+		return false;
+	}
+
+	private boolean selectDownConversation() {
+		if (this.mSelectedConversation != null) {
+			int index = this.conversationList.indexOf(this.mSelectedConversation);
+			if (index != -1 && index < this.conversationList.size() - 1) {
+				return openConversationByIndex(index + 1);
+			}
+		}
+		return false;
+	}
+
+	private boolean openConversationByIndex(int index) {
+		try {
+			this.conversationWasSelectedByKeyboard = true;
+			this.mConversationFragment.stopScrolling();
+			setSelectedConversation(this.conversationList.get(index));
+			this.mConversationFragment.reInit(getSelectedConversation());
+			if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
+				this.listView.setSelection(index);
+			}
+			openConversation();
+			return true;
+		} catch (IndexOutOfBoundsException e) {
+			return false;
+		}
+	}
+
+	@Override
+	protected void onNewIntent(final Intent intent) {
+		if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
+			mOpenConversation = null;
+			mUnprocessedNewIntent = true;
+			if (xmppConnectionServiceBound) {
+				handleViewConversationIntent(intent);
+				intent.setAction(Intent.ACTION_MAIN);
+			} else {
+				setIntent(intent);
+			}
+		}
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		this.mRedirected.set(false);
+		if (this.xmppConnectionServiceBound) {
+			this.onBackendConnected();
+		}
+		if (conversationList.size() >= 1) {
+			this.onConversationUpdate();
+		}
+	}
+
+	@Override
+	public void onPause() {
+		listView.discardUndo();
+		super.onPause();
+		this.mActivityPaused = true;
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		final int theme = findTheme();
+		final boolean usingEnterKey = usingEnterKey();
+		if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
+			recreate();
+		}
+		this.mActivityPaused = false;
+		if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
+			sendReadMarkerIfNecessary(getSelectedConversation());
+		}
+	}
+
+	@Override
+	public void onSaveInstanceState(final Bundle savedInstanceState) {
+		Conversation conversation = getSelectedConversation();
+		if (conversation != null) {
+			savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
+			Pair<Integer, Integer> scrollPosition = mConversationFragment.getScrollPosition();
+			if (scrollPosition != null) {
+				savedInstanceState.putInt(STATE_FIRST_VISIBLE, scrollPosition.first);
+				savedInstanceState.putInt(STATE_OFFSET_FROM_TOP, scrollPosition.second);
+			}
+		} else {
+			savedInstanceState.remove(STATE_OPEN_CONVERSATION);
+		}
+		savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
+		/*if (this.mPendingImageUris.size() >= 1) {
+			Log.d(Config.LOGTAG, "ConversationsActivity.onSaveInstanceState() - saving pending image uri");
+			savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString());
+		} else {
+			savedInstanceState.remove(STATE_PENDING_URI);
+		}*/
+		super.onSaveInstanceState(savedInstanceState);
+	}
+
+	private void clearPending() {
+		mConversationFragment.clearPending();
+	}
+
+	private void redirectToStartConversationActivity(boolean noAnimation) {
+		Account pendingAccount = xmppConnectionService.getPendingAccount();
+		if (pendingAccount == null) {
+			Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
+			startConversationActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+			if (noAnimation) {
+				startConversationActivity.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+			}
+			startConversationActivity.putExtra("init", true);
+			startActivity(startConversationActivity);
+			if (noAnimation) {
+				overridePendingTransition(0, 0);
+			}
+		} else {
+			switchToAccount(pendingAccount, true);
+		}
+	}
+
+	@Override
+	void onBackendConnected() {
+		this.xmppConnectionService.getNotificationService().setIsInForeground(true);
+		updateConversationList();
+
+		if (mPendingConferenceInvite != null) {
+			if (mPendingConferenceInvite.execute(this)) {
+				mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
+				mToast.show();
+			}
+			mPendingConferenceInvite = null;
+		}
+
+		final Intent intent = getIntent();
+
+		if (xmppConnectionService.getAccounts().size() == 0) {
+			if (mRedirected.compareAndSet(false, true)) {
+				if (Config.X509_VERIFICATION) {
+					Intent redirectionIntent = new Intent(this, ManageAccountActivity.class);
+					redirectionIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+					startActivity(redirectionIntent);
+					overridePendingTransition(0, 0);
+				} else if (Config.MAGIC_CREATE_DOMAIN != null) {
+					WelcomeActivity.launch(this);
+				} else {
+					Intent editAccount = new Intent(this, EditAccountActivity.class);
+					editAccount.putExtra("init", true);
+					editAccount.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+					startActivity(editAccount);
+					overridePendingTransition(0, 0);
+				}
+			}
+		} else if (conversationList.size() <= 0) {
+			if (mRedirected.compareAndSet(false, true)) {
+				redirectToStartConversationActivity(true);
+			}
+		} else if (selectConversationByUuid(mOpenConversation)) {
+			if (mPanelOpen) {
+				showConversationsOverview();
+			} else {
+				if (isConversationsOverviewHideable()) {
+					openConversation();
+					updateActionBarTitle(true);
+				}
+			}
+			if (this.mConversationFragment.reInit(getSelectedConversation())) {
+				Log.d(Config.LOGTAG, "setting scroll position on fragment");
+				this.mConversationFragment.setScrollPosition(mScrollPosition);
+			}
+			mOpenConversation = null;
+		} else if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
+			clearPending();
+			handleViewConversationIntent(intent);
+			intent.setAction(Intent.ACTION_MAIN);
+		} else if (getSelectedConversation() == null) {
+			reInitLatestConversation();
+		} else {
+			this.mConversationFragment.messageListAdapter.updatePreferences();
+			//this.mConversationFragment.messagesView.invalidateViews();
+			this.mConversationFragment.setupIme();
+		}
+
+		mConversationFragment.onBackendConnected();
+
+		/*if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService) && !mRedirected.get()) {
+			openBatteryOptimizationDialogIfNeeded();
+		}*/
+		if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
+			xmppConnectionService.getNotificationService().setOpenConversation(null);
+		} else {
+			xmppConnectionService.getNotificationService().setOpenConversation(getSelectedConversation());
+		}
+	}
+
+	private boolean isStopping() {
+		if (Build.VERSION.SDK_INT >= 17) {
+			return isFinishing() || isDestroyed();
+		} else {
+			return isFinishing();
+		}
+	}
+
+	private void reInitLatestConversation() {
+		showConversationsOverview();
+		clearPending();
+		setSelectedConversation(conversationList.get(0));
+		this.mConversationFragment.reInit(getSelectedConversation());
+	}
+
+	private void handleViewConversationIntent(final Intent intent) {
+		final String uuid = intent.getStringExtra(CONVERSATION);
+		final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
+		final String text = intent.getStringExtra(TEXT);
+		final String nick = intent.getStringExtra(NICK);
+		final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
+		this.mConversationFragment.stopScrolling();
+		if (selectConversationByUuid(uuid)) {
+			this.mConversationFragment.reInit(getSelectedConversation());
+			if (nick != null) {
+				if (pm) {
+					Jid jid = getSelectedConversation().getJid();
+					try {
+						Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
+						this.mConversationFragment.privateMessageWith(next);
+					} catch (final InvalidJidException ignored) {
+						//do nothing
+					}
+				} else {
+					this.mConversationFragment.highlightInConference(nick);
+				}
+			} else {
+				this.mConversationFragment.appendText(text);
+			}
+			hideConversationsOverview();
+			mUnprocessedNewIntent = false;
+			openConversation();
+			if (mContentView instanceof SlidingPaneLayout) {
+				updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
+			}
+			if (downloadUuid != null) {
+				final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
+				if (message != null) {
+					//startDownloadable(message);
+				}
+			}
+		} else {
+			mUnprocessedNewIntent = false;
+		}
+	}
+
+	private boolean selectConversationByUuid(String uuid) {
+		if (uuid == null) {
+			return false;
+		}
+		for (Conversation aConversationList : conversationList) {
+			if (aConversationList.getUuid().equals(uuid)) {
+				setSelectedConversation(aConversationList);
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	protected void unregisterListeners() {
+		super.unregisterListeners();
+		xmppConnectionService.getNotificationService().setOpenConversation(null);
+	}
+
+	@Override
+	protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+		super.onActivityResult(requestCode, resultCode, data);
+		if (resultCode != RESULT_OK) {
+			if (requestCode == REQUEST_BATTERY_OP) {
+				setNeverAskForBatteryOptimizationsAgain();
+			}
+		}
+	}
+
+	private String getBatteryOptimizationPreferenceKey() {
+		@SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
+		return "show_battery_optimization" + (device == null ? "" : device);
+	}
+
+	private void setNeverAskForBatteryOptimizationsAgain() {
+		getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
+	}
+
+	private void openBatteryOptimizationDialogIfNeeded() {
+		if (hasAccountWithoutPush()
+				&& isOptimizingBattery()
+				&& getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
+			AlertDialog.Builder builder = new AlertDialog.Builder(this);
+			builder.setTitle(R.string.battery_optimizations_enabled);
+			builder.setMessage(R.string.battery_optimizations_enabled_dialog);
+			builder.setPositiveButton(R.string.next, (dialog, which) -> {
+				Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+				Uri uri = Uri.parse("package:" + getPackageName());
+				intent.setData(uri);
+				try {
+					startActivityForResult(intent, REQUEST_BATTERY_OP);
+				} catch (ActivityNotFoundException e) {
+					Toast.makeText(this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+				}
+			});
+			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+				builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
+			}
+			AlertDialog dialog = builder.create();
+			dialog.setCanceledOnTouchOutside(false);
+			dialog.show();
+		}
+	}
+
+	private boolean hasAccountWithoutPush() {
+		for (Account account : xmppConnectionService.getAccounts()) {
+			if (account.getStatus() == Account.State.ONLINE && !xmppConnectionService.getPushManagementService().available(account)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public void updateConversationList() {
+		xmppConnectionService.populateWithOrderedConversations(conversationList);
+		if (!conversationList.contains(mSelectedConversation)) {
+			mSelectedConversation = null;
+		}
+		if (swipedConversation != null) {
+			if (swipedConversation.isRead()) {
+				conversationList.remove(swipedConversation);
+			} else {
+				listView.discardUndo();
+			}
+		}
+		listAdapter.notifyDataSetChanged();
+	}
+
+	@Override
+	protected void refreshUiReal() {
+		updateConversationList();
+		if (conversationList.size() > 0) {
+			if (!this.mConversationFragment.isAdded()) {
+				Log.d(Config.LOGTAG, "fragment NOT added to activity. detached=" + Boolean.toString(mConversationFragment.isDetached()));
+			}
+			if (getSelectedConversation() == null) {
+				reInitLatestConversation();
+			} else {
+				ConversationLegacyActivity.this.mConversationFragment.refresh();
+				updateActionBarTitle();
+				invalidateOptionsMenu();
+			}
+		} else {
+			if (!isStopping() && mRedirected.compareAndSet(false, true)) {
+				redirectToStartConversationActivity(false);
+			}
+			Log.d(Config.LOGTAG, "not updating conversations fragment because conversations list size was 0");
+		}
+	}
+
+	@Override
+	public void onAccountUpdate() {
+		this.refreshUi();
+	}
+
+	@Override
+	public void onConversationUpdate() {
+		this.refreshUi();
+	}
+
+	@Override
+	public void onRosterUpdate() {
+		this.refreshUi();
+	}
+
+	@Override
+	public void OnUpdateBlocklist(Status status) {
+		this.refreshUi();
+	}
+
+	@Override
+	public void onShowErrorToast(final int resId) {
+		runOnUiThread(() -> Toast.makeText(ConversationLegacyActivity.this, resId, Toast.LENGTH_SHORT).show());
+	}
+
+	public boolean highlightSelectedConversations() {
+		return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
+	}
+}

src/main/java/eu/siacs/conversations/ui/ConversationsMainActivity.java 🔗

@@ -1,245 +0,0 @@
-/*
- * Copyright (c) 2018, Daniel Gultsch All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation and/or
- * other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors
- * may be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package eu.siacs.conversations.ui;
-
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.databinding.DataBindingUtil;
-import android.os.Bundle;
-import android.support.annotation.IdRes;
-import android.support.v7.app.ActionBar;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.databinding.ActivityConversationsBinding;
-import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
-import eu.siacs.conversations.ui.interfaces.OnConversationRead;
-import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
-import eu.siacs.conversations.ui.interfaces.OnConversationsListItemUpdated;
-import eu.siacs.conversations.ui.service.EmojiService;
-import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
-
-public class ConversationsMainActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead, XmppConnectionService.OnAccountUpdate, XmppConnectionService.OnConversationUpdate, XmppConnectionService.OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast {
-
-
-	//secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment
-	private static final @IdRes int[] FRAGMENT_ID_NOTIFICATION_ORDER = {R.id.secondary_fragment, R.id.main_fragment};
-
-	private ActivityConversationsBinding binding;
-
-	@Override
-	protected void refreshUiReal() {
-		for(@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
-			refreshFragment(id);
-		}
-	}
-
-	@Override
-	void onBackendConnected() {
-		for(@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
-			notifyFragmentOfBackendConnected(id);
-		}
-		invalidateActionBarTitle();
-	}
-
-	private void notifyFragmentOfBackendConnected(@IdRes int id) {
-		final Fragment fragment = getFragmentManager().findFragmentById(id);
-		if (fragment != null && fragment instanceof XmppFragment) {
-			((XmppFragment) fragment).onBackendConnected();
-		}
-	}
-
-	private void refreshFragment(@IdRes int id) {
-		final Fragment fragment = getFragmentManager().findFragmentById(id);
-		if (fragment != null && fragment instanceof XmppFragment) {
-			((XmppFragment) fragment).refresh();
-		}
-	}
-
-	@Override
-	protected void onCreate(final Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		new EmojiService(this).init();
-		this.binding = DataBindingUtil.setContentView(this, R.layout.activity_conversations);
-		this.getFragmentManager().addOnBackStackChangedListener(this::invalidateActionBarTitle);
-		this.initializeFragments();
-		this.invalidateActionBarTitle();
-	}
-
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		getMenuInflater().inflate(R.menu.activity_conversations, menu);
-		return super.onCreateOptionsMenu(menu);
-	}
-
-	@Override
-	public void onConversationSelected(Conversation conversation) {
-		Log.d(Config.LOGTAG, "selected " + conversation.getName());
-		ConversationFragment conversationFragment = (ConversationFragment) getFragmentManager().findFragmentById(R.id.secondary_fragment);
-		final boolean mainNeedsRefresh;
-		if (conversationFragment == null) {
-			mainNeedsRefresh = false;
-			conversationFragment = new ConversationFragment();
-			FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
-			fragmentTransaction.replace(R.id.main_fragment, conversationFragment);
-			fragmentTransaction.addToBackStack(null);
-			fragmentTransaction.commit();
-		} else {
-			mainNeedsRefresh = true;
-		}
-		conversationFragment.reInit(conversation);
-		if (mainNeedsRefresh) {
-			refreshFragment(R.id.main_fragment);
-		}
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item) {
-		switch (item.getItemId()) {
-			case android.R.id.home:
-				FragmentManager fm = getFragmentManager();
-				if (fm.getBackStackEntryCount() > 0) {
-					fm.popBackStack();
-					return true;
-				}
-				break;
-		}
-		return super.onOptionsItemSelected(item);
-	}
-
-	private void initializeFragments() {
-		FragmentTransaction transaction = getFragmentManager().beginTransaction();
-		Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
-		Fragment secondaryFragment = getFragmentManager().findFragmentById(R.id.secondary_fragment);
-		if (mainFragment != null) {
-			Log.d(Config.LOGTAG,"initializeFragment(). main fragment exists");
-			if (binding.secondaryFragment != null) {
-				if (mainFragment instanceof ConversationFragment) {
-					Log.d(Config.LOGTAG,"gained secondary fragment. moving...");
-					getFragmentManager().popBackStack();
-					transaction.remove(mainFragment);
-					transaction.commit();
-					getFragmentManager().executePendingTransactions();
-					transaction = getFragmentManager().beginTransaction();
-					transaction.replace(R.id.secondary_fragment, mainFragment);
-					transaction.replace(R.id.main_fragment, new ConversationsOverviewFragment());
-					transaction.commit();
-					return;
-				}
-			} else {
-				if (secondaryFragment != null && secondaryFragment instanceof ConversationFragment) {
-					Log.d(Config.LOGTAG,"lost secondary fragment. moving...");
-					transaction.remove(secondaryFragment);
-					transaction.commit();
-					getFragmentManager().executePendingTransactions();
-					transaction = getFragmentManager().beginTransaction();
-					transaction.replace(R.id.main_fragment, secondaryFragment);
-					transaction.addToBackStack(null);
-					transaction.commit();
-					return;
-				}
-			}
-		} else {
-			transaction.replace(R.id.main_fragment, new ConversationsOverviewFragment());
-		}
-		if (binding.secondaryFragment != null) {
-			transaction.replace(R.id.secondary_fragment, new ConversationFragment());
-		}
-		transaction.commit();
-	}
-
-	private void invalidateActionBarTitle() {
-		final ActionBar actionBar = getSupportActionBar();
-		if (actionBar != null) {
-			Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
-			if (mainFragment != null && mainFragment instanceof ConversationFragment) {
-				final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
-				if (conversation != null) {
-					actionBar.setTitle(conversation.getName());
-					actionBar.setDisplayHomeAsUpEnabled(true);
-					return;
-				}
-			}
-			actionBar.setTitle(R.string.app_name);
-			actionBar.setDisplayHomeAsUpEnabled(false);
-		}
-	}
-
-	@Override
-	public void onConversationArchived(Conversation conversation) {
-
-	}
-
-	@Override
-	public void onConversationsListItemUpdated() {
-		Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment);
-		if (fragment != null && fragment instanceof ConversationsOverviewFragment) {
-			((ConversationsOverviewFragment) fragment).refresh();
-		}
-	}
-
-	@Override
-	public void onConversationRead(Conversation conversation) {
-		Log.d(Config.LOGTAG, "read event for " + conversation.getName() + " received");
-	}
-
-	@Override
-	public void onAccountUpdate() {
-		this.refreshUi();
-	}
-
-	@Override
-	public void onConversationUpdate() {
-		this.refreshUi();
-	}
-
-	@Override
-	public void onRosterUpdate() {
-		this.refreshUi();
-	}
-
-	@Override
-	public void OnUpdateBlocklist(OnUpdateBlocklist.Status status) {
-		this.refreshUi();
-	}
-
-	@Override
-	public void onShowErrorToast(int resId) {
-		runOnUiThread(() -> Toast.makeText(this, resId, Toast.LENGTH_SHORT).show());
-	}
-}

src/main/java/eu/siacs/conversations/ui/SettingsActivity.java 🔗

@@ -33,6 +33,7 @@ import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.services.ExportLogsService;
 import eu.siacs.conversations.services.MemorizingTrustManager;
+import eu.siacs.conversations.ui.util.Color;
 import eu.siacs.conversations.xmpp.XmppConnection;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
@@ -67,9 +68,7 @@ public class SettingsActivity extends XmppActivity implements
 
 		this.mTheme = findTheme();
 		setTheme(this.mTheme);
-
-		int bgcolor = getPrimaryBackgroundColor();
-		getWindow().getDecorView().setBackgroundColor(bgcolor);
+		getWindow().getDecorView().setBackgroundColor(Color.get(this,R.attr.color_background_primary));
 
 	}
 

src/main/java/eu/siacs/conversations/ui/XmppActivity.java 🔗

@@ -40,7 +40,6 @@ import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.text.InputType;
 import android.util.DisplayMetrics;
-import android.util.Pair;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
@@ -51,11 +50,8 @@ import android.widget.Toast;
 import java.io.FileNotFoundException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
@@ -71,9 +67,7 @@ import eu.siacs.conversations.services.BarcodeProvider;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
 import eu.siacs.conversations.ui.util.PresenceSelector;
-import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.ExceptionHelper;
-import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
 import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -507,27 +501,25 @@ public abstract class XmppActivity extends AppCompatActivity {
 	}
 
 	private void switchToConversation(Conversation conversation, String text, String nick, boolean pm, boolean newTask) {
-		Intent viewConversationIntent = new Intent(this,
-				ConversationActivity.class);
-		viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
-		viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
-				conversation.getUuid());
+		Intent intent = new Intent(this, ConversationActivity.class);
+		intent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
+		intent.putExtra(ConversationActivity.EXTRA_CONVERSATION, conversation.getUuid());
 		if (text != null) {
-			viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
+			intent.putExtra(ConversationActivity.EXTRA_TEXT, text);
 		}
 		if (nick != null) {
-			viewConversationIntent.putExtra(ConversationActivity.NICK, nick);
-			viewConversationIntent.putExtra(ConversationActivity.PRIVATE_MESSAGE, pm);
+			intent.putExtra(ConversationActivity.EXTRA_NICK, nick);
+			intent.putExtra(ConversationActivity.EXTRA_IS_PRIVATE_MESSAGE, pm);
 		}
 		if (newTask) {
-			viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+			intent.setFlags(intent.getFlags()
 					| Intent.FLAG_ACTIVITY_NEW_TASK
 					| Intent.FLAG_ACTIVITY_SINGLE_TOP);
 		} else {
-			viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+			intent.setFlags(intent.getFlags()
 					| Intent.FLAG_ACTIVITY_CLEAR_TOP);
 		}
-		startActivity(viewConversationIntent);
+		startActivity(intent);
 		finish();
 	}
 
@@ -823,14 +815,6 @@ public abstract class XmppActivity extends AppCompatActivity {
 		return this.mColorGreen;
 	}
 
-	public int getPrimaryBackgroundColor() {
-		return this.mPrimaryBackgroundColor;
-	}
-
-	public int getSecondaryBackgroundColor() {
-		return this.mSecondaryBackgroundColor;
-	}
-
 	public int getPixel(int dp) {
 		DisplayMetrics metrics = getResources().getDisplayMetrics();
 		return ((int) (dp * metrics.density));

src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java 🔗

@@ -25,9 +25,9 @@ import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.Transferable;
-import eu.siacs.conversations.ui.ConversationActivity;
 import eu.siacs.conversations.ui.ConversationFragment;
 import eu.siacs.conversations.ui.XmppActivity;
+import eu.siacs.conversations.ui.util.Color;
 import eu.siacs.conversations.ui.widget.UnreadCountCustomView;
 import eu.siacs.conversations.utils.EmojiWrapper;
 import eu.siacs.conversations.utils.UIHelper;
@@ -48,13 +48,12 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 			LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 			view = inflater.inflate(R.layout.conversation_list_row,parent, false);
 		}
+		ViewHolder viewHolder = ViewHolder.get(view);
 		Conversation conversation = getItem(position);
 		if (this.activity instanceof XmppActivity) {
-			View swipeableItem = view.findViewById(R.id.swipeable_item);
-			int c = conversation == selectedConversation ? this.activity.getSecondaryBackgroundColor() : this.activity.getPrimaryBackgroundColor();
-			swipeableItem.setBackgroundColor(c);
+			int c = Color.get(activity, conversation == selectedConversation ? R.attr.color_background_secondary: R.attr.color_background_primary);
+			viewHolder.swipeableItem.setBackgroundColor(c);
 		}
-		ViewHolder viewHolder = ViewHolder.get(view);
 		if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {
 			viewHolder.name.setText(EmojiWrapper.transform(conversation.getName()));
 		} else {
@@ -182,6 +181,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 	}
 
 	public static class ViewHolder {
+		private View swipeableItem;
 		private TextView name;
 		private TextView lastMessage;
 		private ImageView lastMessageIcon;
@@ -199,6 +199,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 			ViewHolder viewHolder = (ViewHolder) layout.getTag();
 			if (viewHolder == null) {
 				viewHolder = new ViewHolder();
+				viewHolder.swipeableItem = layout.findViewById(R.id.swipeable_item);
 				viewHolder.name = layout.findViewById(R.id.conversation_name);
 				viewHolder.lastMessage = layout.findViewById(R.id.conversation_lastmsg);
 				viewHolder.lastMessageIcon = layout.findViewById(R.id.conversation_lastmsg_img);

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -63,8 +63,6 @@ import eu.siacs.conversations.entities.Transferable;
 import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.services.MessageArchiveService;
 import eu.siacs.conversations.services.NotificationService;
-import eu.siacs.conversations.ui.ConversationActivity;
-import eu.siacs.conversations.ui.ConversationsMainActivity;
 import eu.siacs.conversations.ui.XmppActivity;
 import eu.siacs.conversations.ui.service.AudioPlayer;
 import eu.siacs.conversations.ui.text.DividerSpan;

src/main/java/eu/siacs/conversations/ui/util/Color.java 🔗

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package eu.siacs.conversations.ui.util;
+
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.annotation.AttrRes;
+import android.support.annotation.ColorInt;
+import android.util.TypedValue;
+
+public class Color {
+
+	public static @ColorInt int get(Context context, @AttrRes int attr) {
+		TypedValue typedValue = new TypedValue();
+		Resources.Theme theme = context.getTheme();
+		theme.resolveAttribute(attr, typedValue, true);
+		return typedValue.data;
+	}
+
+}

src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java 🔗

@@ -37,7 +37,6 @@ import android.preference.PreferenceManager;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Presence;
-import eu.siacs.conversations.ui.ConversationActivity;
 import eu.siacs.conversations.ui.ConversationFragment;
 import eu.siacs.conversations.utils.UIHelper;
 

src/main/res/layout-w945dp/activity_conversations.xml 🔗

@@ -9,13 +9,13 @@
             android:id="@+id/main_fragment"
             android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:layout_weight="1"/>
+            android:layout_weight="1000"/>
 
         <FrameLayout
             android:id="@+id/secondary_fragment"
             android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:layout_weight="2"/>
+            android:layout_weight="1618"/>
 
     </LinearLayout>