choose contact activity: start action mode on short press

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java | 607 
src/main/res/drawable-hdpi/ic_forward_white_24dp.png               |   0 
src/main/res/drawable-mdpi/ic_forward_white_24dp.png               |   0 
src/main/res/drawable-xhdpi/ic_forward_white_24dp.png              |   0 
src/main/res/drawable-xxhdpi/ic_forward_white_24dp.png             |   0 
src/main/res/drawable-xxxhdpi/ic_forward_white_24dp.png            |   0 
src/main/res/drawable/list_item_background_dark.xml                |  34 
src/main/res/drawable/list_item_background_light.xml               |  34 
src/main/res/layout/contact.xml                                    |   2 
src/main/res/menu/select_multiple.xml                              |  10 
src/main/res/values/attrs.xml                                      |   2 
src/main/res/values/colors.xml                                     |   1 
src/main/res/values/strings.xml                                    |   5 
src/main/res/values/themes.xml                                     |   6 
14 files changed, 389 insertions(+), 312 deletions(-)

Detailed changes

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

@@ -11,7 +11,6 @@ import android.support.v4.app.FragmentTransaction;
 import android.support.v7.app.ActionBar;
 import android.view.ActionMode;
 import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
@@ -38,298 +37,316 @@ import eu.siacs.conversations.ui.util.PendingItem;
 import eu.siacs.conversations.utils.XmppUri;
 import rocks.xmpp.addr.Jid;
 
-public class ChooseContactActivity extends AbstractSearchableListItemActivity {
-	public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
-	private List<String> mActivatedAccounts = new ArrayList<>();
-	private Set<String> selected = new HashSet<>();
-	private Set<String> filterContacts;
-
-	private PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
-
-	public static Intent create(Activity activity, Conversation conversation) {
-		final Intent intent = new Intent(activity, ChooseContactActivity.class);
-		List<String> contacts = new ArrayList<>();
-		if (conversation.getMode() == Conversation.MODE_MULTI) {
-			for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
-				Jid jid = user.getRealJid();
-				if (jid != null) {
-					contacts.add(jid.asBareJid().toString());
-				}
-			}
-		} else {
-			contacts.add(conversation.getJid().asBareJid().toString());
-		}
-		intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
-		intent.putExtra("conversation", conversation.getUuid());
-		intent.putExtra("multiple", true);
-		intent.putExtra("show_enter_jid", true);
-		intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
-		return intent;
-	}
-
-	@Override
-	public void onCreate(final Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		filterContacts = new HashSet<>();
-		if (savedInstanceState != null) {
-			String[] selectedContacts = savedInstanceState.getStringArray("selected_contacts");
-			if (selectedContacts != null) {
-				selected.clear();
-				selected.addAll(Arrays.asList(selectedContacts));
-			}
-		}
-
-		String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
-		if (contacts != null) {
-			Collections.addAll(filterContacts, contacts);
-		}
-
-		if (getIntent().getBooleanExtra("multiple", false)) {
-			getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
-			getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
-
-				@Override
-				public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-					return false;
-				}
-
-				@Override
-				public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-					binding.fab.setVisibility(View.GONE);
-					final View view = getSearchEditText();
-					final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-					if (view != null && imm != null) {
-						imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
-					}
-					MenuInflater inflater = getMenuInflater();
-					inflater.inflate(R.menu.select_multiple, menu);
-					MenuItem selectButton = menu.findItem(R.id.selection_submit);
-					String buttonText = getResources().getQuantityString(R.plurals.select_contact, selected.size(), selected.size());
-					selectButton.setTitle(buttonText);
-					return true;
-				}
-
-				@Override
-				public void onDestroyActionMode(ActionMode mode) {
-					binding.fab.setVisibility(View.VISIBLE);
-					selected.clear();
-				}
-
-				@Override
-				public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-					switch (item.getItemId()) {
-						case R.id.selection_submit:
-							final Intent request = getIntent();
-							final Intent data = new Intent();
-							data.putExtra("conversation",
-									request.getStringExtra("conversation"));
-							String[] selection = getSelectedContactJids();
-							data.putExtra("contacts", selection);
-							data.putExtra("multiple", true);
-							data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
-							data.putExtra("subject", request.getStringExtra("subject"));
-							setResult(RESULT_OK, data);
-							finish();
-							return true;
-					}
-					return false;
-				}
-
-				@Override
-				public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
-					Contact item = (Contact) getListItems().get(position);
-					if (checked) {
-						selected.add(item.getJid().toString());
-					} else {
-						selected.remove(item.getJid().toString());
-					}
-					int numSelected = selected.size();
-					MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
-					String buttonText = getResources().getQuantityString(R.plurals.select_contact,
-							numSelected, numSelected);
-					selectButton.setTitle(buttonText);
-				}
-			});
-		}
-
-		getListView().setOnItemClickListener((parent, view, position, id) -> {
-			final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-			imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
-			final Intent request = getIntent();
-			final Intent data = new Intent();
-			final ListItem mListItem = getListItems().get(position);
-			data.putExtra("contact", mListItem.getJid().toString());
-			String account = request.getStringExtra(EXTRA_ACCOUNT);
-			if (account == null && mListItem instanceof Contact) {
-				account = ((Contact) mListItem).getAccount().getJid().asBareJid().toString();
-			}
-			data.putExtra(EXTRA_ACCOUNT, account);
-			data.putExtra("conversation", request.getStringExtra("conversation"));
-			data.putExtra("multiple", false);
-			data.putExtra("subject", request.getStringExtra("subject"));
-			setResult(RESULT_OK, data);
-			finish();
-		});
-		final Intent i = getIntent();
-		boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
-		if (showEnterJid) {
-			this.binding.fab.setOnClickListener((v) -> showEnterJidDialog(null));
-		} else {
-			this.binding.fab.setVisibility(View.GONE);
-		}
-	}
-
-	@Override
-	public void onStart() {
-		super.onStart();
-		Intent intent = getIntent();
-		@StringRes
-		int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
-		ActionBar bar = getSupportActionBar();
-		if (bar != null) {
-			try {
-				bar.setTitle(res);
-			} catch (Exception e) {
-				bar.setTitle(R.string.title_activity_choose_contact);
-			}
-		}
-	}
-
-	@Override
-	public boolean onCreateOptionsMenu(final Menu menu) {
-		super.onCreateOptionsMenu(menu);
-		final Intent i = getIntent();
-		boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
-		menu.findItem(R.id.action_scan_qr_code).setVisible(isCameraFeatureAvailable() && showEnterJid);
-		return true;
-	}
-
-	@Override
-	public void onSaveInstanceState(Bundle savedInstanceState) {
-		savedInstanceState.putStringArray("selected_contacts",getSelectedContactJids());
-		super.onSaveInstanceState(savedInstanceState);
-	}
-
-	protected void filterContacts(final String needle) {
-		getListItems().clear();
-		if (xmppConnectionService == null) {
-			getListItemAdapter().notifyDataSetChanged();
-			return;
-		}
-		for (final Account account : xmppConnectionService.getAccounts()) {
-			if (account.getStatus() != Account.State.DISABLED) {
-				for (final Contact contact : account.getRoster().getContacts()) {
-					if (contact.showInRoster() &&
-							!filterContacts.contains(contact.getJid().asBareJid().toString())
-							&& contact.match(this, needle)) {
-						getListItems().add(contact);
-					}
-				}
-			}
-		}
-		Collections.sort(getListItems());
-		getListItemAdapter().notifyDataSetChanged();
-	}
-
-	private String[] getSelectedContactJids() {
-		return selected.toArray(new String[selected.size()]);
-	}
-
-	public void refreshUiReal() {
-		//nothing to do. This Activity doesn't implement any listeners
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item) {
-		switch (item.getItemId()) {
-			case R.id.action_scan_qr_code:
-				ScanActivity.scan(this);
-				return true;
-		}
-		return super.onOptionsItemSelected(item);
-	}
-
-	protected void showEnterJidDialog(XmppUri uri) {
-		FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-		Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
-		if (prev != null) {
-			ft.remove(prev);
-		}
-		ft.addToBackStack(null);
-		Jid jid = uri == null ? null : uri.getJid();
-		EnterJidDialog dialog = EnterJidDialog.newInstance(
-				mActivatedAccounts,
-				getString(R.string.enter_contact),
-				getString(R.string.select),
-				jid == null ? null : jid.asBareJid().toString(),
-				getIntent().getStringExtra(EXTRA_ACCOUNT),
-				true
-		);
-
-		dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
-			final Intent request = getIntent();
-			final Intent data = new Intent();
-			data.putExtra("contact", contactJid.toString());
-			data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
-			data.putExtra("conversation",
-					request.getStringExtra("conversation"));
-			data.putExtra("multiple", false);
-			data.putExtra("subject", request.getStringExtra("subject"));
-			setResult(RESULT_OK, data);
-			finish();
-
-			return true;
-		});
-
-		dialog.show(ft, "dialog");
-	}
-
-	@Override
-	public void onActivityResult(int requestCode, int resultCode, Intent intent) {
-		super.onActivityResult(requestCode, requestCode, intent);
-		ActivityResult activityResult = ActivityResult.of(requestCode, resultCode, intent);
-		if (xmppConnectionService != null) {
-			handleActivityResult(activityResult);
-		} else {
-			this.postponedActivityResult.push(activityResult);
-		}
-	}
-
-	private void handleActivityResult(ActivityResult activityResult) {
-		if (activityResult.resultCode == RESULT_OK && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
-			String result = activityResult.data.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
-			XmppUri uri = new XmppUri(result == null ? "" : result);
-			if (uri.isJidValid()) {
-				showEnterJidDialog(uri);
-			}
-		}
-	}
-
-	@Override
-	void onBackendConnected() {
-		filterContacts();
-		this.mActivatedAccounts.clear();
-		for (Account account : xmppConnectionService.getAccounts()) {
-			if (account.getStatus() != Account.State.DISABLED) {
-				if (Config.DOMAIN_LOCK != null) {
-					this.mActivatedAccounts.add(account.getJid().getLocal());
-				} else {
-					this.mActivatedAccounts.add(account.getJid().asBareJid().toString());
-				}
-			}
-		}
-		ActivityResult activityResult = this.postponedActivityResult.pop();
-		if (activityResult != null) {
-			handleActivityResult(activityResult);
-		}
-		Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
-		if (fragment != null && fragment instanceof OnBackendConnected) {
-			((OnBackendConnected) fragment).onBackendConnected();
-		}
-	}
-
-	@Override
-	public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
-		ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
-	}
+public class ChooseContactActivity extends AbstractSearchableListItemActivity implements MultiChoiceModeListener {
+    public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
+    private List<String> mActivatedAccounts = new ArrayList<>();
+    private Set<String> selected = new HashSet<>();
+    private Set<String> filterContacts;
+
+    private boolean showEnterJid = false;
+
+    private PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
+
+    public static Intent create(Activity activity, Conversation conversation) {
+        final Intent intent = new Intent(activity, ChooseContactActivity.class);
+        List<String> contacts = new ArrayList<>();
+        if (conversation.getMode() == Conversation.MODE_MULTI) {
+            for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
+                Jid jid = user.getRealJid();
+                if (jid != null) {
+                    contacts.add(jid.asBareJid().toString());
+                }
+            }
+        } else {
+            contacts.add(conversation.getJid().asBareJid().toString());
+        }
+        intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
+        intent.putExtra("conversation", conversation.getUuid());
+        intent.putExtra("multiple", true);
+        intent.putExtra("show_enter_jid", true);
+        intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
+        return intent;
+    }
+
+    @Override
+    public void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        filterContacts = new HashSet<>();
+        if (savedInstanceState != null) {
+            String[] selectedContacts = savedInstanceState.getStringArray("selected_contacts");
+            if (selectedContacts != null) {
+                selected.clear();
+                selected.addAll(Arrays.asList(selectedContacts));
+            }
+        }
+
+        String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
+        if (contacts != null) {
+            Collections.addAll(filterContacts, contacts);
+        }
+
+        Intent intent = getIntent();
+
+        final boolean multiple = intent.getBooleanExtra("multiple", false);
+        if (multiple) {
+            getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+            getListView().setMultiChoiceModeListener(this);
+        }
+
+        getListView().setOnItemClickListener((parent, view, position, id) -> {
+            if (multiple) {
+                startActionMode(this);
+                getListView().setItemChecked(position, true);
+                return;
+            }
+            final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+            imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
+            final Intent request = getIntent();
+            final Intent data = new Intent();
+            final ListItem mListItem = getListItems().get(position);
+            data.putExtra("contact", mListItem.getJid().toString());
+            String account = request.getStringExtra(EXTRA_ACCOUNT);
+            if (account == null && mListItem instanceof Contact) {
+                account = ((Contact) mListItem).getAccount().getJid().asBareJid().toString();
+            }
+            data.putExtra(EXTRA_ACCOUNT, account);
+            data.putExtra("conversation", request.getStringExtra("conversation"));
+            data.putExtra("multiple", false);
+            data.putExtra("subject", request.getStringExtra("subject"));
+            setResult(RESULT_OK, data);
+            finish();
+        });
+        final Intent i = getIntent();
+        this.showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
+        this.binding.fab.setOnClickListener(this::onFabClicked);
+        if (this.showEnterJid) {
+            this.binding.fab.setVisibility(View.VISIBLE);
+        } else {
+            this.binding.fab.setVisibility(View.GONE);
+        }
+    }
+
+    private void onFabClicked(View v) {
+        if (selected.size() == 0) {
+            showEnterJidDialog(null);
+        } else {
+            submitSelection();
+        }
+    }
+
+    @Override
+    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        return false;
+    }
+
+    @Override
+    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        mode.setTitle(getTitleFromIntent());
+        binding.fab.setImageResource(R.drawable.ic_forward_white_24dp);
+        binding.fab.setVisibility(View.VISIBLE);
+        final View view = getSearchEditText();
+        final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+        if (view != null && imm != null) {
+            imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
+        }
+        return true;
+    }
+
+    @Override
+    public void onDestroyActionMode(ActionMode mode) {
+        this.binding.fab.setImageResource(R.drawable.ic_person_add_white_24dp);
+        if (this.showEnterJid) {
+            this.binding.fab.setVisibility(View.VISIBLE);
+        } else {
+            this.binding.fab.setVisibility(View.GONE);
+        }
+        selected.clear();
+    }
+
+    @Override
+    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        return false;
+    }
+
+    private void submitSelection() {
+        final Intent request = getIntent();
+        final Intent data = new Intent();
+        data.putExtra("conversation", request.getStringExtra("conversation"));
+        String[] selection = getSelectedContactJids();
+        data.putExtra("contacts", selection);
+        data.putExtra("multiple", true);
+        data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
+        data.putExtra("subject", request.getStringExtra("subject"));
+        setResult(RESULT_OK, data);
+        finish();
+    }
+
+    @Override
+    public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
+        Contact item = (Contact) getListItems().get(position);
+        if (checked) {
+            selected.add(item.getJid().toString());
+        } else {
+            selected.remove(item.getJid().toString());
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        ActionBar bar = getSupportActionBar();
+        if (bar != null) {
+            try {
+                bar.setTitle(getTitleFromIntent());
+            } catch (Exception e) {
+                bar.setTitle(R.string.title_activity_choose_contact);
+            }
+        }
+    }
+
+    public @StringRes
+    int getTitleFromIntent() {
+        final Intent intent = getIntent();
+        boolean multiple = intent != null && intent.getBooleanExtra("multiple", false);
+        @StringRes int fallback = multiple ? R.string.title_activity_choose_contacts : R.string.title_activity_choose_contact;
+        return intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, fallback) : fallback;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(final Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        final Intent i = getIntent();
+        boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
+        menu.findItem(R.id.action_scan_qr_code).setVisible(isCameraFeatureAvailable() && showEnterJid);
+        return true;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle savedInstanceState) {
+        savedInstanceState.putStringArray("selected_contacts", getSelectedContactJids());
+        super.onSaveInstanceState(savedInstanceState);
+    }
+
+    protected void filterContacts(final String needle) {
+        getListItems().clear();
+        if (xmppConnectionService == null) {
+            getListItemAdapter().notifyDataSetChanged();
+            return;
+        }
+        for (final Account account : xmppConnectionService.getAccounts()) {
+            if (account.getStatus() != Account.State.DISABLED) {
+                for (final Contact contact : account.getRoster().getContacts()) {
+                    if (contact.showInRoster() &&
+                            !filterContacts.contains(contact.getJid().asBareJid().toString())
+                            && contact.match(this, needle)) {
+                        getListItems().add(contact);
+                    }
+                }
+            }
+        }
+        Collections.sort(getListItems());
+        getListItemAdapter().notifyDataSetChanged();
+    }
+
+    private String[] getSelectedContactJids() {
+        return selected.toArray(new String[selected.size()]);
+    }
+
+    public void refreshUiReal() {
+        //nothing to do. This Activity doesn't implement any listeners
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_scan_qr_code:
+                ScanActivity.scan(this);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    protected void showEnterJidDialog(XmppUri uri) {
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
+        if (prev != null) {
+            ft.remove(prev);
+        }
+        ft.addToBackStack(null);
+        Jid jid = uri == null ? null : uri.getJid();
+        EnterJidDialog dialog = EnterJidDialog.newInstance(
+                mActivatedAccounts,
+                getString(R.string.enter_contact),
+                getString(R.string.select),
+                jid == null ? null : jid.asBareJid().toString(),
+                getIntent().getStringExtra(EXTRA_ACCOUNT),
+                true
+        );
+
+        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
+            final Intent request = getIntent();
+            final Intent data = new Intent();
+            data.putExtra("contact", contactJid.toString());
+            data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
+            data.putExtra("conversation",
+                    request.getStringExtra("conversation"));
+            data.putExtra("multiple", false);
+            data.putExtra("subject", request.getStringExtra("subject"));
+            setResult(RESULT_OK, data);
+            finish();
+
+            return true;
+        });
+
+        dialog.show(ft, "dialog");
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+        super.onActivityResult(requestCode, requestCode, intent);
+        ActivityResult activityResult = ActivityResult.of(requestCode, resultCode, intent);
+        if (xmppConnectionService != null) {
+            handleActivityResult(activityResult);
+        } else {
+            this.postponedActivityResult.push(activityResult);
+        }
+    }
+
+    private void handleActivityResult(ActivityResult activityResult) {
+        if (activityResult.resultCode == RESULT_OK && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
+            String result = activityResult.data.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
+            XmppUri uri = new XmppUri(result == null ? "" : result);
+            if (uri.isJidValid()) {
+                showEnterJidDialog(uri);
+            }
+        }
+    }
+
+    @Override
+    void onBackendConnected() {
+        filterContacts();
+        this.mActivatedAccounts.clear();
+        for (Account account : xmppConnectionService.getAccounts()) {
+            if (account.getStatus() != Account.State.DISABLED) {
+                if (Config.DOMAIN_LOCK != null) {
+                    this.mActivatedAccounts.add(account.getJid().getLocal());
+                } else {
+                    this.mActivatedAccounts.add(account.getJid().asBareJid().toString());
+                }
+            }
+        }
+        ActivityResult activityResult = this.postponedActivityResult.pop();
+        if (activityResult != null) {
+            handleActivityResult(activityResult);
+        }
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
+        if (fragment != null && fragment instanceof OnBackendConnected) {
+            ((OnBackendConnected) fragment).onBackendConnected();
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+        ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
+    }
 }

src/main/res/drawable/list_item_background_dark.xml 🔗

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<item android:state_activated="true"
+      android:drawable="@color/grey700" />
+</selector>

src/main/res/drawable/list_item_background_light.xml 🔗

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<item android:state_activated="true"
+      android:drawable="@color/grey300" />
+</selector>

src/main/res/layout/contact.xml 🔗

@@ -5,7 +5,7 @@
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?android:attr/activatedBackgroundIndicator"
+        android:background="?attr/list_item_background"
         android:padding="@dimen/list_padding">
 
         <com.makeramen.roundedimageview.RoundedImageView

src/main/res/menu/select_multiple.xml 🔗

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-      xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <item
-        android:id="@+id/selection_submit"
-        android:title="@string/invite_contact"
-        app:showAsAction="always" />
-
-</menu>

src/main/res/values/attrs.xml 🔗

@@ -20,6 +20,8 @@
     <attr name="activity_background_search" format="reference"/>
     <attr name="activity_background_no_results" format="reference"/>
 
+    <attr name="list_item_background" format="reference"/>
+
     <attr name="TextColorOnline" format="reference|color"/>
     <attr name="TextColorError" format="reference|color"/>
 

src/main/res/values/colors.xml 🔗

@@ -13,6 +13,7 @@
 	<color name="grey200">#ffeeeeee</color>
 	<color name="grey300">#ffe0e0e0</color>
 	<color name="grey500">#ff9e9e9e</color>
+	<color name="grey700">#ff616161</color>
 	<color name="grey800">#ff424242</color>
 	<color name="grey900">#ff282828</color>
 	<color name="red500">#fff44336</color>

src/main/res/values/strings.xml 🔗

@@ -20,6 +20,7 @@
     <string name="title_activity_sharewith">Share with Conversation</string>
     <string name="title_activity_start_conversation">Start Conversation</string>
     <string name="title_activity_choose_contact">Choose Contact</string>
+    <string name="title_activity_choose_contacts">Choose Contacts</string>
     <string name="title_activity_share_via_account">Share via account</string>
     <string name="title_activity_block_list">Block list</string>
     <string name="just_now">just now</string>
@@ -419,10 +420,6 @@
         <item quantity="one">%d certificate deleted</item>
         <item quantity="other">%d certificates deleted</item>
     </plurals>
-    <plurals name="select_contact">
-        <item quantity="one">Select %d contact</item>
-        <item quantity="other">Select %d contacts</item>
-    </plurals>
     <string name="pref_quick_action_summary">Replace send button with quick action</string>
     <string name="pref_quick_action">Quick Action</string>
     <string name="none">None</string>

src/main/res/values/themes.xml 🔗

@@ -15,11 +15,12 @@
 
         <item name="activity_background_search">@drawable/search_background_light</item>
         <item name="activity_background_no_results">@drawable/no_results_background_light</item>
+        <item name="list_item_background">@drawable/list_item_background_light</item>
 
         <item name="EmojiColor">@color/black</item>
 
         <item name="windowActionModeOverlay">true</item>
-        <item name="android:actionModeBackground">@color/blue_a200</item>
+        <item name="android:actionModeBackground">?colorPrimary</item>
 
         <item name="TextSizeCaption">12sp</item>
         <item name="TextSizeBody1">14sp</item>
@@ -107,6 +108,7 @@
         <item name="color_background_secondary">@color/grey900</item>
         <item name="activity_background_search">@drawable/search_background_dark</item>
         <item name="activity_background_no_results">@drawable/no_results_background_dark</item>
+        <item name="list_item_background">@drawable/list_item_background_dark</item>
         <item name="color_warning">@color/red_a100</item>
 
         <item name="TextColorOnline">@color/green500</item>
@@ -115,7 +117,7 @@
         <item name="EmojiColor">@color/white</item>
 
         <item name="windowActionModeOverlay">true</item>
-        <item name="android:actionModeBackground">@color/blue_a100</item>
+        <item name="android:actionModeBackground">?colorPrimary</item>
 
         <item name="TextSizeCaption">12sp</item>
         <item name="TextSizeBody1">14sp</item>