diff --git a/build.gradle b/build.gradle index 240736afc..c3242f851 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,7 @@ dependencies { 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' + implementation 'com.google.guava:guava:27.1-android' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.10.1' } diff --git a/proguard-rules.pro b/proguard-rules.pro index afd589c54..3c3593577 100644 --- a/proguard-rules.pro +++ b/proguard-rules.pro @@ -18,3 +18,5 @@ -dontwarn org.bouncycastle.cert.dane.** -dontwarn rocks.xmpp.addr.** -dontwarn com.google.firebase.analytics.connector.AnalyticsConnector +-dontwarn java.lang.** +-dontwarn javax.lang.** diff --git a/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java index 2b132d8fc..5ef557c0f 100644 --- a/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java +++ b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.http.services; +import com.google.common.base.Objects; + import java.util.Collections; import java.util.List; import java.util.Set; @@ -31,12 +33,8 @@ public interface MuclumbusService { 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; @@ -59,6 +57,21 @@ public interface MuclumbusService { Jid room = getRoom(); return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Room room = (Room) o; + return Objects.equal(address, room.address) && + Objects.equal(name, room.name) && + Objects.equal(description, room.description); + } + + @Override + public int hashCode() { + return Objects.hashCode(address, name, description); + } } class SearchRequest { diff --git a/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java new file mode 100644 index 000000000..1c7c6883e --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java @@ -0,0 +1,106 @@ +package eu.siacs.conversations.services; + +import android.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.http.services.MuclumbusService; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class ChannelDiscoveryService { + + private final XmppConnectionService service; + + + private final MuclumbusService muclumbusService; + + private final Cache> cache; + + public ChannelDiscoveryService(XmppConnectionService service) { + this.service = service; + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(Config.CHANNEL_DISCOVERY) + .addConverterFactory(GsonConverterFactory.create()) + .callbackExecutor(Executors.newSingleThreadExecutor()) + .build(); + this.muclumbusService = retrofit.create(MuclumbusService.class); + this.cache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); + } + + public void discover(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { + final boolean all = query == null || query.trim().isEmpty(); + Log.d(Config.LOGTAG, "discover channels. query=" + query); + List result = cache.getIfPresent(all ? "" : query); + if (result != null) { + onChannelSearchResultsFound.onChannelSearchResultsFound(result); + return; + } + if (all) { + discoverChannels(onChannelSearchResultsFound); + } else { + discoverChannels(query, onChannelSearchResultsFound); + } + } + + private void discoverChannels(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; + } + cache.put("", body.items); + listener.onChannelSearchResultsFound(body.items); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void discoverChannels(final 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; + } + cache.put(query, body.result.items); + listener.onChannelSearchResultsFound(body.result.items); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + throwable.printStackTrace(); + } + }); + } + + public interface OnChannelSearchResultsFound { + void onChannelSearchResultsFound(List results); + } +} diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 45b5e9f97..575cac60e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -209,6 +209,7 @@ public class XmppConnectionService extends Service { private FileBackend fileBackend = new FileBackend(this); private MemorizingTrustManager mMemorizingTrustManager; private NotificationService mNotificationService = new NotificationService(this); + private ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this); private ShortcutService mShortcutService = new ShortcutService(this); private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false); private AtomicBoolean mForceForegroundService = new AtomicBoolean(false); @@ -242,7 +243,6 @@ 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() @@ -805,61 +805,8 @@ public class XmppConnectionService extends Service { return pingNow; } - 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); - } - } - - 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; - } - 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); + public void discoverChannels(String query, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { + mChannelDiscoveryService.discover(query, onChannelSearchResultsFound); } public boolean isDataSaverDisabled() { @@ -1130,13 +1077,6 @@ 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 03211c9ec..ab29c6f86 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.ui; import android.app.AlertDialog; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.databinding.DataBindingUtil; import android.os.Bundle; @@ -25,19 +24,21 @@ import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.http.services.MuclumbusService; -import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.services.ChannelDiscoveryService; 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; -public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, XmppConnectionService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected { +public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected { private static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in"; private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter(); + private ActivityChannelDiscoveryBinding binding; + private final PendingItem mInitialSearchValue = new PendingItem<>(); private MenuItem mMenuSearchView; @@ -66,7 +67,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityChannelDiscoveryBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery); + binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery); setSupportActionBar((Toolbar) binding.toolbar); configureActionBar(getSupportActionBar(), true); binding.list.setAdapter(this.adapter); @@ -116,13 +117,18 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); mSearchEditText.setText(""); - adapter.submitList(Collections.emptyList()); + toggleLoadingScreen(); if (optedIn) { xmppConnectionService.discoverChannels(null, this); } return true; } + private void toggleLoadingScreen() { + adapter.submitList(Collections.emptyList()); + binding.progressBar.setVisibility(View.VISIBLE); + } + @Override public void onStart() { super.onStart(); @@ -159,14 +165,18 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O if (optedIn) { xmppConnectionService.discoverChannels(v.getText().toString(), this); } - adapter.submitList(Collections.emptyList()); + toggleLoadingScreen(); SoftKeyboardUtils.hideSoftKeyboard(this); return true; } @Override public void onChannelSearchResultsFound(List results) { - runOnUiThread(() -> adapter.submitList(results)); + runOnUiThread(() -> { + adapter.submitList(results); + binding.list.setVisibility(View.VISIBLE); + binding.progressBar.setVisibility(View.GONE); + }); } 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 42c6647ed..7d1a7c873 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java @@ -22,7 +22,7 @@ public class ChannelSearchResultAdapter extends ListAdapter DIFF = new DiffUtil.ItemCallback() { @Override public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { - return false; + return a.address != null && a.address.equals(b.address); } @Override diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java index d888d3198..405ddcb41 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.ui.adapter; -import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.databinding.DataBindingUtil; @@ -8,7 +7,6 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.support.annotation.AttrRes; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -17,7 +15,6 @@ import android.widget.ImageView; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.RejectedExecutionException; @@ -26,7 +23,6 @@ import eu.siacs.conversations.databinding.MediaPreviewBinding; import eu.siacs.conversations.ui.ConversationFragment; import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.util.Attachment; -import eu.siacs.conversations.ui.util.StyledAttributes; public class MediaPreviewAdapter extends RecyclerView.Adapter { diff --git a/src/main/res/layout/activity_channel_discovery.xml b/src/main/res/layout/activity_channel_discovery.xml index 679c5156e..6ed46433e 100644 --- a/src/main/res/layout/activity_channel_discovery.xml +++ b/src/main/res/layout/activity_channel_discovery.xml @@ -14,18 +14,26 @@ layout="@layout/toolbar" /> + + -