Browse Source

Fix #430

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 6 years ago
parent
commit
189a73975b

+ 4 - 4
app/build.gradle

@@ -17,8 +17,8 @@ android {
         targetSdkVersion 28
         targetSdkVersion 28
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 
 
-        versionCode 80
-        versionName "3.2.1"
+        versionCode 81
+        versionName "3.2.2"
 
 
         flavorDimensions "default"
         flavorDimensions "default"
         renderscriptTargetApi 19
         renderscriptTargetApi 19
@@ -123,7 +123,7 @@ dependencies {
 
 
     implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
     implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
 
 
-    implementation 'com.android.support:multidex:1.0.3'
+    implementation 'androidx.multidex:multidex:2.0.0'
 
 
     implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
     implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
     implementation "io.reactivex.rxjava2:rxjava:2.2.2"
     implementation "io.reactivex.rxjava2:rxjava:2.2.2"
@@ -195,7 +195,7 @@ dependencies {
 
 
     implementation 'com.github.mario:chatkit:d63d61db95'
     implementation 'com.github.mario:chatkit:d63d61db95'
 
 
-    implementation 'com.otaliastudios:autocomplete:1.1.0'
+    implementation 'com.github.natario1:Autocomplete:v1.1.0'
 
 
     implementation 'com.github.Kennyc1012:BottomSheet:2.4.1'
     implementation 'com.github.Kennyc1012:BottomSheet:2.4.1'
     implementation 'eu.davidea:flipview:1.2.0'
     implementation 'eu.davidea:flipview:1.2.0'

+ 18 - 39
app/src/main/java/com/nextcloud/talk/controllers/ChatController.java

@@ -36,18 +36,14 @@ import android.text.InputFilter;
 import android.text.TextUtils;
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.text.TextWatcher;
 import android.util.Log;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.ImageButton;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
+import android.view.*;
+import android.widget.*;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import autodagger.AutoInjector;
+import butterknife.BindView;
+import butterknife.OnClick;
 import com.bluelinelabs.conductor.RouterTransaction;
 import com.bluelinelabs.conductor.RouterTransaction;
 import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
 import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
 import com.facebook.drawee.backends.pipeline.Fresco;
 import com.facebook.drawee.backends.pipeline.Fresco;
@@ -74,17 +70,13 @@ import com.nextcloud.talk.models.json.rooms.Conversation;
 import com.nextcloud.talk.models.json.rooms.RoomOverall;
 import com.nextcloud.talk.models.json.rooms.RoomOverall;
 import com.nextcloud.talk.models.json.rooms.RoomsOverall;
 import com.nextcloud.talk.models.json.rooms.RoomsOverall;
 import com.nextcloud.talk.presenters.MentionAutocompletePresenter;
 import com.nextcloud.talk.presenters.MentionAutocompletePresenter;
-import com.nextcloud.talk.utils.ApiUtils;
-import com.nextcloud.talk.utils.DisplayUtils;
-import com.nextcloud.talk.utils.KeyboardUtils;
-import com.nextcloud.talk.utils.NotificationUtils;
+import com.nextcloud.talk.utils.*;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.database.user.UserUtils;
 import com.nextcloud.talk.utils.database.user.UserUtils;
 import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
 import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
 import com.otaliastudios.autocomplete.Autocomplete;
 import com.otaliastudios.autocomplete.Autocomplete;
 import com.otaliastudios.autocomplete.AutocompleteCallback;
 import com.otaliastudios.autocomplete.AutocompleteCallback;
 import com.otaliastudios.autocomplete.AutocompletePresenter;
 import com.otaliastudios.autocomplete.AutocompletePresenter;
-import com.otaliastudios.autocomplete.CharPolicy;
 import com.stfalcon.chatkit.commons.ImageLoader;
 import com.stfalcon.chatkit.commons.ImageLoader;
 import com.stfalcon.chatkit.commons.models.IMessage;
 import com.stfalcon.chatkit.commons.models.IMessage;
 import com.stfalcon.chatkit.messages.MessageHolders;
 import com.stfalcon.chatkit.messages.MessageHolders;
@@ -100,32 +92,19 @@ import com.vanniktech.emoji.listeners.OnEmojiClickListener;
 import com.vanniktech.emoji.listeners.OnEmojiPopupDismissListener;
 import com.vanniktech.emoji.listeners.OnEmojiPopupDismissListener;
 import com.vanniktech.emoji.listeners.OnEmojiPopupShownListener;
 import com.vanniktech.emoji.listeners.OnEmojiPopupShownListener;
 import com.webianks.library.PopupBubble;
 import com.webianks.library.PopupBubble;
-
-import org.parceler.Parcels;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import autodagger.AutoInjector;
-import butterknife.BindView;
-import butterknife.OnClick;
 import io.reactivex.Observer;
 import io.reactivex.Observer;
 import io.reactivex.android.schedulers.AndroidSchedulers;
 import io.reactivex.android.schedulers.AndroidSchedulers;
 import io.reactivex.disposables.Disposable;
 import io.reactivex.disposables.Disposable;
 import io.reactivex.schedulers.Schedulers;
 import io.reactivex.schedulers.Schedulers;
+import org.parceler.Parcels;
 import retrofit2.HttpException;
 import retrofit2.HttpException;
 import retrofit2.Response;
 import retrofit2.Response;
 
 
+import javax.inject.Inject;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
 @AutoInjector(NextcloudTalkApplication.class)
 @AutoInjector(NextcloudTalkApplication.class)
 public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener,
 public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener,
         MessagesListAdapter.Formatter<Date>, MessagesListAdapter.OnMessageLongClickListener, MessageHolders.ContentChecker {
         MessagesListAdapter.Formatter<Date>, MessagesListAdapter.OnMessageLongClickListener, MessageHolders.ContentChecker {
@@ -406,7 +385,7 @@ public class ChatController extends BaseController implements MessagesListAdapte
                 if (s.length() == 1000) {
                 if (s.length() == 1000) {
                     messageInput.setError(getResources().getString(R.string.nc_limit_hit));
                     messageInput.setError(getResources().getString(R.string.nc_limit_hit));
                 } else {
                 } else {
-                   messageInput.setError(null);
+                    messageInput.setError(null);
                 }
                 }
             }
             }
 
 
@@ -459,11 +438,11 @@ public class ChatController extends BaseController implements MessagesListAdapte
         AutocompletePresenter<Mention> presenter = new MentionAutocompletePresenter(getApplicationContext(), roomToken);
         AutocompletePresenter<Mention> presenter = new MentionAutocompletePresenter(getApplicationContext(), roomToken);
         AutocompleteCallback<Mention> callback = new MentionAutocompleteCallback();
         AutocompleteCallback<Mention> callback = new MentionAutocompleteCallback();
 
 
-        if (mentionAutocomplete != null) {
+        if (mentionAutocomplete == null && messageInput != null) {
             mentionAutocomplete = Autocomplete.<Mention>on(messageInput)
             mentionAutocomplete = Autocomplete.<Mention>on(messageInput)
                     .with(elevation)
                     .with(elevation)
                     .with(backgroundDrawable)
                     .with(backgroundDrawable)
-                    .with(new CharPolicy('@'))
+                    .with(new MagicCharPolicy('@'))
                     .with(presenter)
                     .with(presenter)
                     .with(callback)
                     .with(callback)
                     .build();
                     .build();

+ 52 - 52
app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java

@@ -21,9 +21,10 @@
 package com.nextcloud.talk.presenters;
 package com.nextcloud.talk.presenters;
 
 
 import android.content.Context;
 import android.content.Context;
-import android.text.TextUtils;
 import android.view.View;
 import android.view.View;
-
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+import autodagger.AutoInjector;
 import com.nextcloud.talk.adapters.items.MentionAutocompleteItem;
 import com.nextcloud.talk.adapters.items.MentionAutocompleteItem;
 import com.nextcloud.talk.api.NcApi;
 import com.nextcloud.talk.api.NcApi;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
@@ -33,15 +34,6 @@ import com.nextcloud.talk.models.json.mention.MentionOverall;
 import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.database.user.UserUtils;
 import com.nextcloud.talk.utils.database.user.UserUtils;
 import com.otaliastudios.autocomplete.RecyclerViewPresenter;
 import com.otaliastudios.autocomplete.RecyclerViewPresenter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
-import autodagger.AutoInjector;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
 import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
 import io.reactivex.Observer;
 import io.reactivex.Observer;
@@ -49,6 +41,10 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
 import io.reactivex.disposables.Disposable;
 import io.reactivex.disposables.Disposable;
 import io.reactivex.schedulers.Schedulers;
 import io.reactivex.schedulers.Schedulers;
 
 
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+
 @AutoInjector(NextcloudTalkApplication.class)
 @AutoInjector(NextcloudTalkApplication.class)
 public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention> implements FlexibleAdapter.OnItemClickListener {
 public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention> implements FlexibleAdapter.OnItemClickListener {
     @Inject
     @Inject
@@ -90,57 +86,61 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
 
 
     @Override
     @Override
     protected void onQuery(@Nullable CharSequence query) {
     protected void onQuery(@Nullable CharSequence query) {
-        if (!TextUtils.isEmpty(query)) {
-
-            adapter.setFilter(query.toString());
-            ncApi.getMentionAutocompleteSuggestions(ApiUtils.getCredentials(currentUser.getUsername(), currentUser
-                            .getToken()), ApiUtils.getUrlForMentionSuggestions(currentUser.getBaseUrl(), roomToken),
-                    query.toString(), null)
-                    .subscribeOn(Schedulers.newThread())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .retry(3)
-                    .subscribe(new Observer<MentionOverall>() {
-                        @Override
-                        public void onSubscribe(Disposable d) {
-                        }
 
 
-                        @Override
-                        public void onNext(MentionOverall mentionOverall) {
-                            List<Mention> mentionsList = mentionOverall.getOcs().getData();
+        String queryString;
+        if (query != null && query.length() > 1) {
+            queryString = String.valueOf(query.subSequence(1, query.length()));
+        } else {
+            queryString = "";
+        }
+
+        adapter.setFilter(queryString);
+        ncApi.getMentionAutocompleteSuggestions(ApiUtils.getCredentials(currentUser.getUsername(), currentUser
+                        .getToken()), ApiUtils.getUrlForMentionSuggestions(currentUser.getBaseUrl(), roomToken),
+                queryString, 5)
+                .subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .retry(3)
+                .subscribe(new Observer<MentionOverall>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
+                    }
+
+                    @Override
+                    public void onNext(MentionOverall mentionOverall) {
+                        List<Mention> mentionsList = mentionOverall.getOcs().getData();
+
+                        if (mentionsList.size() == 0) {
+                            adapter.clear();
+                        } else {
+                            List<AbstractFlexibleItem> internalAbstractFlexibleItemList = new ArrayList<>();
+                            for (Mention mention : mentionsList) {
+                                internalAbstractFlexibleItemList.add(
+                                        new MentionAutocompleteItem(mention.getId(), mention.getLabel(),
+                                                currentUser));
+                            }
 
 
-                            if (mentionsList.size() == 0) {
+                            if (adapter.getItemCount() != 0) {
                                 adapter.clear();
                                 adapter.clear();
-                            } else {
-                                List<AbstractFlexibleItem> internalAbstractFlexibleItemList = new ArrayList<>();
-                                for (Mention mention : mentionsList) {
-                                    internalAbstractFlexibleItemList.add(
-                                            new MentionAutocompleteItem(mention.getId(), mention.getLabel(),
-                                                    currentUser));
-                                }
-
-                                if (adapter.getItemCount() != 0) {
-                                    adapter.clear();
-                                }
-
-                                adapter.updateDataSet(internalAbstractFlexibleItemList);
                             }
                             }
-                        }
 
 
-                        @Override
-                        public void onError(Throwable e) {
-                            adapter.clear();
+                            adapter.updateDataSet(internalAbstractFlexibleItemList);
                         }
                         }
+                    }
 
 
-                        @Override
-                        public void onComplete() {
+                    @Override
+                    public void onError(Throwable e) {
+                        adapter.clear();
+                    }
 
 
-                        }
-                    });
-        } else {
-            adapter.clear();
-        }
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
     }
     }
 
 
+
     @Override
     @Override
     public boolean onItemClick(View view, int position) {
     public boolean onItemClick(View view, int position) {
         Mention mention = new Mention();
         Mention mention = new Mention();

+ 113 - 0
app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java

@@ -0,0 +1,113 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.utils;
+
+import android.text.Spannable;
+import android.text.Spanned;
+import android.util.Log;
+import androidx.annotation.Nullable;
+import com.otaliastudios.autocomplete.AutocompletePolicy;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MagicCharPolicy implements AutocompletePolicy {
+
+    private final char character;
+
+    public MagicCharPolicy(char character) {
+        this.character = character;
+    }
+
+    private int[] checkText(Spannable text, int cursorPos) {
+        if (text.length() == 0) {
+            return null;
+        }
+
+        int[] span = new int[2];
+        Pattern pattern = Pattern.compile("@.*\\w*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+        Matcher matcher = pattern.matcher(text);
+
+        while (matcher.find()) {
+            if (cursorPos >= matcher.start() && cursorPos <= matcher.end()) {
+                span[0] = matcher.start();
+                span[1] = matcher.end();
+                if (text.subSequence(matcher.start(), matcher.end()).charAt(0) == character) {
+                    return span;
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean shouldShowPopup(Spannable text, int cursorPos) {
+        int[] show = checkText(text, cursorPos);
+        if (show != null) {
+            text.setSpan(new QuerySpan(), show[0], show[1], Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean shouldDismissPopup(Spannable text, int cursorPos) {
+        return checkText(text, cursorPos) == null;
+    }
+
+    @Override
+    public CharSequence getQuery(Spannable text) {
+        QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class);
+        if (span == null || span.length == 0) {
+            // Should never happen.
+            return "";
+        }
+        QuerySpan sp = span[0];
+        return text.subSequence(text.getSpanStart(sp), text.getSpanEnd(sp));
+    }
+
+
+    @Override
+    public void onDismiss(Spannable text) {
+        // Remove any span added by shouldShow. Should be useless, but anyway.
+        QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class);
+        for (QuerySpan s : span) {
+            text.removeSpan(s);
+        }
+    }
+
+    private static class QuerySpan {
+    }
+
+    @Nullable
+    public static int[] getQueryRange(Spannable text) {
+        QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class);
+        if (span == null || span.length == 0) return null;
+        if (span.length > 1) {
+            // Do absolutely nothing
+        }
+        QuerySpan sp = span[0];
+        return new int[]{text.getSpanStart(sp), text.getSpanEnd(sp)};
+    }
+}