ChooseContactActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.Activity;
  4import android.content.Context;
  5import android.content.Intent;
  6import android.content.SharedPreferences;
  7import android.os.Bundle;
  8import android.support.annotation.NonNull;
  9import android.support.annotation.StringRes;
 10import android.support.v4.app.Fragment;
 11import android.support.v4.app.FragmentTransaction;
 12import android.support.v7.app.ActionBar;
 13import android.view.ActionMode;
 14import android.view.KeyEvent;
 15import android.view.Menu;
 16import android.view.MenuItem;
 17import android.view.View;
 18import android.view.inputmethod.InputMethodManager;
 19import android.widget.AbsListView.MultiChoiceModeListener;
 20import android.widget.AdapterView;
 21import android.widget.ListView;
 22import android.widget.TextView;
 23
 24import java.util.ArrayList;
 25import java.util.Arrays;
 26import java.util.Collections;
 27import java.util.HashSet;
 28import java.util.List;
 29import java.util.Set;
 30
 31import eu.siacs.conversations.Config;
 32import eu.siacs.conversations.R;
 33import eu.siacs.conversations.entities.Account;
 34import eu.siacs.conversations.entities.Contact;
 35import eu.siacs.conversations.entities.Conversation;
 36import eu.siacs.conversations.entities.ListItem;
 37import eu.siacs.conversations.entities.MucOptions;
 38import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 39import eu.siacs.conversations.ui.util.ActivityResult;
 40import eu.siacs.conversations.ui.util.PendingItem;
 41import eu.siacs.conversations.utils.XmppUri;
 42import eu.siacs.conversations.xmpp.Jid;
 43
 44public class ChooseContactActivity extends AbstractSearchableListItemActivity implements MultiChoiceModeListener, AdapterView.OnItemClickListener {
 45    public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
 46    public static final String EXTRA_GROUP_CHAT_NAME = "extra_group_chat_name";
 47    public static final String EXTRA_SELECT_MULTIPLE = "extra_select_multiple";
 48    public static final String EXTRA_SHOW_ENTER_JID = "extra_show_enter_jid";
 49    public static final String EXTRA_CONVERSATION = "extra_conversation";
 50    private static final String EXTRA_FILTERED_CONTACTS = "extra_filtered_contacts";
 51    private List<String> mActivatedAccounts = new ArrayList<>();
 52    private Set<String> selected = new HashSet<>();
 53    private Set<String> filterContacts;
 54
 55    private boolean showEnterJid = false;
 56    private boolean startSearching = false;
 57    private boolean multiple = false;
 58
 59    private PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
 60
 61    public static Intent create(Activity activity, Conversation conversation) {
 62        final Intent intent = new Intent(activity, ChooseContactActivity.class);
 63        List<String> contacts = new ArrayList<>();
 64        if (conversation.getMode() == Conversation.MODE_MULTI) {
 65            for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
 66                Jid jid = user.getRealJid();
 67                if (jid != null) {
 68                    contacts.add(jid.asBareJid().toString());
 69                }
 70            }
 71        } else {
 72            contacts.add(conversation.getJid().asBareJid().toString());
 73        }
 74        intent.putExtra(EXTRA_FILTERED_CONTACTS, contacts.toArray(new String[contacts.size()]));
 75        intent.putExtra(EXTRA_CONVERSATION, conversation.getUuid());
 76        intent.putExtra(EXTRA_SELECT_MULTIPLE, true);
 77        intent.putExtra(EXTRA_SHOW_ENTER_JID, true);
 78        intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
 79        return intent;
 80    }
 81
 82    public static List<Jid> extractJabberIds(Intent result) {
 83        List<Jid> jabberIds = new ArrayList<>();
 84        try {
 85            if (result.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false)) {
 86                String[] toAdd = result.getStringArrayExtra("contacts");
 87                for (String item : toAdd) {
 88                    jabberIds.add(Jid.of(item));
 89                }
 90            } else {
 91                jabberIds.add(Jid.of(result.getStringExtra("contact")));
 92            }
 93            return jabberIds;
 94        } catch (IllegalArgumentException e) {
 95            return jabberIds;
 96        }
 97    }
 98
 99    @Override
100    public void onCreate(final Bundle savedInstanceState) {
101        super.onCreate(savedInstanceState);
102        filterContacts = new HashSet<>();
103        if (savedInstanceState != null) {
104            String[] selectedContacts = savedInstanceState.getStringArray("selected_contacts");
105            if (selectedContacts != null) {
106                selected.clear();
107                selected.addAll(Arrays.asList(selectedContacts));
108            }
109        }
110
111        String[] contacts = getIntent().getStringArrayExtra(EXTRA_FILTERED_CONTACTS);
112        if (contacts != null) {
113            Collections.addAll(filterContacts, contacts);
114        }
115
116        Intent intent = getIntent();
117
118        multiple = intent.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false);
119        if (multiple) {
120            getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
121            getListView().setMultiChoiceModeListener(this);
122        }
123
124        getListView().setOnItemClickListener(this);
125        this.showEnterJid = intent.getBooleanExtra(EXTRA_SHOW_ENTER_JID, false);
126        this.binding.fab.setOnClickListener(this::onFabClicked);
127        if (this.showEnterJid) {
128            this.binding.fab.show();
129        } else {
130            binding.fab.setImageResource(R.drawable.ic_forward_white_24dp);
131        }
132
133        final SharedPreferences preferences = getPreferences();
134        this.startSearching = intent.getBooleanExtra("direct_search", false) && preferences.getBoolean("start_searching", getResources().getBoolean(R.bool.start_searching));
135
136    }
137
138    private void onFabClicked(View v) {
139        if (selected.size() == 0) {
140            showEnterJidDialog(null);
141        } else {
142            submitSelection();
143        }
144    }
145
146    @Override
147    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
148        return false;
149    }
150
151    @Override
152    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
153        mode.setTitle(getTitleFromIntent());
154        binding.fab.setImageResource(R.drawable.ic_forward_white_24dp);
155        binding.fab.show();
156        final View view = getSearchEditText();
157        final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
158        if (view != null && imm != null) {
159            imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
160        }
161        return true;
162    }
163
164    @Override
165    public void onDestroyActionMode(ActionMode mode) {
166        this.binding.fab.setImageResource(R.drawable.ic_person_add_white_24dp);
167        if (this.showEnterJid) {
168            this.binding.fab.show();
169        } else {
170            this.binding.fab.hide();
171        }
172        selected.clear();
173    }
174
175    @Override
176    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
177        return false;
178    }
179
180    private void submitSelection() {
181        final Intent request = getIntent();
182        final Intent data = new Intent();
183        data.putExtra("contacts", getSelectedContactJids());
184        data.putExtra(EXTRA_SELECT_MULTIPLE, true);
185        data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
186        copy(request, data);
187        setResult(RESULT_OK, data);
188        finish();
189    }
190
191    private static void copy(Intent from, Intent to) {
192        to.putExtra(EXTRA_CONVERSATION, from.getStringExtra(EXTRA_CONVERSATION));
193        to.putExtra(EXTRA_GROUP_CHAT_NAME, from.getStringExtra(EXTRA_GROUP_CHAT_NAME));
194    }
195
196    @Override
197    public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
198        if (selected.size() != 0) {
199            getListView().playSoundEffect(0);
200        }
201        Contact item = (Contact) getListItems().get(position);
202        if (checked) {
203            selected.add(item.getJid().toString());
204        } else {
205            selected.remove(item.getJid().toString());
206        }
207    }
208
209    @Override
210    public void onStart() {
211        super.onStart();
212        ActionBar bar = getSupportActionBar();
213        if (bar != null) {
214            try {
215                bar.setTitle(getTitleFromIntent());
216            } catch (Exception e) {
217                bar.setTitle(R.string.title_activity_choose_contact);
218            }
219        }
220    }
221
222    public @StringRes
223    int getTitleFromIntent() {
224        final Intent intent = getIntent();
225        boolean multiple = intent != null && intent.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false);
226        @StringRes int fallback = multiple ? R.string.title_activity_choose_contacts : R.string.title_activity_choose_contact;
227        return intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, fallback) : fallback;
228    }
229
230    @Override
231    public boolean onCreateOptionsMenu(final Menu menu) {
232        super.onCreateOptionsMenu(menu);
233        final Intent i = getIntent();
234        boolean showEnterJid = i != null && i.getBooleanExtra(EXTRA_SHOW_ENTER_JID, false);
235        menu.findItem(R.id.action_scan_qr_code).setVisible(isCameraFeatureAvailable() && showEnterJid);
236        MenuItem mMenuSearchView = menu.findItem(R.id.action_search);
237        if (startSearching) {
238            mMenuSearchView.expandActionView();
239        }
240        return true;
241    }
242
243    @Override
244    public void onSaveInstanceState(Bundle savedInstanceState) {
245        savedInstanceState.putStringArray("selected_contacts", getSelectedContactJids());
246        super.onSaveInstanceState(savedInstanceState);
247    }
248
249    @Override
250    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
251        if (multiple) {
252            return false;
253        } else {
254            List<ListItem> items = getListItems();
255            if (items.size() == 1) {
256                onListItemClicked(items.get(0));
257                return true;
258            }
259            return false;
260        }
261    }
262
263    protected void filterContacts(final String needle) {
264        getListItems().clear();
265        if (xmppConnectionService == null) {
266            getListItemAdapter().notifyDataSetChanged();
267            return;
268        }
269        for (final Account account : xmppConnectionService.getAccounts()) {
270            if (account.getStatus() != Account.State.DISABLED) {
271                for (final Contact contact : account.getRoster().getContacts()) {
272                    if (contact.showInContactList() &&
273                            !filterContacts.contains(contact.getJid().asBareJid().toString())
274                            && contact.match(this, needle)) {
275                        getListItems().add(contact);
276                    }
277                }
278            }
279        }
280        Collections.sort(getListItems());
281        getListItemAdapter().notifyDataSetChanged();
282    }
283
284    private String[] getSelectedContactJids() {
285        return selected.toArray(new String[0]);
286    }
287
288    public void refreshUiReal() {
289        //nothing to do. This Activity doesn't implement any listeners
290    }
291
292    @Override
293    public boolean onOptionsItemSelected(MenuItem item) {
294        switch (item.getItemId()) {
295            case R.id.action_scan_qr_code:
296                ScanActivity.scan(this);
297                return true;
298        }
299        return super.onOptionsItemSelected(item);
300    }
301
302    protected void showEnterJidDialog(XmppUri uri) {
303        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
304        Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
305        if (prev != null) {
306            ft.remove(prev);
307        }
308        ft.addToBackStack(null);
309        Jid jid = uri == null ? null : uri.getJid();
310        EnterJidDialog dialog = EnterJidDialog.newInstance(
311                mActivatedAccounts,
312                getString(R.string.enter_contact),
313                getString(R.string.select),
314                jid == null ? null : jid.asBareJid().toString(),
315                getIntent().getStringExtra(EXTRA_ACCOUNT),
316                true,
317                false
318        );
319
320        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
321            final Intent request = getIntent();
322            final Intent data = new Intent();
323            data.putExtra("contact", contactJid.toString());
324            data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
325            data.putExtra(EXTRA_SELECT_MULTIPLE, false);
326            copy(request, data);
327            setResult(RESULT_OK, data);
328            finish();
329
330            return true;
331        });
332
333        dialog.show(ft, "dialog");
334    }
335
336    @Override
337    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
338        super.onActivityResult(requestCode, requestCode, intent);
339        ActivityResult activityResult = ActivityResult.of(requestCode, resultCode, intent);
340        if (xmppConnectionService != null) {
341            handleActivityResult(activityResult);
342        } else {
343            this.postponedActivityResult.push(activityResult);
344        }
345    }
346
347    private void handleActivityResult(ActivityResult activityResult) {
348        if (activityResult.resultCode == RESULT_OK && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
349            String result = activityResult.data.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
350            XmppUri uri = new XmppUri(result == null ? "" : result);
351            if (uri.isValidJid()) {
352                showEnterJidDialog(uri);
353            }
354        }
355    }
356
357    @Override
358    void onBackendConnected() {
359        filterContacts();
360        this.mActivatedAccounts.clear();
361        for (Account account : xmppConnectionService.getAccounts()) {
362            if (account.getStatus() != Account.State.DISABLED) {
363                if (Config.DOMAIN_LOCK != null) {
364                    this.mActivatedAccounts.add(account.getJid().getEscapedLocal());
365                } else {
366                    this.mActivatedAccounts.add(account.getJid().asBareJid().toEscapedString());
367                }
368            }
369        }
370        ActivityResult activityResult = this.postponedActivityResult.pop();
371        if (activityResult != null) {
372            handleActivityResult(activityResult);
373        }
374        final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
375        if (fragment instanceof OnBackendConnected) {
376            ((OnBackendConnected) fragment).onBackendConnected();
377        }
378    }
379
380    @Override
381    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
382        ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
383    }
384
385    @Override
386    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
387        if (multiple) {
388            startActionMode(this);
389            getListView().setItemChecked(position, true);
390            return;
391        }
392        final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
393        imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
394        final ListItem mListItem = getListItems().get(position);
395        onListItemClicked(mListItem);
396    }
397
398    private void onListItemClicked(ListItem item) {
399        final Intent request = getIntent();
400        final Intent data = new Intent();
401        data.putExtra("contact", item.getJid().toString());
402        String account = request.getStringExtra(EXTRA_ACCOUNT);
403        if (account == null && item instanceof Contact) {
404            account = ((Contact) item).getAccount().getJid().asBareJid().toString();
405        }
406        data.putExtra(EXTRA_ACCOUNT, account);
407        data.putExtra(EXTRA_SELECT_MULTIPLE, false);
408        copy(request, data);
409        setResult(RESULT_OK, data);
410        finish();
411    }
412}