1package eu.siacs.conversations.ui;
2
3import android.app.Activity;
4import android.content.Context;
5import android.content.Intent;
6import android.os.Bundle;
7import android.support.annotation.NonNull;
8import android.support.annotation.StringRes;
9import android.support.v4.app.Fragment;
10import android.support.v4.app.FragmentTransaction;
11import android.support.v7.app.ActionBar;
12import android.view.ActionMode;
13import android.view.Menu;
14import android.view.MenuInflater;
15import android.view.MenuItem;
16import android.view.View;
17import android.view.inputmethod.InputMethodManager;
18import android.widget.AbsListView.MultiChoiceModeListener;
19import android.widget.ListView;
20
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collections;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
28import eu.siacs.conversations.Config;
29import eu.siacs.conversations.R;
30import eu.siacs.conversations.entities.Account;
31import eu.siacs.conversations.entities.Contact;
32import eu.siacs.conversations.entities.Conversation;
33import eu.siacs.conversations.entities.ListItem;
34import eu.siacs.conversations.entities.MucOptions;
35import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
36import eu.siacs.conversations.ui.util.ActivityResult;
37import eu.siacs.conversations.ui.util.PendingItem;
38import eu.siacs.conversations.utils.XmppUri;
39import rocks.xmpp.addr.Jid;
40
41public class ChooseContactActivity extends AbstractSearchableListItemActivity {
42 public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
43 private List<String> mActivatedAccounts = new ArrayList<>();
44 private Set<String> selected = new HashSet<>();
45 private Set<String> filterContacts;
46
47 private PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
48
49 public static Intent create(Activity activity, Conversation conversation) {
50 final Intent intent = new Intent(activity, ChooseContactActivity.class);
51 List<String> contacts = new ArrayList<>();
52 if (conversation.getMode() == Conversation.MODE_MULTI) {
53 for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
54 Jid jid = user.getRealJid();
55 if (jid != null) {
56 contacts.add(jid.asBareJid().toString());
57 }
58 }
59 } else {
60 contacts.add(conversation.getJid().asBareJid().toString());
61 }
62 intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
63 intent.putExtra("conversation", conversation.getUuid());
64 intent.putExtra("multiple", true);
65 intent.putExtra("show_enter_jid", true);
66 intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
67 return intent;
68 }
69
70 @Override
71 public void onCreate(final Bundle savedInstanceState) {
72 super.onCreate(savedInstanceState);
73 filterContacts = new HashSet<>();
74 if (savedInstanceState != null) {
75 String[] selectedContacts = savedInstanceState.getStringArray("selected_contacts");
76 if (selectedContacts != null) {
77 selected.clear();
78 selected.addAll(Arrays.asList(selectedContacts));
79 }
80 }
81
82 String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
83 if (contacts != null) {
84 Collections.addAll(filterContacts, contacts);
85 }
86
87 if (getIntent().getBooleanExtra("multiple", false)) {
88 getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
89 getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
90
91 @Override
92 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
93 return false;
94 }
95
96 @Override
97 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
98 binding.fab.setVisibility(View.GONE);
99 final View view = getSearchEditText();
100 final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
101 if (view != null && imm != null) {
102 imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
103 }
104 MenuInflater inflater = getMenuInflater();
105 inflater.inflate(R.menu.select_multiple, menu);
106 MenuItem selectButton = menu.findItem(R.id.selection_submit);
107 String buttonText = getResources().getQuantityString(R.plurals.select_contact, selected.size(), selected.size());
108 selectButton.setTitle(buttonText);
109 return true;
110 }
111
112 @Override
113 public void onDestroyActionMode(ActionMode mode) {
114 binding.fab.setVisibility(View.VISIBLE);
115 selected.clear();
116 }
117
118 @Override
119 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
120 switch (item.getItemId()) {
121 case R.id.selection_submit:
122 final Intent request = getIntent();
123 final Intent data = new Intent();
124 data.putExtra("conversation",
125 request.getStringExtra("conversation"));
126 String[] selection = getSelectedContactJids();
127 data.putExtra("contacts", selection);
128 data.putExtra("multiple", true);
129 data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
130 data.putExtra("subject", request.getStringExtra("subject"));
131 setResult(RESULT_OK, data);
132 finish();
133 return true;
134 }
135 return false;
136 }
137
138 @Override
139 public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
140 Contact item = (Contact) getListItems().get(position);
141 if (checked) {
142 selected.add(item.getJid().toString());
143 } else {
144 selected.remove(item.getJid().toString());
145 }
146 int numSelected = selected.size();
147 MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
148 String buttonText = getResources().getQuantityString(R.plurals.select_contact,
149 numSelected, numSelected);
150 selectButton.setTitle(buttonText);
151 }
152 });
153 }
154
155 getListView().setOnItemClickListener((parent, view, position, id) -> {
156 final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
157 imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
158 final Intent request = getIntent();
159 final Intent data = new Intent();
160 final ListItem mListItem = getListItems().get(position);
161 data.putExtra("contact", mListItem.getJid().toString());
162 String account = request.getStringExtra(EXTRA_ACCOUNT);
163 if (account == null && mListItem instanceof Contact) {
164 account = ((Contact) mListItem).getAccount().getJid().asBareJid().toString();
165 }
166 data.putExtra(EXTRA_ACCOUNT, account);
167 data.putExtra("conversation", request.getStringExtra("conversation"));
168 data.putExtra("multiple", false);
169 data.putExtra("subject", request.getStringExtra("subject"));
170 setResult(RESULT_OK, data);
171 finish();
172 });
173 final Intent i = getIntent();
174 boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
175 if (showEnterJid) {
176 this.binding.fab.setOnClickListener((v) -> showEnterJidDialog(null));
177 } else {
178 this.binding.fab.setVisibility(View.GONE);
179 }
180 }
181
182 @Override
183 public void onStart() {
184 super.onStart();
185 Intent intent = getIntent();
186 @StringRes
187 int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
188 ActionBar bar = getSupportActionBar();
189 if (bar != null) {
190 try {
191 bar.setTitle(res);
192 } catch (Exception e) {
193 bar.setTitle(R.string.title_activity_choose_contact);
194 }
195 }
196 }
197
198 @Override
199 public boolean onCreateOptionsMenu(final Menu menu) {
200 super.onCreateOptionsMenu(menu);
201 final Intent i = getIntent();
202 boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
203 menu.findItem(R.id.action_scan_qr_code).setVisible(isCameraFeatureAvailable() && showEnterJid);
204 return true;
205 }
206
207 @Override
208 public void onSaveInstanceState(Bundle savedInstanceState) {
209 savedInstanceState.putStringArray("selected_contacts",getSelectedContactJids());
210 super.onSaveInstanceState(savedInstanceState);
211 }
212
213 protected void filterContacts(final String needle) {
214 getListItems().clear();
215 if (xmppConnectionService == null) {
216 getListItemAdapter().notifyDataSetChanged();
217 return;
218 }
219 for (final Account account : xmppConnectionService.getAccounts()) {
220 if (account.getStatus() != Account.State.DISABLED) {
221 for (final Contact contact : account.getRoster().getContacts()) {
222 if (contact.showInRoster() &&
223 !filterContacts.contains(contact.getJid().asBareJid().toString())
224 && contact.match(this, needle)) {
225 getListItems().add(contact);
226 }
227 }
228 }
229 }
230 Collections.sort(getListItems());
231 getListItemAdapter().notifyDataSetChanged();
232 }
233
234 private String[] getSelectedContactJids() {
235 return selected.toArray(new String[selected.size()]);
236 }
237
238 public void refreshUiReal() {
239 //nothing to do. This Activity doesn't implement any listeners
240 }
241
242 @Override
243 public boolean onOptionsItemSelected(MenuItem item) {
244 switch (item.getItemId()) {
245 case R.id.action_scan_qr_code:
246 ScanActivity.scan(this);
247 return true;
248 }
249 return super.onOptionsItemSelected(item);
250 }
251
252 protected void showEnterJidDialog(XmppUri uri) {
253 FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
254 Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
255 if (prev != null) {
256 ft.remove(prev);
257 }
258 ft.addToBackStack(null);
259 Jid jid = uri == null ? null : uri.getJid();
260 EnterJidDialog dialog = EnterJidDialog.newInstance(
261 mActivatedAccounts,
262 getString(R.string.enter_contact),
263 getString(R.string.select),
264 jid == null ? null : jid.asBareJid().toString(),
265 getIntent().getStringExtra(EXTRA_ACCOUNT),
266 true
267 );
268
269 dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
270 final Intent request = getIntent();
271 final Intent data = new Intent();
272 data.putExtra("contact", contactJid.toString());
273 data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
274 data.putExtra("conversation",
275 request.getStringExtra("conversation"));
276 data.putExtra("multiple", false);
277 data.putExtra("subject", request.getStringExtra("subject"));
278 setResult(RESULT_OK, data);
279 finish();
280
281 return true;
282 });
283
284 dialog.show(ft, "dialog");
285 }
286
287 @Override
288 public void onActivityResult(int requestCode, int resultCode, Intent intent) {
289 super.onActivityResult(requestCode, requestCode, intent);
290 ActivityResult activityResult = ActivityResult.of(requestCode, resultCode, intent);
291 if (xmppConnectionService != null) {
292 handleActivityResult(activityResult);
293 } else {
294 this.postponedActivityResult.push(activityResult);
295 }
296 }
297
298 private void handleActivityResult(ActivityResult activityResult) {
299 if (activityResult.resultCode == RESULT_OK && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
300 String result = activityResult.data.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
301 XmppUri uri = new XmppUri(result == null ? "" : result);
302 if (uri.isJidValid()) {
303 showEnterJidDialog(uri);
304 }
305 }
306 }
307
308 @Override
309 void onBackendConnected() {
310 filterContacts();
311 this.mActivatedAccounts.clear();
312 for (Account account : xmppConnectionService.getAccounts()) {
313 if (account.getStatus() != Account.State.DISABLED) {
314 if (Config.DOMAIN_LOCK != null) {
315 this.mActivatedAccounts.add(account.getJid().getLocal());
316 } else {
317 this.mActivatedAccounts.add(account.getJid().asBareJid().toString());
318 }
319 }
320 }
321 ActivityResult activityResult = this.postponedActivityResult.pop();
322 if (activityResult != null) {
323 handleActivityResult(activityResult);
324 }
325 Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
326 if (fragment != null && fragment instanceof OnBackendConnected) {
327 ((OnBackendConnected) fragment).onBackendConnected();
328 }
329 }
330
331 @Override
332 public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
333 ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
334 }
335}