From 2fa687b8ae16b93feb9c8f7a7d7e7b7d87182720 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 22 Apr 2019 13:00:45 +0200 Subject: [PATCH] refactor muc search to use http --- build.gradle | 2 + .../java/eu/siacs/conversations/Config.java | 2 +- .../entities/ChannelSearchResult.java | 35 ------ .../http/services/MuclumbusService.java | 85 ++++++++++++++ .../conversations/services/AvatarService.java | 18 +-- .../services/XmppConnectionService.java | 107 +++++++++++------- .../ui/ChannelDiscoveryActivity.java | 54 +++++++-- .../adapter/ChannelSearchResultAdapter.java | 14 +-- src/main/res/values/strings.xml | 2 +- 9 files changed, 211 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java create mode 100644 src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java diff --git a/build.gradle b/build.gradle index dc8c7303f..9dc7576b3 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,8 @@ dependencies { implementation 'org.conscrypt:conscrypt-android:1.3.0' implementation 'me.drakeet.support:toastcompat:1.1.0' implementation "com.leinardi.android:speed-dial:2.0.1" + implementation 'com.squareup.retrofit2:retrofit:2.5.0' + implementation 'com.squareup.retrofit2:converter-gson:2.5.0' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.10.1' } diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 4626bffc8..d95e4d906 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -41,7 +41,7 @@ public final class Config { public static final String MAGIC_CREATE_DOMAIN = "conversations.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 String CHANNEL_DISCOVERY = "https://search.jabbercat.org"; public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox diff --git a/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java b/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java deleted file mode 100644 index 0222f2f85..000000000 --- a/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java +++ /dev/null @@ -1,35 +0,0 @@ -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()); - } -} diff --git a/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java new file mode 100644 index 000000000..2b132d8fc --- /dev/null +++ b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java @@ -0,0 +1,85 @@ +package eu.siacs.conversations.http.services; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import eu.siacs.conversations.services.AvatarService; +import eu.siacs.conversations.utils.UIHelper; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Query; +import rocks.xmpp.addr.Jid; + +public interface MuclumbusService { + + @GET("/api/1.0/rooms/unsafe") + Call getRooms(@Query("p") int page); + + @POST("/api/1.0/search") + Call search(@Body SearchRequest searchRequest); + + class Rooms { + int page; + int total; + int pages; + public List items; + } + + class Room implements AvatarService.Avatarable { + + public String address; + public int nusers; + public boolean is_open; + public String anonymity_mode; + public String name; + public String description; + public String language; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Jid getRoom() { + try { + return Jid.of(address); + } catch (IllegalArgumentException e) { + return null; + } + } + + @Override + public int getAvatarBackgroundColor() { + Jid room = getRoom(); + return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name); + } + } + + class SearchRequest { + + public Set keywords; + + public SearchRequest(String keyword) { + this.keywords = Collections.singleton(keyword); + } + } + + class SearchResult { + + public Result result; + + } + + class Result { + + public List items; + + } + +} diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 055c50005..17e05929c 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -3,7 +3,6 @@ package eu.siacs.conversations.services; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -14,7 +13,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.v4.content.res.ResourcesCompat; import android.text.TextUtils; @@ -33,17 +31,16 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Bookmark; -import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; +import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.XmppConnection; -import eu.siacs.conversations.xmpp.pep.Avatar; import rocks.xmpp.addr.Jid; public class AvatarService implements OnAdvancedStreamFeaturesLoaded { @@ -83,15 +80,20 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return get((ListItem) avatarable, size, cachedOnly); } else if (avatarable instanceof MucOptions.User) { return get((MucOptions.User) avatarable, size, cachedOnly); - } else if (avatarable instanceof ChannelSearchResult) { - return get((ChannelSearchResult) avatarable, size, cachedOnly); + } else if (avatarable instanceof MuclumbusService.Room) { + return get((MuclumbusService.Room) avatarable, size, cachedOnly); } 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 MuclumbusService.Room result, final int size, boolean cacheOnly) { + final Jid room = result.getRoom(); + Conversation conversation = room != null ? mXmppConnectionService.findFirstMuc(room) : null; + if (conversation != null) { + return get(conversation,size,cacheOnly); + } + return get(result.getName(), room != null ? room.asBareJid().toEscapedString() : result.getName(), size, cacheOnly); } private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 3c2164902..45b5e9f97 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -49,7 +49,6 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.io.File; import java.net.URL; -import java.nio.channels.Channel; import java.security.SecureRandom; import java.security.Security; import java.security.cert.CertificateException; @@ -69,6 +68,9 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -85,7 +87,6 @@ import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Bookmark; -import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; @@ -102,6 +103,7 @@ import eu.siacs.conversations.generator.MessageGenerator; import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.http.CustomURLStreamHandlerFactory; +import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.parser.AbstractParser; import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.parser.MessageParser; @@ -114,7 +116,6 @@ import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.ui.interfaces.OnAvatarPublication; import eu.siacs.conversations.ui.interfaces.OnMediaLoaded; import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable; -import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.ConversationsFileObserver; import eu.siacs.conversations.utils.CryptoHelper; @@ -154,6 +155,11 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import me.leolin.shortcutbadger.ShortcutBadger; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; import rocks.xmpp.addr.Jid; public class XmppConnectionService extends Service { @@ -236,6 +242,7 @@ public class XmppConnectionService extends Service { private AvatarService mAvatarService = new AvatarService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private PushManagementService mPushManagementService = new PushManagementService(this); + private MuclumbusService muclumbusService; private QuickConversationsService mQuickConversationsService = new QuickConversationsService(this); private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( Environment.getExternalStorageDirectory().getAbsolutePath() @@ -798,56 +805,61 @@ public class XmppConnectionService extends Service { 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); + public void discoverChannels(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { + Log.d(Config.LOGTAG,"discover channels. query="+query); + if (query == null || query.trim().isEmpty()) { + discoverChannelsInternal(onChannelSearchResultsFound); + } else { + discoverChannelsInternal(query, onChannelSearchResultsFound); } - 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 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"); + } + + private void discoverChannelsInternal(OnChannelSearchResultsFound listener) { + Call call = muclumbusService.getRooms(1); + try { + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + final MuclumbusService.Rooms body = response.body(); + if (body == null) { + return; } - } else { - Log.d(Config.LOGTAG,response.toString()); - } - if (listener != null) { - listener.onChannelSearchResultsFound(searchResults); + listener.onChannelSearchResultsFound(body.items); } + @Override + public void onFailure(Call call, Throwable throwable) { + + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void discoverChannelsInternal(String query, OnChannelSearchResultsFound listener) { + Call searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query)); + + searchResultCall.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + System.out.println(response.message()); + MuclumbusService.SearchResult body = response.body(); + if (body == null) { + return; + } + listener.onChannelSearchResultsFound(body.result.items); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + throwable.printStackTrace(); } }); } public interface OnChannelSearchResultsFound { - void onChannelSearchResultsFound(List results); + void onChannelSearchResultsFound(List results); } public boolean isDataSaverDisabled() { @@ -1118,6 +1130,13 @@ public class XmppConnectionService extends Service { } mForceDuringOnCreate.set(false); toggleForegroundService(); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(Config.CHANNEL_DISCOVERY) + .addConverterFactory(GsonConverterFactory.create()) + .callbackExecutor(Executors.newSingleThreadExecutor()) + .build(); + muclumbusService = retrofit.create(MuclumbusService.class); } private void checkForDeletedFiles() { diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java index 5b0c30ed3..03211c9ec 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -2,13 +2,12 @@ package eu.siacs.conversations.ui; import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; +import android.content.Intent; 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; @@ -17,19 +16,18 @@ 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.http.services.MuclumbusService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter; +import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.SoftKeyboardUtils; import eu.siacs.conversations.utils.AccountUtils; import rocks.xmpp.addr.Jid; @@ -40,6 +38,9 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter(); + private final PendingItem mInitialSearchValue = new PendingItem<>(); + + private MenuItem mMenuSearchView; private EditText mSearchEditText; private boolean optedIn = false; @@ -52,7 +53,13 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O @Override void onBackendConnected() { if (optedIn) { - xmppConnectionService.discoverChannels(null, this); + String query; + if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) { + query = mSearchEditText.getText().toString(); + } else { + query = mInitialSearchValue.peek(); + } + xmppConnectionService.discoverChannels(query, this); } } @@ -65,17 +72,32 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O binding.list.setAdapter(this.adapter); this.adapter.setOnChannelSearchResultSelectedListener(this); optedIn = getPreferences().getBoolean(CHANNEL_DISCOVERY_OPT_IN, false); + + final String search = savedInstanceState == null ? null : savedInstanceState.getString("search"); + if (search != null) { + mInitialSearchValue.push(search); + } + } @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(); + mMenuSearchView = menu.findItem(R.id.action_search); + final View mSearchView = mMenuSearchView.getActionView(); mSearchEditText = mSearchView.findViewById(R.id.search_field); mSearchEditText.setHint(R.string.search_channels); + String initialSearchValue = mInitialSearchValue.pop(); + if (initialSearchValue != null) { + mMenuSearchView.expandActionView(); + mSearchEditText.append(initialSearchValue); + mSearchEditText.requestFocus(); + if (optedIn) { + xmppConnectionService.discoverChannels(initialSearchValue, this); + } + } mSearchEditText.setOnEditorActionListener(this); - menuSearchView.setOnActionExpandListener(this); + mMenuSearchView.setOnActionExpandListener(this); return true; } @@ -117,6 +139,14 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } } + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) { + savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null); + } + super.onSaveInstanceState(savedInstanceState); + } + private void optIn() { SharedPreferences preferences = getPreferences(); preferences.edit().putBoolean(CHANNEL_DISCOVERY_OPT_IN,true).apply(); @@ -135,13 +165,13 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } @Override - public void onChannelSearchResultsFound(List results) { + public void onChannelSearchResultsFound(List results) { runOnUiThread(() -> adapter.submitList(results)); } @Override - public void onChannelSearchResult(final ChannelSearchResult result) { + public void onChannelSearchResult(final MuclumbusService.Room result) { List accounts = AccountUtils.getEnabledAccounts(xmppConnectionService); if (accounts.size() == 1) { joinChannelSearchResult(accounts.get(0),result); @@ -157,7 +187,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } - public void joinChannelSearchResult(String accountJid, ChannelSearchResult result) { + public void joinChannelSearchResult(String accountJid, MuclumbusService.Room result) { Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid)); final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); switchToConversation(conversation); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java index ae1c70c1e..42c6647ed 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java @@ -12,21 +12,21 @@ 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.http.services.MuclumbusService; import eu.siacs.conversations.ui.util.AvatarWorkerTask; -public class ChannelSearchResultAdapter extends ListAdapter { +public class ChannelSearchResultAdapter extends ListAdapter { private OnChannelSearchResultSelected listener; - private static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() { + private static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() { @Override - public boolean areItemsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) { + public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { return false; } @Override - public boolean areContentsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) { + public boolean areContentsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { return a.equals(b); } }; @@ -43,7 +43,7 @@ public class ChannelSearchResultAdapter extends ListAdapterDiscover channels Search channels Possible privacy violation! - search.jabber.network.

Using this feature will transmit your Jabber ID and search terms to that service. See their Privacy Policy for more information.]]>
+ search.jabbercat.org.

Using this feature will transmit your IP address and search terms to that service. See their Privacy Policy for more information.]]>