implement channel discovery over jabber.search.network
This commit is contained in:
parent
7a825231fb
commit
a0f88aa9b4
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:background="?attr/color_background_primary"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar" />
|
||||||
|
|
||||||
|
|
||||||
|
<android.support.design.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/coordinator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/color_background_primary">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/color_background_primary"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:padding="@dimen/list_padding">
|
||||||
|
|
||||||
|
<com.makeramen.roundedimageview.RoundedImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:riv_corner_radius="2dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toRightOf="@+id/avatar"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginLeft="@dimen/avatar_item_distance">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Subhead"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/room"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
</layout>
|
|
@ -283,6 +283,7 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MucUsersActivity"
|
android:name=".ui.MucUsersActivity"
|
||||||
android:label="@string/group_chat_members" />
|
android:label="@string/group_chat_members" />
|
||||||
|
<activity android:label="@string/discover_channels" android:name=".ui.ChannelDiscoveryActivity"/>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -40,6 +40,9 @@ public final class Config {
|
||||||
public static final String DOMAIN_LOCK = null; //only allow account creation for this domain
|
public static final String DOMAIN_LOCK = null; //only allow account creation for this domain
|
||||||
public static final String MAGIC_CREATE_DOMAIN = "conversations.im";
|
public static final String MAGIC_CREATE_DOMAIN = "conversations.im";
|
||||||
public static final String QUICKSY_DOMAIN = "quicksy.im";
|
public static final String QUICKSY_DOMAIN = "quicksy.im";
|
||||||
|
|
||||||
|
public static final Jid CHANNEL_DISCOVERY = Jid.of("rodrigo.de.mucobedo@dreckshal.de");
|
||||||
|
|
||||||
public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox
|
public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox
|
||||||
|
|
||||||
public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false;
|
public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.services.AvatarService;
|
||||||
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
public class ChannelSearchResult implements AvatarService.Avatarable {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String description;
|
||||||
|
private final Jid room;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jid getRoom() {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelSearchResult(String name, String description, Jid room) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.room = room;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvatarBackgroundColor() {
|
||||||
|
return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : getName());
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Bookmark;
|
import eu.siacs.conversations.entities.Bookmark;
|
||||||
|
import eu.siacs.conversations.entities.ChannelSearchResult;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Conversational;
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
|
@ -82,11 +83,17 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
|
||||||
return get((ListItem) avatarable, size, cachedOnly);
|
return get((ListItem) avatarable, size, cachedOnly);
|
||||||
} else if (avatarable instanceof MucOptions.User) {
|
} else if (avatarable instanceof MucOptions.User) {
|
||||||
return get((MucOptions.User) avatarable, size, cachedOnly);
|
return get((MucOptions.User) avatarable, size, cachedOnly);
|
||||||
|
} else if (avatarable instanceof ChannelSearchResult) {
|
||||||
|
return get((ChannelSearchResult) avatarable, size, cachedOnly);
|
||||||
}
|
}
|
||||||
throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName());
|
throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Bitmap get(final ChannelSearchResult result, final int size, boolean cacheOnly) {
|
||||||
|
return get(result.getName(), result.getRoom().asBareJid().toEscapedString(), size, cacheOnly);
|
||||||
|
}
|
||||||
|
|
||||||
private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
|
private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
|
||||||
if (contact.isSelf()) {
|
if (contact.isSelf()) {
|
||||||
return get(contact.getAccount(), size, cachedOnly);
|
return get(contact.getAccount(), size, cachedOnly);
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.channels.Channel;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
@ -84,6 +85,7 @@ import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Blockable;
|
import eu.siacs.conversations.entities.Blockable;
|
||||||
import eu.siacs.conversations.entities.Bookmark;
|
import eu.siacs.conversations.entities.Bookmark;
|
||||||
|
import eu.siacs.conversations.entities.ChannelSearchResult;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Conversational;
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
|
@ -112,6 +114,7 @@ import eu.siacs.conversations.ui.UiCallback;
|
||||||
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
|
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
|
||||||
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
|
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
|
||||||
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
|
import eu.siacs.conversations.utils.AccountUtils;
|
||||||
import eu.siacs.conversations.utils.Compatibility;
|
import eu.siacs.conversations.utils.Compatibility;
|
||||||
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
@ -795,6 +798,58 @@ public class XmppConnectionService extends Service {
|
||||||
return pingNow;
|
return pingNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void discoverChannels(String query, OnChannelSearchResultsFound listener) {
|
||||||
|
IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
|
||||||
|
packet.setTo(Config.CHANNEL_DISCOVERY);
|
||||||
|
Element search = packet.addChild("search","https://xmlns.zombofant.net/muclumbus/search/1.0");
|
||||||
|
search.addChild("set","http://jabber.org/protocol/rsm").addChild("max").setContent("100");
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
if (!TextUtils.isEmpty(query)) {
|
||||||
|
bundle.putString("q",query);
|
||||||
|
}
|
||||||
|
Data data = Data.create("https://xmlns.zombofant.net/muclumbus/search/1.0#params", bundle);
|
||||||
|
search.addChild(data);
|
||||||
|
final Account account = AccountUtils.getFirstEnabled(this);
|
||||||
|
if (account == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendIqPacket(account, packet, new OnIqPacketReceived() {
|
||||||
|
@Override
|
||||||
|
public void onIqPacketReceived(Account account, IqPacket response) {
|
||||||
|
ArrayList<ChannelSearchResult> searchResults = new ArrayList<>();
|
||||||
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
|
Element result = response.findChild("result","https://xmlns.zombofant.net/muclumbus/search/1.0");
|
||||||
|
if (result != null) {
|
||||||
|
for(Element child : result.getChildren()) {
|
||||||
|
if ("item".equals(child.getName())) {
|
||||||
|
String name = child.findChildContent("name");
|
||||||
|
String description = child.findChildContent("description");
|
||||||
|
Jid room = child.getAttributeAsJid("address");
|
||||||
|
if (room != null) {
|
||||||
|
searchResults.add(new ChannelSearchResult(name,description,room));
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG,"skipping because room was null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG,"result was null");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG,response.toString());
|
||||||
|
}
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onChannelSearchResultsFound(searchResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnChannelSearchResultsFound {
|
||||||
|
void onChannelSearchResultsFound(List<ChannelSearchResult> results);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDataSaverDisabled() {
|
public boolean isDataSaverDisabled() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
|
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.databinding.DataBindingUtil;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.entities.ChannelSearchResult;
|
||||||
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter;
|
||||||
|
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
|
||||||
|
import eu.siacs.conversations.utils.AccountUtils;
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, XmppConnectionService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected {
|
||||||
|
|
||||||
|
private static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in";
|
||||||
|
|
||||||
|
private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter();
|
||||||
|
|
||||||
|
private EditText mSearchEditText;
|
||||||
|
|
||||||
|
private boolean optedIn = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void refreshUiReal() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onBackendConnected() {
|
||||||
|
if (optedIn) {
|
||||||
|
xmppConnectionService.discoverChannels(null, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ActivityChannelDiscoveryBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery);
|
||||||
|
setSupportActionBar((Toolbar) binding.toolbar);
|
||||||
|
configureActionBar(getSupportActionBar(), true);
|
||||||
|
binding.list.setAdapter(this.adapter);
|
||||||
|
this.adapter.setOnChannelSearchResultSelectedListener(this);
|
||||||
|
optedIn = getPreferences().getBoolean(CHANNEL_DISCOVERY_OPT_IN, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.muc_users_activity, menu);
|
||||||
|
final MenuItem menuSearchView = menu.findItem(R.id.action_search);
|
||||||
|
final View mSearchView = menuSearchView.getActionView();
|
||||||
|
mSearchEditText = mSearchView.findViewById(R.id.search_field);
|
||||||
|
mSearchEditText.setHint(R.string.search_channels);
|
||||||
|
mSearchEditText.setOnEditorActionListener(this);
|
||||||
|
menuSearchView.setOnActionExpandListener(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||||
|
mSearchEditText.post(() -> {
|
||||||
|
mSearchEditText.requestFocus();
|
||||||
|
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||||
|
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||||
|
mSearchEditText.setText("");
|
||||||
|
adapter.submitList(Collections.emptyList());
|
||||||
|
if (optedIn) {
|
||||||
|
xmppConnectionService.discoverChannels(null, this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
if (!optedIn) {
|
||||||
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setTitle(R.string.channel_discovery_opt_in_title);
|
||||||
|
builder.setMessage(Html.fromHtml(getString(R.string.channel_discover_opt_in_message)));
|
||||||
|
builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
|
||||||
|
builder.setPositiveButton(R.string.confirm, (dialog, which) -> optIn());
|
||||||
|
builder.setOnCancelListener(dialog -> finish());
|
||||||
|
final AlertDialog dialog = builder.create();
|
||||||
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void optIn() {
|
||||||
|
SharedPreferences preferences = getPreferences();
|
||||||
|
preferences.edit().putBoolean(CHANNEL_DISCOVERY_OPT_IN,true).apply();
|
||||||
|
optedIn = true;
|
||||||
|
xmppConnectionService.discoverChannels(null, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
if (optedIn) {
|
||||||
|
xmppConnectionService.discoverChannels(v.getText().toString(), this);
|
||||||
|
}
|
||||||
|
adapter.submitList(Collections.emptyList());
|
||||||
|
SoftKeyboardUtils.hideSoftKeyboard(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChannelSearchResultsFound(List<ChannelSearchResult> results) {
|
||||||
|
runOnUiThread(() -> adapter.submitList(results));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChannelSearchResult(final ChannelSearchResult result) {
|
||||||
|
List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService);
|
||||||
|
if (accounts.size() == 1) {
|
||||||
|
joinChannelSearchResult(accounts.get(0),result);
|
||||||
|
} else if (accounts.size() > 0){
|
||||||
|
final AtomicReference<String> account = new AtomicReference<>(accounts.get(0));
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setTitle(R.string.choose_account);
|
||||||
|
builder.setSingleChoiceItems(accounts.toArray(new CharSequence[0]), 0, (dialog, which) -> account.set(accounts.get(which)));
|
||||||
|
builder.setPositiveButton(R.string.join, (dialog, which) -> joinChannelSearchResult(account.get(), result));
|
||||||
|
builder.setNegativeButton(R.string.cancel, null);
|
||||||
|
builder.create().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinChannelSearchResult(String accountJid, ChannelSearchResult result) {
|
||||||
|
Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid));
|
||||||
|
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true);
|
||||||
|
switchToConversation(conversation);
|
||||||
|
}
|
||||||
|
}
|
|
@ -313,6 +313,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
||||||
prefilled = null;
|
prefilled = null;
|
||||||
}
|
}
|
||||||
switch (actionItem.getId()) {
|
switch (actionItem.getId()) {
|
||||||
|
case R.id.discover_public_channels:
|
||||||
|
startActivity(new Intent(this, ChannelDiscoveryActivity.class));
|
||||||
|
break;
|
||||||
case R.id.join_public_channel:
|
case R.id.join_public_channel:
|
||||||
showJoinConferenceDialog(prefilled);
|
showJoinConferenceDialog(prefilled);
|
||||||
break;
|
break;
|
||||||
|
@ -781,15 +784,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
||||||
this.mPostponedActivityResult = null;
|
this.mPostponedActivityResult = null;
|
||||||
}
|
}
|
||||||
this.mActivatedAccounts.clear();
|
this.mActivatedAccounts.clear();
|
||||||
for (Account account : xmppConnectionService.getAccounts()) {
|
this.mActivatedAccounts.addAll(AccountUtils.getEnabledAccounts(xmppConnectionService));
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configureHomeButton();
|
configureHomeButton();
|
||||||
Intent intent = pendingViewIntent.pop();
|
Intent intent = pendingViewIntent.pop();
|
||||||
if (intent != null && processViewIntent(intent)) {
|
if (intent != null && processViewIntent(intent)) {
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package eu.siacs.conversations.ui.adapter;
|
||||||
|
|
||||||
|
import android.databinding.DataBindingUtil;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.recyclerview.extensions.ListAdapter;
|
||||||
|
import android.support.v7.util.DiffUtil;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.databinding.SearchResultItemBinding;
|
||||||
|
import eu.siacs.conversations.entities.ChannelSearchResult;
|
||||||
|
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
|
||||||
|
|
||||||
|
public class ChannelSearchResultAdapter extends ListAdapter<ChannelSearchResult, ChannelSearchResultAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private OnChannelSearchResultSelected listener;
|
||||||
|
|
||||||
|
private static final DiffUtil.ItemCallback<ChannelSearchResult> DIFF = new DiffUtil.ItemCallback<ChannelSearchResult>() {
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) {
|
||||||
|
return a.equals(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public ChannelSearchResultAdapter() {
|
||||||
|
super(DIFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
|
||||||
|
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.search_result_item,viewGroup,false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
|
||||||
|
final ChannelSearchResult searchResult = getItem(position);
|
||||||
|
viewHolder.binding.name.setText(searchResult.getName());
|
||||||
|
final String description = searchResult.getDescription();
|
||||||
|
if (TextUtils.isEmpty(description)) {
|
||||||
|
viewHolder.binding.description.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
viewHolder.binding.description.setText(description);
|
||||||
|
viewHolder.binding.description.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
viewHolder.binding.room.setText(searchResult.getRoom().asBareJid().toString());
|
||||||
|
AvatarWorkerTask.loadAvatar(searchResult, viewHolder.binding.avatar, R.dimen.avatar);
|
||||||
|
viewHolder.binding.getRoot().setOnClickListener(v -> listener.onChannelSearchResult(searchResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnChannelSearchResultSelectedListener(OnChannelSearchResultSelected listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final SearchResultItemBinding binding;
|
||||||
|
|
||||||
|
private ViewHolder(SearchResultItemBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnChannelSearchResultSelected {
|
||||||
|
void onChannelSearchResult(ChannelSearchResult result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,10 @@ import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
@ -22,6 +24,20 @@ public class AccountUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static List<String> getEnabledAccounts(final XmppConnectionService service) {
|
||||||
|
ArrayList<String> accounts = new ArrayList<>();
|
||||||
|
for (Account account : service.getAccounts()) {
|
||||||
|
if (account.getStatus() != Account.State.DISABLED) {
|
||||||
|
if (Config.DOMAIN_LOCK != null) {
|
||||||
|
accounts.add(account.getJid().getLocal());
|
||||||
|
} else {
|
||||||
|
accounts.add(account.getJid().asBareJid().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
public static Account getFirstEnabled(XmppConnectionService service) {
|
public static Account getFirstEnabled(XmppConnectionService service) {
|
||||||
final List<Account> accounts = service.getAccounts();
|
final List<Account> accounts = service.getAccounts();
|
||||||
for(Account account : accounts) {
|
for(Account account : accounts) {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?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/action_search"
|
||||||
|
app:actionLayout="@layout/actionview_search"
|
||||||
|
android:icon="?attr/icon_search"
|
||||||
|
app:showAsAction="collapseActionView|always"
|
||||||
|
android:title="@string/search"/>
|
||||||
|
</menu>
|
|
@ -1,5 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/discover_public_channels"
|
||||||
|
android:title="@string/discover_channels"
|
||||||
|
android:icon="@drawable/ic_search_white_24dp"/>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/join_public_channel"
|
android:id="@+id/join_public_channel"
|
||||||
android:title="@string/join_public_channel"
|
android:title="@string/join_public_channel"
|
||||||
|
|
|
@ -857,4 +857,8 @@
|
||||||
<string name="search_participants">Search participants</string>
|
<string name="search_participants">Search participants</string>
|
||||||
<string name="file_too_large">File too large</string>
|
<string name="file_too_large">File too large</string>
|
||||||
<string name="attach">Attach</string>
|
<string name="attach">Attach</string>
|
||||||
|
<string name="discover_channels">Discover channels</string>
|
||||||
|
<string name="search_channels">Search channels</string>
|
||||||
|
<string name="channel_discovery_opt_in_title">Possible privacy violation!</string>
|
||||||
|
<string name="channel_discover_opt_in_message"><![CDATA[Channel discovery uses a third party service called <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Using this feature will transmit your Jabber ID and search terms to that service. See their <a href="https://search.jabber.network/privacy">Privacy Policy</a> for more information.]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue