From a55cf22ea9c79d32495d82ee8e86c7e78db81364 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Thu, 4 Feb 2016 18:44:15 +0100
Subject: [PATCH] Fixed an issue with backlog requesting and with URL Spans

---
 .../de/kuschku/libquassel/client/Client.java  |  2 +-
 .../{backlogmanagers => }/BacklogFilter.java  | 10 ++--
 .../backlogstorage/BacklogStorage.java        |  8 ++--
 .../backlogstorage/MemoryBacklogStorage.java  | 17 ++++---
 .../syncables/types/SyncableObject.java       | 20 +++++++-
 .../types/abstracts/ABacklogManager.java      | 13 +++--
 .../syncables/types/impl/BacklogManager.java  | 47 +++++++++++++++----
 .../types/interfaces/QBacklogManager.java     |  4 +-
 .../quasseldroid_ng/ui/chat/ChatActivity.java |  2 +-
 .../ui/chat/chatview/MessageViewHolder.java   |  2 +
 .../de/kuschku/util/irc/IrcFormatHelper.java  |  4 +-
 11 files changed, 89 insertions(+), 40 deletions(-)
 rename app/src/main/java/de/kuschku/libquassel/localtypes/{backlogmanagers => }/BacklogFilter.java (93%)

diff --git a/app/src/main/java/de/kuschku/libquassel/client/Client.java b/app/src/main/java/de/kuschku/libquassel/client/Client.java
index a94de792c..c089bab10 100644
--- a/app/src/main/java/de/kuschku/libquassel/client/Client.java
+++ b/app/src/main/java/de/kuschku/libquassel/client/Client.java
@@ -101,6 +101,7 @@ public class Client extends AClient {
         this.backlogStorage = backlogStorage;
         backlogStorage.setClient(this);
         this.backlogManager = new BacklogManager(this, backlogStorage);
+        this.backlogManager.init("", provider, this);
         this.notificationManager = new NotificationManager(this);
     }
 
@@ -365,7 +366,6 @@ public class Client extends AClient {
 
     public void initBacklog(int id) {
         backlogRequests.remove((Integer) id);
-        requestInitBacklog(id, 0);
         if (backlogRequests.isEmpty())
             setConnectionStatus(ConnectionChangeEvent.Status.CONNECTED);
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java b/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java
similarity index 93%
rename from app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
rename to app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java
index 63dee1ad2..efa3d08c8 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java
@@ -19,7 +19,7 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.kuschku.libquassel.localtypes.backlogmanagers;
+package de.kuschku.libquassel.localtypes;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -35,16 +35,16 @@ import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
 import de.kuschku.util.observables.callbacks.UICallback;
-import de.kuschku.util.observables.lists.ObservableSortedList;
+import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 
 public class BacklogFilter implements UICallback {
     @NonNull
     private final Client client;
     private final int bufferId;
     @NonNull
-    private final ObservableSortedList<Message> unfiltered;
+    private final ObservableComparableSortedList<Message> unfiltered;
     @NonNull
-    private final ObservableSortedList<Message> filtered;
+    private final ObservableComparableSortedList<Message> filtered;
 
     @NonNull
     private final Set<Message.Type> filteredTypes = new HashSet<>();
@@ -52,7 +52,7 @@ public class BacklogFilter implements UICallback {
     @Nullable
     private DateTime earliestMessage;
 
-    public BacklogFilter(@NonNull Client client, int bufferId, @NonNull ObservableSortedList<Message> unfiltered, @NonNull ObservableSortedList<Message> filtered) {
+    public BacklogFilter(@NonNull Client client, int bufferId, @NonNull ObservableComparableSortedList<Message> unfiltered, @NonNull ObservableComparableSortedList<Message> filtered) {
         this.client = client;
         this.bufferId = bufferId;
         this.unfiltered = unfiltered;
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/BacklogStorage.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/BacklogStorage.java
index 7f7dc0ab9..0907bd1f8 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/BacklogStorage.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/BacklogStorage.java
@@ -25,16 +25,16 @@ import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 
 import de.kuschku.libquassel.client.Client;
-import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
+import de.kuschku.libquassel.localtypes.BacklogFilter;
 import de.kuschku.libquassel.message.Message;
-import de.kuschku.util.observables.lists.ObservableSortedList;
+import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 
 public interface BacklogStorage {
     @NonNull
-    ObservableSortedList<Message> getUnfiltered(@IntRange(from = 0) int bufferid);
+    ObservableComparableSortedList<Message> getUnfiltered(@IntRange(from = 0) int bufferid);
 
     @NonNull
-    ObservableSortedList<Message> getFiltered(@IntRange(from = 0) int bufferid);
+    ObservableComparableSortedList<Message> getFiltered(@IntRange(from = 0) int bufferid);
 
     @NonNull
     BacklogFilter getFilter(@IntRange(from = 0) int bufferid);
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/MemoryBacklogStorage.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/MemoryBacklogStorage.java
index c1720e105..ee0e0c3f4 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/MemoryBacklogStorage.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/MemoryBacklogStorage.java
@@ -26,18 +26,17 @@ import android.support.annotation.NonNull;
 import android.util.SparseArray;
 
 import de.kuschku.libquassel.client.Client;
-import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
+import de.kuschku.libquassel.localtypes.BacklogFilter;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.util.observables.lists.ObservableComparableSortedList;
-import de.kuschku.util.observables.lists.ObservableSortedList;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 public class MemoryBacklogStorage implements BacklogStorage {
     @NonNull
-    private final SparseArray<ObservableSortedList<Message>> backlogs = new SparseArray<>();
+    private final SparseArray<ObservableComparableSortedList<Message>> backlogs = new SparseArray<>();
     @NonNull
-    private final SparseArray<ObservableSortedList<Message>> filteredBacklogs = new SparseArray<>();
+    private final SparseArray<ObservableComparableSortedList<Message>> filteredBacklogs = new SparseArray<>();
     @NonNull
     private final SparseArray<BacklogFilter> filters = new SparseArray<>();
 
@@ -45,14 +44,14 @@ public class MemoryBacklogStorage implements BacklogStorage {
 
     @NonNull
     @Override
-    public ObservableSortedList<Message> getUnfiltered(@IntRange(from = -1) int bufferid) {
+    public ObservableComparableSortedList<Message> getUnfiltered(@IntRange(from = -1) int bufferid) {
         ensureExisting(bufferid);
         return backlogs.get(bufferid);
     }
 
     @NonNull
     @Override
-    public ObservableSortedList<Message> getFiltered(@IntRange(from = -1) int bufferid) {
+    public ObservableComparableSortedList<Message> getFiltered(@IntRange(from = -1) int bufferid) {
         ensureExisting(bufferid);
         return filteredBacklogs.get(bufferid);
     }
@@ -73,9 +72,9 @@ public class MemoryBacklogStorage implements BacklogStorage {
 
     @Override
     public void insertMessages(@NonNull Message... messages) {
-        if (messages.length > 0) {
-            int bufferId = messages[0].bufferInfo.id();
-            insertMessages(bufferId, messages);
+        for (Message message : messages) {
+            ensureExisting(message.bufferInfo.id());
+            backlogs.get(message.bufferInfo.id()).add(message);
         }
     }
 
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
index 5a674da8d..0913f4ea4 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
@@ -26,7 +26,6 @@ import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Observable;
 
@@ -39,6 +38,8 @@ import de.kuschku.libquassel.syncables.types.interfaces.QSyncableObject;
 import de.kuschku.util.backports.Objects;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
+import static de.kuschku.util.AndroidAssert.assertTrue;
+import static junit.framework.Assert.assertEquals;
 
 public abstract class SyncableObject<T extends SyncableObject<T>> extends Observable implements QSyncableObject<T> {
     @Nullable
@@ -53,9 +54,23 @@ public abstract class SyncableObject<T extends SyncableObject<T>> extends Observ
     }
 
     public void sync(@NonNull String methodName, @NonNull Object[] params) {
+        assertTrue(initialized);
         assertNotNull(provider);
 
-        provider.dispatch(new SyncFunction<>(getClassName(), getObjectName(), methodName, Arrays.asList(params)));
+        provider.dispatch(new SyncFunction<>(getClassName(), getObjectName(), methodName, toVariantList(params)));
+    }
+
+    public void sync(@NonNull String methodName, @NonNull String[] strings, @NonNull Object[] objects) {
+        assertTrue(initialized);
+        assertNotNull(provider);
+        assertEquals(strings.length, objects.length);
+
+        List<QVariant> params = new ArrayList<>();
+        for (int i = 0; i < strings.length; i++) {
+            params.add(new QVariant<>(strings[i], objects[i]));
+        }
+
+        provider.dispatch(new SyncFunction<>(getClassName(), getObjectName(), methodName, params));
     }
 
     @NonNull
@@ -90,6 +105,7 @@ public abstract class SyncableObject<T extends SyncableObject<T>> extends Observ
     }
 
     public void rpc(@NonNull String procedureName, @NonNull List<QVariant> params) {
+        assertTrue(initialized);
         assertNotNull(provider);
 
         RpcCallFunction function = new RpcCallFunction(procedureName, params);
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ABacklogManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ABacklogManager.java
index 88c014777..09b4c6e23 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ABacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ABacklogManager.java
@@ -24,31 +24,34 @@ package de.kuschku.libquassel.syncables.types.abstracts;
 import java.util.List;
 
 import de.kuschku.libquassel.message.Message;
+import de.kuschku.libquassel.primitives.QMetaType;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
 import de.kuschku.libquassel.syncables.types.interfaces.QBacklogManager;
 
-public abstract class ABacklogManager<T extends ABacklogManager<T>> extends SyncableObject<T> implements QBacklogManager {
+public abstract class ABacklogManager<T extends ABacklogManager<T>> extends SyncableObject<T> implements QBacklogManager<T> {
+    static String intName = QMetaType.Type.Int.getSerializableName();
+
     @Override
     public void requestBacklog(int id, int first, int last, int limit, int additional) {
         _requestBacklog(id, first, last, limit, additional);
-        syncVar("requestBacklog", id, first, last, limit, additional);
+        sync("requestBacklog", new String[]{"BufferId", "MsgId", "MsgId", intName, intName}, new Object[]{id, first, last, limit, additional});
     }
 
     @Override
     public void receiveBacklog(int id, int first, int last, int limit, int additional, List<Message> messages) {
         _receiveBacklog(id, first, last, limit, additional, messages);
-        syncVar("receiveBacklog", id, first, last, limit, additional, messages);
+        sync("receiveBacklog", new String[]{"BufferId", "MsgId", "MsgId", intName, intName}, new Object[]{id, first, last, limit, additional, messages});
     }
 
     @Override
     public void requestBacklogAll(int first, int last, int limit, int additional) {
         _requestBacklogAll(first, last, limit, additional);
-        syncVar("requestBacklogAll", first, last, limit, additional);
+        sync("requestBacklogAll", new String[]{"MsgId", "MsgId", intName, intName}, new Object[]{first, last, limit, additional});
     }
 
     @Override
     public void receiveBacklogAll(int first, int last, int limit, int additional, List<Message> messages) {
         _receiveBacklogAll(first, last, limit, additional, messages);
-        syncVar("receiveBacklogAll", first, last, limit, additional, messages);
+        sync("receiveBacklogAll", new String[]{"MsgId", "MsgId", intName, intName}, new Object[]{first, last, limit, additional, messages});
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java
index e732e424b..1d0bb6287 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BacklogManager.java
@@ -23,18 +23,25 @@ package de.kuschku.libquassel.syncables.types.impl;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.util.Log;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.client.Client;
-import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
+import de.kuschku.libquassel.events.BacklogReceivedEvent;
+import de.kuschku.libquassel.localtypes.BacklogFilter;
 import de.kuschku.libquassel.localtypes.backlogstorage.BacklogStorage;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.abstracts.ABacklogManager;
 import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 
+import static de.kuschku.util.AndroidAssert.assertNotNull;
+
 public class BacklogManager extends ABacklogManager<BacklogManager> {
     private final Client client;
     private final BacklogStorage storage;
@@ -46,56 +53,75 @@ public class BacklogManager extends ABacklogManager<BacklogManager> {
 
     @Override
     public void requestMoreBacklog(int bufferId, int amount) {
-
+        Log.d("libquassel", "request more backlog for id " + bufferId + ": " + amount);
+        Message last;
+        if (storage.getUnfiltered(bufferId).isEmpty() || null == (last = storage.getUnfiltered(bufferId).last()))
+            requestBacklogInitial(bufferId, amount);
+        else {
+            requestBacklog(bufferId, -1, last.messageId, amount, 0);
+        }
     }
 
     @Override
     public void requestBacklogInitial(int id, int amount) {
+        Log.d("libquassel", "request initial backlog for id " + id + ": " + amount);
         requestBacklog(id, -1, -1, amount, 0);
     }
 
     @Override
     public void _requestBacklog(int id, int first, int last, int limit, int additional) {
         // Do nothing, we are on the client
+        Log.d("libquassel", "request backlog for id " + id);
     }
 
     @Override
     public void _receiveBacklog(int id, int first, int last, int limit, int additional, @NonNull List<Message> messages) {
+        assertNotNull(provider);
+
         storage.insertMessages(id, messages.toArray(new Message[messages.size()]));
         client.initBacklog(id);
+        provider.sendEvent(new BacklogReceivedEvent(id));
+
+        Log.d("libquassel", "received backlog for id " + id);
     }
 
     @Override
     public void _requestBacklogAll(int first, int last, int limit, int additional) {
         // Do nothing, we are on the client
+        Log.d("libquassel", "request backlog for all");
     }
 
     @Override
     public void _receiveBacklogAll(int first, int last, int limit, int additional, @NonNull List<Message> messages) {
+        assertNotNull(provider);
+
+        Set<Integer> buffers = new HashSet<>();
         for (Message message : messages) {
             storage.insertMessages(message.bufferInfo.id(), message);
+            buffers.add(message.bufferInfo.id());
+        }
+        for (int id : buffers) {
+            provider.sendEvent(new BacklogReceivedEvent(id));
+            Log.d("libquassel", "received backlog for id " + id);
         }
     }
 
-    // FIXME: Implement
     @Nullable
     @Override
     public BacklogFilter filter(int id) {
-        return null;
+        return storage.getFilter(id);
     }
 
-    // FIXME: Implement
     @Nullable
     @Override
     public ObservableComparableSortedList<Message> unfiltered(int id) {
-        return new ObservableComparableSortedList<>(Message.class);
+        return storage.getUnfiltered(id);
     }
 
-    // FIXME: Implement
     @Nullable
     @Override
     public ObservableComparableSortedList<Message> filtered(int id) {
-        return new ObservableComparableSortedList<>(Message.class);
+        return storage.getFiltered(id);
     }
 
     @Override
@@ -107,4 +133,9 @@ public class BacklogManager extends ABacklogManager<BacklogManager> {
     public void update(BacklogManager from) {
 
     }
+
+    @Override
+    public void init(@NonNull String objectName, @NonNull BusProvider provider, @NonNull Client client) {
+        super.init(objectName, provider, client);
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java
index 4a4c44eb0..be65a64fc 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBacklogManager.java
@@ -25,12 +25,12 @@ import android.support.annotation.Nullable;
 
 import java.util.List;
 
-import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
+import de.kuschku.libquassel.localtypes.BacklogFilter;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.syncables.Synced;
 import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 
-public interface QBacklogManager {
+public interface QBacklogManager<T extends QSyncableObject<T>> extends QSyncableObject<T> {
     void requestMoreBacklog(int bufferId, int amount);
 
     void requestBacklogInitial(int id, int amount);
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
index c6724ed5a..b8cda329a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
@@ -80,7 +80,7 @@ import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.LagChangedEvent;
 import de.kuschku.libquassel.events.LoginRequireEvent;
 import de.kuschku.libquassel.events.UnknownCertificateEvent;
-import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
+import de.kuschku.libquassel.localtypes.BacklogFilter;
 import de.kuschku.libquassel.localtypes.buffers.Buffer;
 import de.kuschku.libquassel.localtypes.buffers.ChannelBuffer;
 import de.kuschku.libquassel.message.Message;
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java
index a18cc0310..5c8f238de 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java
@@ -24,6 +24,7 @@ package de.kuschku.quasseldroid_ng.ui.chat.chatview;
 import android.support.annotation.NonNull;
 import android.support.annotation.UiThread;
 import android.support.v7.widget.RecyclerView;
+import android.text.method.LinkMovementMethod;
 import android.view.View;
 import android.widget.TextView;
 
@@ -46,5 +47,6 @@ public class MessageViewHolder extends RecyclerView.ViewHolder {
     public MessageViewHolder(@NonNull View itemView) {
         super(itemView);
         ButterKnife.bind(this, itemView);
+        content.setMovementMethod(LinkMovementMethod.getInstance());
     }
 }
diff --git a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java b/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
index 27b1f7a57..ad9999231 100644
--- a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
+++ b/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
@@ -87,7 +87,7 @@ public class IrcFormatHelper {
         SpannableString str = new SpannableString(MessageUtil.parseStyleCodes(context.themeUtil(), message, context.settings().mircColors.get()));
         Matcher urlMatcher = urlPattern.matcher(str);
         while (urlMatcher.find()) {
-            spans.add(new FutureClickableSpan(new CustomURLSpan(urlMatcher.toString()), urlMatcher.start(), urlMatcher.end()));
+            spans.add(new FutureClickableSpan(new CustomURLSpan(urlMatcher.group()), urlMatcher.start(), urlMatcher.end()));
         }
         for (FutureClickableSpan span : spans) {
             str.setSpan(span.span, span.start, span.end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
@@ -137,8 +137,6 @@ public class IrcFormatHelper {
 
         @Override
         public void onClick(@NonNull View widget) {
-            Log.e("TEST", "THIS IS A TEST");
-
             Uri uri = Uri.parse(getURL());
             Context context = widget.getContext();
             Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-- 
GitLab