ChannelDiscoveryActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.AlertDialog;
  4import android.content.Context;
  5import android.content.Intent;
  6import android.content.SharedPreferences;
  7import android.net.Uri;
  8import android.os.Bundle;
  9import android.preference.PreferenceManager;
 10import android.text.Html;
 11import android.text.method.LinkMovementMethod;
 12import android.view.KeyEvent;
 13import android.view.Menu;
 14import android.view.MenuItem;
 15import android.view.View;
 16import android.view.inputmethod.InputMethodManager;
 17import android.widget.EditText;
 18import android.widget.TextView;
 19import android.widget.Toast;
 20
 21import androidx.databinding.DataBindingUtil;
 22
 23import com.google.common.base.Strings;
 24
 25import java.util.Collections;
 26import java.util.List;
 27import java.util.concurrent.atomic.AtomicReference;
 28
 29import eu.siacs.conversations.Config;
 30import eu.siacs.conversations.R;
 31import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding;
 32import eu.siacs.conversations.entities.Account;
 33import eu.siacs.conversations.entities.Bookmark;
 34import eu.siacs.conversations.entities.Conversation;
 35import eu.siacs.conversations.entities.Room;
 36import eu.siacs.conversations.services.ChannelDiscoveryService;
 37import eu.siacs.conversations.services.QuickConversationsService;
 38import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter;
 39import eu.siacs.conversations.ui.util.PendingItem;
 40import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
 41import eu.siacs.conversations.ui.util.StyledAttributes;
 42import eu.siacs.conversations.utils.AccountUtils;
 43import eu.siacs.conversations.xmpp.Jid;
 44
 45public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected {
 46
 47    private static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in";
 48
 49    private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter();
 50    private final PendingItem<String> mInitialSearchValue = new PendingItem<>();
 51    private ActivityChannelDiscoveryBinding binding;
 52    private MenuItem mMenuSearchView;
 53    private EditText mSearchEditText;
 54
 55    private ChannelDiscoveryService.Method method = ChannelDiscoveryService.Method.LOCAL_SERVER;
 56
 57    private boolean optedIn = false;
 58
 59    @Override
 60    protected void refreshUiReal() {
 61
 62    }
 63
 64    @Override
 65    void onBackendConnected() {
 66        if (optedIn || method == ChannelDiscoveryService.Method.LOCAL_SERVER) {
 67            final String query;
 68            if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {
 69                query = mSearchEditText.getText().toString();
 70            } else {
 71                query = mInitialSearchValue.peek();
 72            }
 73            toggleLoadingScreen();
 74            xmppConnectionService.discoverChannels(query, this.method, this);
 75        }
 76    }
 77
 78    @Override
 79    protected void onCreate(final Bundle savedInstanceState) {
 80        super.onCreate(savedInstanceState);
 81        binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery);
 82        setSupportActionBar(binding.toolbar);
 83        configureActionBar(getSupportActionBar(), true);
 84        binding.list.setAdapter(this.adapter);
 85        this.adapter.setOnChannelSearchResultSelectedListener(this);
 86        this.optedIn = getPreferences().getBoolean(CHANNEL_DISCOVERY_OPT_IN, false);
 87
 88        final String search = savedInstanceState == null ? null : savedInstanceState.getString("search");
 89        if (search != null) {
 90            mInitialSearchValue.push(search);
 91        }
 92    }
 93
 94    private static ChannelDiscoveryService.Method getMethod(final Context c) {
 95        if ( Strings.isNullOrEmpty(Config.CHANNEL_DISCOVERY)) {
 96            return ChannelDiscoveryService.Method.LOCAL_SERVER;
 97        }
 98        if (QuickConversationsService.isQuicksy()) {
 99            return ChannelDiscoveryService.Method.JABBER_NETWORK;
100        }
101        final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(c);
102        final String m = p.getString("channel_discovery_method", c.getString(R.string.default_channel_discovery));
103        try {
104            return ChannelDiscoveryService.Method.valueOf(m);
105        } catch (IllegalArgumentException e) {
106            return ChannelDiscoveryService.Method.JABBER_NETWORK;
107        }
108    }
109
110    @Override
111    public boolean onCreateOptionsMenu(final Menu menu) {
112        getMenuInflater().inflate(R.menu.channel_discovery_activity, menu);
113        AccountUtils.showHideMenuItems(menu);
114        mMenuSearchView = menu.findItem(R.id.action_search);
115        final View mSearchView = mMenuSearchView.getActionView();
116        mSearchEditText = mSearchView.findViewById(R.id.search_field);
117        mSearchEditText.setHint(R.string.search_channels);
118        final String initialSearchValue = mInitialSearchValue.pop();
119        if (initialSearchValue != null) {
120            mMenuSearchView.expandActionView();
121            mSearchEditText.append(initialSearchValue);
122            mSearchEditText.requestFocus();
123            if ((optedIn || method == ChannelDiscoveryService.Method.LOCAL_SERVER) && xmppConnectionService != null) {
124                xmppConnectionService.discoverChannels(initialSearchValue, this.method, this);
125            }
126        }
127        mSearchEditText.setOnEditorActionListener(this);
128        mMenuSearchView.setOnActionExpandListener(this);
129        return true;
130    }
131
132    @Override
133    public boolean onMenuItemActionExpand(MenuItem item) {
134        mSearchEditText.post(() -> {
135            mSearchEditText.requestFocus();
136            final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
137            imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
138        });
139        return true;
140    }
141
142    @Override
143    public boolean onMenuItemActionCollapse(MenuItem item) {
144        final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
145        imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
146        mSearchEditText.setText("");
147        toggleLoadingScreen();
148        if (optedIn || method == ChannelDiscoveryService.Method.LOCAL_SERVER) {
149            xmppConnectionService.discoverChannels(null, this.method, this);
150        }
151        return true;
152    }
153
154    private void toggleLoadingScreen() {
155        adapter.submitList(Collections.emptyList());
156        binding.progressBar.setVisibility(View.VISIBLE);
157        binding.list.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_primary));
158    }
159
160    @Override
161    public void onStart() {
162        super.onStart();
163        this.method = getMethod(this);
164        if (!optedIn && method == ChannelDiscoveryService.Method.JABBER_NETWORK) {
165            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
166            builder.setTitle(R.string.channel_discovery_opt_in_title);
167            builder.setMessage(Html.fromHtml(getString(R.string.channel_discover_opt_in_message)));
168            builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
169            builder.setPositiveButton(R.string.confirm, (dialog, which) -> optIn());
170            builder.setOnCancelListener(dialog -> finish());
171            final AlertDialog dialog = builder.create();
172            dialog.setOnShowListener(d -> {
173                final TextView textView = dialog.findViewById(android.R.id.message);
174                if (textView == null) {
175                    return;
176                }
177                textView.setMovementMethod(LinkMovementMethod.getInstance());
178            });
179            dialog.setCanceledOnTouchOutside(false);
180            dialog.show();
181            holdLoading();
182        }
183    }
184
185    private void holdLoading() {
186        adapter.submitList(Collections.emptyList());
187        binding.progressBar.setVisibility(View.GONE);
188        binding.list.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_primary));
189    }
190
191    @Override
192    public void onSaveInstanceState(Bundle savedInstanceState) {
193        if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {
194            savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null);
195        }
196        super.onSaveInstanceState(savedInstanceState);
197    }
198
199    private void optIn() {
200        SharedPreferences preferences = getPreferences();
201        preferences.edit().putBoolean(CHANNEL_DISCOVERY_OPT_IN, true).apply();
202        optedIn = true;
203        toggleLoadingScreen();
204        xmppConnectionService.discoverChannels(null, this.method, this);
205    }
206
207    @Override
208    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
209        if (optedIn || method == ChannelDiscoveryService.Method.LOCAL_SERVER) {
210            toggleLoadingScreen();
211            SoftKeyboardUtils.hideSoftKeyboard(this);
212            xmppConnectionService.discoverChannels(v.getText().toString(), this.method, this);
213        }
214        return true;
215    }
216
217    @Override
218    public void onChannelSearchResultsFound(final List<Room> results) {
219        runOnUiThread(() -> {
220            adapter.submitList(results);
221            binding.progressBar.setVisibility(View.GONE);
222            if (results.size() == 0) {
223                binding.list.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_primary_background_no_results));
224            } else {
225                binding.list.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_primary));
226            }
227        });
228
229    }
230
231    @Override
232    public void onChannelSearchResult(final Room result) {
233        final List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService);
234        if (accounts.size() == 1) {
235            joinChannelSearchResult(accounts.get(0), result);
236        } else if (accounts.size() == 0) {
237            Toast.makeText(this, R.string.please_enable_an_account, Toast.LENGTH_LONG).show();
238        } else {
239            final AtomicReference<String> account = new AtomicReference<>(accounts.get(0));
240            AlertDialog.Builder builder = new AlertDialog.Builder(this);
241            builder.setTitle(R.string.choose_account);
242            builder.setSingleChoiceItems(accounts.toArray(new CharSequence[0]), 0, (dialog, which) -> account.set(accounts.get(which)));
243            builder.setPositiveButton(R.string.join, (dialog, which) -> joinChannelSearchResult(account.get(), result));
244            builder.setNegativeButton(R.string.cancel, null);
245            builder.create().show();
246        }
247
248    }
249
250    @Override
251    public boolean onContextItemSelected(MenuItem item) {
252        final Room room = adapter.getCurrent();
253        if (room != null) {
254            switch (item.getItemId()) {
255                case R.id.share_with:
256                    StartConversationActivity.shareAsChannel(this, room.address);
257                    return true;
258                case R.id.open_join_dialog:
259                    final Intent intent = new Intent(this, StartConversationActivity.class);
260                    intent.setAction(Intent.ACTION_VIEW);
261                    intent.putExtra("force_dialog", true);
262                    intent.setData(Uri.parse(String.format("xmpp:%s?join", room.address)));
263                    startActivity(intent);
264                    return true;
265            }
266        }
267        return false;
268    }
269
270    public void joinChannelSearchResult(String selectedAccount, Room result) {
271        final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK);
272        final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin);
273        final Account account = xmppConnectionService.findAccountByJid(jid);
274        final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true);
275        Bookmark bookmark = conversation.getBookmark();
276        if (bookmark != null) {
277            if (!bookmark.autojoin() && syncAutoJoin) {
278                bookmark.setAutojoin(true);
279                xmppConnectionService.createBookmark(account, bookmark);
280            }
281        } else {
282            bookmark = new Bookmark(account, conversation.getJid().asBareJid());
283            bookmark.setAutojoin(syncAutoJoin);
284            xmppConnectionService.createBookmark(account, bookmark);
285        }
286        switchToConversation(conversation);
287    }
288}