cache channel search results

This commit is contained in:
Daniel Gultsch 2019-04-24 13:25:54 +02:00
parent ba21c9b55f
commit ff84ee6964
9 changed files with 156 additions and 80 deletions

View file

@ -66,6 +66,7 @@ dependencies {
implementation "com.leinardi.android:speed-dial:2.0.1" implementation "com.leinardi.android:speed-dial:2.0.1"
implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson: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' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.10.1'
} }

2
proguard-rules.pro vendored
View file

@ -18,3 +18,5 @@
-dontwarn org.bouncycastle.cert.dane.** -dontwarn org.bouncycastle.cert.dane.**
-dontwarn rocks.xmpp.addr.** -dontwarn rocks.xmpp.addr.**
-dontwarn com.google.firebase.analytics.connector.AnalyticsConnector -dontwarn com.google.firebase.analytics.connector.AnalyticsConnector
-dontwarn java.lang.**
-dontwarn javax.lang.**

View file

@ -1,5 +1,7 @@
package eu.siacs.conversations.http.services; package eu.siacs.conversations.http.services;
import com.google.common.base.Objects;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -31,12 +33,8 @@ public interface MuclumbusService {
class Room implements AvatarService.Avatarable { class Room implements AvatarService.Avatarable {
public String address; public String address;
public int nusers;
public boolean is_open;
public String anonymity_mode;
public String name; public String name;
public String description; public String description;
public String language;
public String getName() { public String getName() {
return name; return name;
@ -59,6 +57,21 @@ public interface MuclumbusService {
Jid room = getRoom(); Jid room = getRoom();
return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name); 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 { class SearchRequest {

View file

@ -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<String, List<MuclumbusService.Room>> 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<MuclumbusService.Room> 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<MuclumbusService.Rooms> call = muclumbusService.getRooms(1);
try {
call.enqueue(new Callback<MuclumbusService.Rooms>() {
@Override
public void onResponse(Call<MuclumbusService.Rooms> call, Response<MuclumbusService.Rooms> response) {
final MuclumbusService.Rooms body = response.body();
if (body == null) {
return;
}
cache.put("", body.items);
listener.onChannelSearchResultsFound(body.items);
}
@Override
public void onFailure(Call<MuclumbusService.Rooms> call, Throwable throwable) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void discoverChannels(final String query, OnChannelSearchResultsFound listener) {
Call<MuclumbusService.SearchResult> searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query));
searchResultCall.enqueue(new Callback<MuclumbusService.SearchResult>() {
@Override
public void onResponse(Call<MuclumbusService.SearchResult> call, Response<MuclumbusService.SearchResult> 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<MuclumbusService.SearchResult> call, Throwable throwable) {
throwable.printStackTrace();
}
});
}
public interface OnChannelSearchResultsFound {
void onChannelSearchResultsFound(List<MuclumbusService.Room> results);
}
}

View file

@ -209,6 +209,7 @@ public class XmppConnectionService extends Service {
private FileBackend fileBackend = new FileBackend(this); private FileBackend fileBackend = new FileBackend(this);
private MemorizingTrustManager mMemorizingTrustManager; private MemorizingTrustManager mMemorizingTrustManager;
private NotificationService mNotificationService = new NotificationService(this); private NotificationService mNotificationService = new NotificationService(this);
private ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this);
private ShortcutService mShortcutService = new ShortcutService(this); private ShortcutService mShortcutService = new ShortcutService(this);
private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false); private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false);
private AtomicBoolean mForceForegroundService = 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 AvatarService mAvatarService = new AvatarService(this);
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private PushManagementService mPushManagementService = new PushManagementService(this); private PushManagementService mPushManagementService = new PushManagementService(this);
private MuclumbusService muclumbusService;
private QuickConversationsService mQuickConversationsService = new QuickConversationsService(this); private QuickConversationsService mQuickConversationsService = new QuickConversationsService(this);
private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( private final ConversationsFileObserver fileObserver = new ConversationsFileObserver(
Environment.getExternalStorageDirectory().getAbsolutePath() Environment.getExternalStorageDirectory().getAbsolutePath()
@ -805,61 +805,8 @@ public class XmppConnectionService extends Service {
return pingNow; return pingNow;
} }
public void discoverChannels(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { public void discoverChannels(String query, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) {
Log.d(Config.LOGTAG,"discover channels. query="+query); mChannelDiscoveryService.discover(query, onChannelSearchResultsFound);
if (query == null || query.trim().isEmpty()) {
discoverChannelsInternal(onChannelSearchResultsFound);
} else {
discoverChannelsInternal(query, onChannelSearchResultsFound);
}
}
private void discoverChannelsInternal(OnChannelSearchResultsFound listener) {
Call<MuclumbusService.Rooms> call = muclumbusService.getRooms(1);
try {
call.enqueue(new Callback<MuclumbusService.Rooms>() {
@Override
public void onResponse(Call<MuclumbusService.Rooms> call, Response<MuclumbusService.Rooms> response) {
final MuclumbusService.Rooms body = response.body();
if (body == null) {
return;
}
listener.onChannelSearchResultsFound(body.items);
}
@Override
public void onFailure(Call<MuclumbusService.Rooms> call, Throwable throwable) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void discoverChannelsInternal(String query, OnChannelSearchResultsFound listener) {
Call<MuclumbusService.SearchResult> searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query));
searchResultCall.enqueue(new Callback<MuclumbusService.SearchResult>() {
@Override
public void onResponse(Call<MuclumbusService.SearchResult> call, Response<MuclumbusService.SearchResult> 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<MuclumbusService.SearchResult> call, Throwable throwable) {
throwable.printStackTrace();
}
});
}
public interface OnChannelSearchResultsFound {
void onChannelSearchResultsFound(List<MuclumbusService.Room> results);
} }
public boolean isDataSaverDisabled() { public boolean isDataSaverDisabled() {
@ -1130,13 +1077,6 @@ public class XmppConnectionService extends Service {
} }
mForceDuringOnCreate.set(false); mForceDuringOnCreate.set(false);
toggleForegroundService(); 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() { private void checkForDeletedFiles() {

View file

@ -2,7 +2,6 @@ package eu.siacs.conversations.ui;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.os.Bundle; 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.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.http.services.MuclumbusService; 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.adapter.ChannelSearchResultAdapter;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils; import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.AccountUtils;
import rocks.xmpp.addr.Jid; 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 static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in";
private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter(); private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter();
private ActivityChannelDiscoveryBinding binding;
private final PendingItem<String> mInitialSearchValue = new PendingItem<>(); private final PendingItem<String> mInitialSearchValue = new PendingItem<>();
private MenuItem mMenuSearchView; private MenuItem mMenuSearchView;
@ -66,7 +67,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(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); setSupportActionBar((Toolbar) binding.toolbar);
configureActionBar(getSupportActionBar(), true); configureActionBar(getSupportActionBar(), true);
binding.list.setAdapter(this.adapter); 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); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
mSearchEditText.setText(""); mSearchEditText.setText("");
adapter.submitList(Collections.emptyList()); toggleLoadingScreen();
if (optedIn) { if (optedIn) {
xmppConnectionService.discoverChannels(null, this); xmppConnectionService.discoverChannels(null, this);
} }
return true; return true;
} }
private void toggleLoadingScreen() {
adapter.submitList(Collections.emptyList());
binding.progressBar.setVisibility(View.VISIBLE);
}
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
@ -159,14 +165,18 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
if (optedIn) { if (optedIn) {
xmppConnectionService.discoverChannels(v.getText().toString(), this); xmppConnectionService.discoverChannels(v.getText().toString(), this);
} }
adapter.submitList(Collections.emptyList()); toggleLoadingScreen();
SoftKeyboardUtils.hideSoftKeyboard(this); SoftKeyboardUtils.hideSoftKeyboard(this);
return true; return true;
} }
@Override @Override
public void onChannelSearchResultsFound(List<MuclumbusService.Room> results) { public void onChannelSearchResultsFound(List<MuclumbusService.Room> results) {
runOnUiThread(() -> adapter.submitList(results)); runOnUiThread(() -> {
adapter.submitList(results);
binding.list.setVisibility(View.VISIBLE);
binding.progressBar.setVisibility(View.GONE);
});
} }

View file

@ -22,7 +22,7 @@ public class ChannelSearchResultAdapter extends ListAdapter<MuclumbusService.Roo
private static final DiffUtil.ItemCallback<MuclumbusService.Room> DIFF = new DiffUtil.ItemCallback<MuclumbusService.Room>() { private static final DiffUtil.ItemCallback<MuclumbusService.Room> DIFF = new DiffUtil.ItemCallback<MuclumbusService.Room>() {
@Override @Override
public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) {
return false; return a.address != null && a.address.equals(b.address);
} }
@Override @Override

View file

@ -1,6 +1,5 @@
package eu.siacs.conversations.ui.adapter; package eu.siacs.conversations.ui.adapter;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
@ -8,7 +7,6 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -17,7 +15,6 @@ import android.widget.ImageView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.RejectedExecutionException; 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.ConversationFragment;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment; import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.StyledAttributes;
public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> { public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {

View file

@ -14,18 +14,26 @@
layout="@layout/toolbar" /> layout="@layout/toolbar" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="@+id/list"
android:layout_gravity="center_horizontal"/>
<android.support.design.widget.CoordinatorLayout <android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator" android:id="@+id/coordinator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/color_background_primary"> android:background="?attr/color_background_primary">
<android.support.v7.widget.RecyclerView <android.support.v7.widget.RecyclerView
android:id="@+id/list" android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/color_background_primary" android:background="?attr/color_background_primary"
android:orientation="vertical" android:orientation="vertical"
android:visibility="gone"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>