From f9f07063876ec5f3917ce72385b015e1b5f31d7e Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Fri, 26 Apr 2019 23:12:05 +0200 Subject: implement channel discovery refactor muc search to use http cache channel search results --- build.gradle | 3 + proguard-rules.pro | 2 + src/main/AndroidManifest.xml | 3 + src/main/java/de/pixart/messenger/Config.java | 1 + .../messenger/http/services/MuclumbusService.java | 92 ++++++++++ .../pixart/messenger/services/AvatarService.java | 12 ++ .../services/ChannelDiscoveryService.java | 103 +++++++++++ .../messenger/services/XmppConnectionService.java | 16 +- .../messenger/ui/ChannelDiscoveryActivity.java | 196 +++++++++++++++++++++ .../messenger/ui/StartConversationActivity.java | 6 +- .../ui/adapter/ChannelSearchResultAdapter.java | 78 ++++++++ .../de/pixart/messenger/utils/AccountUtils.java | 15 ++ src/main/res/layout/activity_channel_discovery.xml | 40 +++++ src/main/res/layout/search_result_item.xml | 52 ++++++ src/main/res/menu/channel_discovery_activity.xml | 11 ++ .../res/menu/start_conversation_fab_submenu.xml | 4 + src/main/res/values/strings.xml | 4 + 17 files changed, 635 insertions(+), 3 deletions(-) create mode 100644 src/main/java/de/pixart/messenger/http/services/MuclumbusService.java create mode 100644 src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java create mode 100644 src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java create mode 100644 src/main/java/de/pixart/messenger/ui/adapter/ChannelSearchResultAdapter.java create mode 100644 src/main/res/layout/activity_channel_discovery.xml create mode 100644 src/main/res/layout/search_result_item.xml create mode 100644 src/main/res/menu/channel_discovery_activity.xml diff --git a/build.gradle b/build.gradle index a27b439b0..53875273a 100644 --- a/build.gradle +++ b/build.gradle @@ -83,6 +83,9 @@ dependencies { implementation 'com.leinardi.android:speed-dial:2.0.1' implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.squareup.okhttp3:okhttp:3.12.2' // versions > 3.12.x don't support API level < 21 anymore + 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' } ext { diff --git a/proguard-rules.pro b/proguard-rules.pro index e60465e6c..336771bcd 100644 --- a/proguard-rules.pro +++ b/proguard-rules.pro @@ -19,6 +19,8 @@ -dontwarn org.bouncycastle.cert.dane.** -dontwarn rocks.xmpp.addr.** -dontwarn com.google.firebase.analytics.connector.AnalyticsConnector +-dontwarn java.lang.** +-dontwarn javax.lang.** # JSR 305 annotations are for embedding nullability information. -dontwarn javax.annotation.** diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 71b2e5909..217f90d1e 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -312,6 +312,9 @@ + diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index a5d25786b..e61b1d28e 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -54,6 +54,7 @@ public final class Config { public static final String DOMAIN_LOCK = null; //BuildConfig.DOMAIN_LOCK; //only allow account creation for this domain public static final String MAGIC_CREATE_DOMAIN = "blabber.im"; public static final String QUICKSY_DOMAIN = "quicksy.im"; + public static final String CHANNEL_DISCOVERY = "https://search.jabbercat.org"; public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false; diff --git a/src/main/java/de/pixart/messenger/http/services/MuclumbusService.java b/src/main/java/de/pixart/messenger/http/services/MuclumbusService.java new file mode 100644 index 000000000..7c8ca9e32 --- /dev/null +++ b/src/main/java/de/pixart/messenger/http/services/MuclumbusService.java @@ -0,0 +1,92 @@ +package de.pixart.messenger.http.services; + +import com.google.common.base.Objects; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import de.pixart.messenger.services.AvatarService; +import de.pixart.messenger.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 String name; + public String description; + + 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); + } + + @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 { + public Set keywords; + + public SearchRequest(String keyword) { + this.keywords = Collections.singleton(keyword); + } + } + + class SearchResult { + public Result result; + } + + class Result { + public List items; + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java index b37811b7f..c38fdcc99 100644 --- a/src/main/java/de/pixart/messenger/services/AvatarService.java +++ b/src/main/java/de/pixart/messenger/services/AvatarService.java @@ -38,6 +38,7 @@ import de.pixart.messenger.entities.Conversational; import de.pixart.messenger.entities.ListItem; import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.MucOptions; +import de.pixart.messenger.http.services.MuclumbusService; import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.xmpp.OnAdvancedStreamFeaturesLoaded; import de.pixart.messenger.xmpp.XmppConnection; @@ -82,10 +83,21 @@ 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 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 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) { if (contact.isSelf()) { return get(contact.getAccount(), size, cachedOnly); diff --git a/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java b/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java new file mode 100644 index 000000000..21711c647 --- /dev/null +++ b/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java @@ -0,0 +1,103 @@ +package de.pixart.messenger.services; + +import android.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import de.pixart.messenger.Config; +import de.pixart.messenger.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); + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 5de9a7501..1456d5a8e 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -78,6 +78,7 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -110,6 +111,7 @@ import de.pixart.messenger.generator.MessageGenerator; import de.pixart.messenger.generator.PresenceGenerator; import de.pixart.messenger.http.CustomURLStreamHandlerFactory; import de.pixart.messenger.http.HttpConnectionManager; +import de.pixart.messenger.http.services.MuclumbusService; import de.pixart.messenger.parser.AbstractParser; import de.pixart.messenger.parser.IqParser; import de.pixart.messenger.parser.MessageParser; @@ -162,13 +164,14 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket; import de.pixart.messenger.xmpp.stanzas.MessagePacket; import de.pixart.messenger.xmpp.stanzas.PresencePacket; import me.leolin.shortcutbadger.ShortcutBadger; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.SettingsActivity.CHAT_STATES; import static de.pixart.messenger.ui.SettingsActivity.CONFIRM_MESSAGES; import static de.pixart.messenger.ui.SettingsActivity.ENABLE_MULTI_ACCOUNTS; import static de.pixart.messenger.ui.SettingsActivity.INDICATE_RECEIVED; -import static de.pixart.messenger.ui.SettingsActivity.ENABLE_MULTI_ACCOUNTS; public class XmppConnectionService extends Service { @@ -255,9 +258,11 @@ public class XmppConnectionService extends Service { private long mLastActivity = 0; 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); protected AtomicBoolean mForceForegroundService = new AtomicBoolean(false); + private AtomicBoolean mForceDuringOnCreate = new AtomicBoolean(false); private OnMessagePacketReceived mMessageParser = new MessageParser(this); private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); private IqParser mIqParser = new IqParser(this); @@ -834,6 +839,10 @@ public class XmppConnectionService extends Service { editor.commit(); } + public void discoverChannels(String query, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { + mChannelDiscoveryService.discover(query, onChannelSearchResultsFound); + } + public boolean isDataSaverDisabled() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); @@ -1144,6 +1153,7 @@ public class XmppConnectionService extends Service { if (Compatibility.runsTwentySix()) { mNotificationService.initializeChannels(); } + mForceDuringOnCreate.set(Compatibility.runsAndTargetsTwentySix(this)); final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; this.mBitmapCache = new LruCache(cacheSize) { @@ -1213,6 +1223,8 @@ public class XmppConnectionService extends Service { intentFilter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); registerReceiver(this.mInternalEventReceiver, intentFilter); } + mForceDuringOnCreate.set(false); + toggleForegroundService(); //start export log service every day at given time ScheduleAutomaticExport(); // cancel scheduled exporter @@ -1307,7 +1319,7 @@ public class XmppConnectionService extends Service { private void toggleForegroundService(boolean force) { final boolean status; - if (force || mForceForegroundService.get() || (Compatibility.keepForegroundService(this)/* && hasEnabledAccounts()*/)) { + if (force || mForceDuringOnCreate.get() || mForceForegroundService.get() || (Compatibility.keepForegroundService(this) && hasEnabledAccounts())) { final Notification notification = this.mNotificationService.createForegroundNotification(); startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, notification); if (!mForceForegroundService.get()) { diff --git a/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java b/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java new file mode 100644 index 000000000..e0b70b6ac --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java @@ -0,0 +1,196 @@ +package de.pixart.messenger.ui; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.text.Html; +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.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.ActivityChannelDiscoveryBinding; +import de.pixart.messenger.entities.Account; +import de.pixart.messenger.entities.Conversation; +import de.pixart.messenger.http.services.MuclumbusService; +import de.pixart.messenger.services.ChannelDiscoveryService; +import de.pixart.messenger.ui.adapter.ChannelSearchResultAdapter; +import de.pixart.messenger.ui.util.PendingItem; +import de.pixart.messenger.ui.util.SoftKeyboardUtils; +import de.pixart.messenger.utils.AccountUtils; +import rocks.xmpp.addr.Jid; + +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; + private EditText mSearchEditText; + private boolean optedIn = false; + + @Override + protected void refreshUiReal() { + + } + + @Override + void onBackendConnected() { + if (optedIn) { + String query; + if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) { + query = mSearchEditText.getText().toString(); + } else { + query = mInitialSearchValue.peek(); + } + xmppConnectionService.discoverChannels(query, this); + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + 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); + + 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); + 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); + mMenuSearchView.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(""); + 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(); + 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(); + } + } + + @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(); + optedIn = true; + xmppConnectionService.discoverChannels(null, this); + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (optedIn) { + xmppConnectionService.discoverChannels(v.getText().toString(), this); + } + toggleLoadingScreen(); + SoftKeyboardUtils.hideSoftKeyboard(this); + return true; + } + + @Override + public void onChannelSearchResultsFound(List results) { + runOnUiThread(() -> { + adapter.submitList(results); + binding.list.setVisibility(View.VISIBLE); + binding.progressBar.setVisibility(View.GONE); + }); + } + + @Override + public void onChannelSearchResult(final MuclumbusService.Room result) { + List accounts = AccountUtils.getEnabledAccounts(xmppConnectionService); + if (accounts.size() == 1) { + joinChannelSearchResult(accounts.get(0), result); + } else if (accounts.size() > 0) { + final AtomicReference 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, MuclumbusService.Room result) { + Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid)); + final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); + switchToConversation(conversation); + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java index 6706d004c..75e7c2f52 100644 --- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java @@ -70,6 +70,7 @@ import de.pixart.messenger.ui.interfaces.OnBackendConnected; import de.pixart.messenger.ui.util.JidDialog; import de.pixart.messenger.ui.util.PendingItem; import de.pixart.messenger.ui.util.SoftKeyboardUtils; +import de.pixart.messenger.utils.AccountUtils; import de.pixart.messenger.utils.MenuDoubleTabUtil; import de.pixart.messenger.utils.XmppUri; import de.pixart.messenger.xmpp.OnUpdateBlocklist; @@ -307,6 +308,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne prefilled = null; } switch (actionItem.getId()) { + case R.id.discover_public_channels: + startActivity(new Intent(this, ChannelDiscoveryActivity.class)); + break; case R.id.join_public_channel: showJoinConferenceDialog(prefilled); break; @@ -763,7 +767,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second); this.mPostponedActivityResult = null; } - this.mActivatedAccounts.clear(); + this.mActivatedAccounts.addAll(AccountUtils.getEnabledAccounts(xmppConnectionService)); for (Account account : xmppConnectionService.getAccounts()) { if (account.getStatus() != Account.State.DISABLED) { if (Config.DOMAIN_LOCK != null) { diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ChannelSearchResultAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ChannelSearchResultAdapter.java new file mode 100644 index 000000000..d137e3a6f --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/adapter/ChannelSearchResultAdapter.java @@ -0,0 +1,78 @@ +package de.pixart.messenger.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 de.pixart.messenger.R; +import de.pixart.messenger.databinding.SearchResultItemBinding; +import de.pixart.messenger.http.services.MuclumbusService; +import de.pixart.messenger.ui.util.AvatarWorkerTask; + + +public class ChannelSearchResultAdapter extends ListAdapter { + + private OnChannelSearchResultSelected listener; + + private static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { + return a.address != null && a.address.equals(b.address); + } + + @Override + public boolean areContentsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room 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 MuclumbusService.Room 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(MuclumbusService.Room result); + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/AccountUtils.java b/src/main/java/de/pixart/messenger/utils/AccountUtils.java index d675ed680..593d3a089 100644 --- a/src/main/java/de/pixart/messenger/utils/AccountUtils.java +++ b/src/main/java/de/pixart/messenger/utils/AccountUtils.java @@ -4,8 +4,10 @@ import android.app.Activity; import android.content.Intent; import android.widget.Toast; +import java.util.ArrayList; import java.util.List; +import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; import de.pixart.messenger.services.XmppConnectionService; @@ -18,6 +20,19 @@ public class AccountUtils { MANAGE_ACCOUNT_ACTIVITY = getManageAccountActivityClass(); } + public static List getEnabledAccounts(final XmppConnectionService service) { + ArrayList 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) { final List accounts = service.getAccounts(); diff --git a/src/main/res/layout/activity_channel_discovery.xml b/src/main/res/layout/activity_channel_discovery.xml new file mode 100644 index 000000000..ee253f3dd --- /dev/null +++ b/src/main/res/layout/activity_channel_discovery.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/search_result_item.xml b/src/main/res/layout/search_result_item.xml new file mode 100644 index 000000000..856aa7ab6 --- /dev/null +++ b/src/main/res/layout/search_result_item.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/menu/channel_discovery_activity.xml b/src/main/res/menu/channel_discovery_activity.xml new file mode 100644 index 000000000..93e044f27 --- /dev/null +++ b/src/main/res/menu/channel_discovery_activity.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/menu/start_conversation_fab_submenu.xml b/src/main/res/menu/start_conversation_fab_submenu.xml index 212a94a7e..2cf545d68 100644 --- a/src/main/res/menu/start_conversation_fab_submenu.xml +++ b/src/main/res/menu/start_conversation_fab_submenu.xml @@ -1,5 +1,9 @@ + Delete locally Delete from server You have to activate and/or go online with the account you want to delete from server. + Discover channels + Search channels + Privacy notice + search.jabbercat.org.

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