Browse Source

Initial mention implementation

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 7 years ago
parent
commit
fce49d717e

+ 1 - 0
app/build.gradle

@@ -151,6 +151,7 @@ dependencies {
     implementation 'com.github.wooplr:Spotlight:1.2.3'
 
     implementation 'com.github.stfalcon:chatkit:0.2.2'
+    implementation 'com.otaliastudios:autocomplete:1.1.0'
 
     implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'
     implementation 'eu.davidea:flipview:1.1.3'

+ 116 - 0
app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java

@@ -0,0 +1,116 @@
+/*
+ * 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.adapters.items;
+
+import android.view.View;
+
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.load.model.LazyHeaders;
+import com.bumptech.glide.load.resource.bitmap.CircleCrop;
+import com.bumptech.glide.request.RequestOptions;
+import com.nextcloud.talk.R;
+import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.models.database.UserEntity;
+import com.nextcloud.talk.utils.ApiUtils;
+import com.nextcloud.talk.utils.glide.GlideApp;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+import eu.davidea.flexibleadapter.items.IFilterable;
+import eu.davidea.flexibleadapter.items.IFlexible;
+
+public class MentionAutocompleteItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
+        implements IFilterable<String> {
+
+    private String userId;
+    private String displayName;
+    private UserEntity currentUser;
+
+    public MentionAutocompleteItem(String userId, String displayName, UserEntity currentUser) {
+        this.userId = userId;
+        this.displayName = displayName;
+        this.currentUser = currentUser;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof MentionAutocompleteItem) {
+            MentionAutocompleteItem inItem = (MentionAutocompleteItem) o;
+            return (userId.equals(inItem.userId) && displayName.equals(inItem.displayName));
+        }
+
+        return false;
+    }
+
+    @Override
+    public int getLayoutRes() {
+        return R.layout.rv_item_mention;
+    }
+
+    @Override
+    public UserItem.UserItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
+        return new UserItem.UserItemViewHolder(view, adapter);
+    }
+
+
+    @Override
+    public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, UserItem.UserItemViewHolder holder, int position, List<Object> payloads) {
+        holder.contactDisplayName.setText(displayName);
+        holder.contactMentionId.setText("@" + userId);
+
+        GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(),
+                userId, false), new LazyHeaders.Builder()
+                .setHeader("Accept", "image/*")
+                .setHeader("User-Agent", ApiUtils.getUserAgent())
+                .build());
+
+        int avatarSize = Math.round(NextcloudTalkApplication
+                .getSharedApplication().getResources().getDimension(R.dimen.avatar_size));
+
+        GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
+                .asBitmap()
+                .diskCacheStrategy(DiskCacheStrategy.NONE)
+                .load(glideUrl)
+                .centerInside()
+                .override(avatarSize, avatarSize)
+                .apply(RequestOptions.bitmapTransform(new CircleCrop()))
+                .into(holder.avatarFlipView.getFrontImageView());
+    }
+
+    @Override
+    public boolean filter(String constraint) {
+        return userId != null && StringUtils.containsIgnoreCase(userId, constraint);
+
+    }
+}

+ 4 - 0
app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java

@@ -39,6 +39,8 @@ import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import butterknife.BindView;
 import butterknife.ButterKnife;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
@@ -161,6 +163,8 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
         public TextView contactDisplayName;
         @BindView(R.id.avatar_flip_view)
         public FlipView avatarFlipView;
+        @Nullable @BindView(R.id.secondary_text)
+        public TextView contactMentionId;
 
         /**
          * Default constructor.

+ 8 - 0
app/src/main/java/com/nextcloud/talk/api/NcApi.java

@@ -27,6 +27,7 @@ import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
 import com.nextcloud.talk.models.json.chat.ChatOverall;
 import com.nextcloud.talk.models.json.generic.GenericOverall;
 import com.nextcloud.talk.models.json.generic.Status;
+import com.nextcloud.talk.models.json.mention.MentionOverall;
 import com.nextcloud.talk.models.json.participants.AddParticipantOverall;
 import com.nextcloud.talk.models.json.participants.ParticipantsOverall;
 import com.nextcloud.talk.models.json.push.PushRegistrationOverall;
@@ -49,6 +50,7 @@ import retrofit2.http.GET;
 import retrofit2.http.Header;
 import retrofit2.http.POST;
 import retrofit2.http.PUT;
+import retrofit2.http.Query;
 import retrofit2.http.QueryMap;
 import retrofit2.http.Url;
 
@@ -282,4 +284,10 @@ public interface NcApi {
                                                @FieldMap Map<String, String> fields);
 
 
+    //@Headers("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
+    @GET
+    Observable<MentionOverall> getMentionAutocompleteSuggestions(@Header("Authorization") String authorization,
+                                                                           @Url String url, @Query("search") String query,
+                                                                           @Nullable @Query("limit") Integer limit);
+
 }

+ 45 - 0
app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java

@@ -0,0 +1,45 @@
+/*
+ * 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.callbacks;
+
+import android.text.Editable;
+
+import com.nextcloud.talk.models.json.mention.Mention;
+import com.otaliastudios.autocomplete.AutocompleteCallback;
+import com.otaliastudios.autocomplete.CharPolicy;
+
+public class MentionAutocompleteCallback implements AutocompleteCallback<Mention> {
+    @Override
+    public boolean onPopupItemClicked(Editable editable, Mention item) {
+        int[] range = CharPolicy.getQueryRange(editable);
+        if (range == null) return false;
+        int start = range[0];
+        int end = range[1];
+        String replacement = item.getId() + " ";
+        editable.replace(start, end, replacement);
+        return true;
+    }
+
+    @Override
+    public void onPopupVisibilityChanged(boolean shown) {
+
+    }
+}

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

@@ -22,6 +22,9 @@ package com.nextcloud.talk.controllers;
 
 
 import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.v7.widget.LinearLayoutManager;
@@ -32,6 +35,7 @@ import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
 import android.widget.ImageView;
 
 import com.bluelinelabs.conductor.RouterTransaction;
@@ -44,6 +48,7 @@ import com.nextcloud.talk.activities.CallActivity;
 import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder;
 import com.nextcloud.talk.api.NcApi;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.callbacks.MentionAutocompleteCallback;
 import com.nextcloud.talk.controllers.base.BaseController;
 import com.nextcloud.talk.models.database.UserEntity;
 import com.nextcloud.talk.models.json.call.Call;
@@ -51,10 +56,16 @@ import com.nextcloud.talk.models.json.call.CallOverall;
 import com.nextcloud.talk.models.json.chat.ChatMessage;
 import com.nextcloud.talk.models.json.chat.ChatOverall;
 import com.nextcloud.talk.models.json.generic.GenericOverall;
+import com.nextcloud.talk.models.json.mention.Mention;
+import com.nextcloud.talk.presenters.MentionAutocompletePresenter;
 import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.database.user.UserUtils;
 import com.nextcloud.talk.utils.glide.GlideApp;
+import com.otaliastudios.autocomplete.Autocomplete;
+import com.otaliastudios.autocomplete.AutocompleteCallback;
+import com.otaliastudios.autocomplete.AutocompletePresenter;
+import com.otaliastudios.autocomplete.CharPolicy;
 import com.stfalcon.chatkit.commons.ImageLoader;
 import com.stfalcon.chatkit.messages.MessageInput;
 import com.stfalcon.chatkit.messages.MessagesList;
@@ -106,6 +117,15 @@ public class ChatController extends BaseController implements MessagesListAdapte
     private MessagesListAdapter<ChatMessage> adapter;
     private Menu globalMenu;
 
+    private Autocomplete mentionAutocomplete;
+    /*
+    TODO:
+        - format mentions
+        - copy message
+        - autocomplete nicks
+        - check push notifications
+        - new conversation handling
+     */
     public ChatController(Bundle args) {
         super(args);
         setHasOptionsMenu(true);
@@ -155,6 +175,10 @@ public class ChatController extends BaseController implements MessagesListAdapte
         adapter.setDateHeadersFormatter(this::format);
         //adapter.enableSelectionMode(this);
 
+
+        setupMentionAutocomplete();
+
+        messageInput.getInputEditText().setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
         messageInput.setInputListener(input -> {
             sendMessage(input.toString());
             return true;
@@ -165,6 +189,21 @@ public class ChatController extends BaseController implements MessagesListAdapte
         }
     }
 
+    private void setupMentionAutocomplete() {
+        float elevation = 6f;
+        Drawable backgroundDrawable = new ColorDrawable(Color.WHITE);
+        AutocompletePresenter<Mention> presenter = new MentionAutocompletePresenter(getApplicationContext(), roomToken);
+        AutocompleteCallback<Mention> callback = new MentionAutocompleteCallback();
+
+        mentionAutocomplete = Autocomplete.<Mention>on(messageInput.getInputEditText())
+                .with(elevation)
+                .with(backgroundDrawable)
+                .with(new CharPolicy('@'))
+                .with(presenter)
+                .with(callback)
+                .build();
+    }
+
     @Override
     protected void onAttach(@NonNull View view) {
         super.onAttach(view);

+ 3 - 1
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOCS.java

@@ -27,12 +27,14 @@ import org.parceler.Parcel;
 
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import lombok.Data;
 
 @Data
 @Parcel
 @JsonObject
 public class ChatOCS extends GenericOCS {
-    @JsonField(name = "data")
+    @Nullable @JsonField(name = "data")
     List<ChatMessage> data;
 }

+ 1 - 1
app/src/main/java/com/nextcloud/talk/models/json/generic/GenericMeta.java

@@ -29,7 +29,7 @@ import lombok.Data;
 
 @Parcel
 @Data
-@JsonObject
+@JsonObject(serializeNullObjects = true)
 public class GenericMeta {
     @JsonField(name = "status")
     String status;

+ 42 - 0
app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java

@@ -0,0 +1,42 @@
+/*
+ * 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.models.json.mention;
+
+import com.bluelinelabs.logansquare.annotation.JsonField;
+import com.bluelinelabs.logansquare.annotation.JsonObject;
+
+import org.parceler.Parcel;
+
+import lombok.Data;
+
+@Parcel
+@Data
+@JsonObject
+public class Mention {
+    @JsonField(name = "id")
+    String id;
+
+    @JsonField(name = "label")
+    String label;
+
+    // type of user (guests or users)
+    @JsonField(name = "source")
+    String source;
+}

+ 38 - 0
app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOCS.java

@@ -0,0 +1,38 @@
+/*
+ * 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.models.json.mention;
+
+import com.bluelinelabs.logansquare.annotation.JsonField;
+import com.bluelinelabs.logansquare.annotation.JsonObject;
+import com.nextcloud.talk.models.json.generic.GenericOCS;
+
+import org.parceler.Parcel;
+
+import java.util.List;
+
+import lombok.Data;
+
+@Data
+@Parcel
+@JsonObject
+public class MentionOCS extends GenericOCS {
+    @JsonField(name = "data")
+    List<Mention> data;
+}

+ 35 - 0
app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOverall.java

@@ -0,0 +1,35 @@
+/*
+ * 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.models.json.mention;
+
+import com.bluelinelabs.logansquare.annotation.JsonField;
+import com.bluelinelabs.logansquare.annotation.JsonObject;
+
+import org.parceler.Parcel;
+
+import lombok.Data;
+
+@Data
+@Parcel
+@JsonObject
+public class MentionOverall {
+    @JsonField(name = "ocs")
+    MentionOCS ocs;
+}

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

@@ -0,0 +1,153 @@
+/*
+ * 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.presenters;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.nextcloud.talk.adapters.items.MentionAutocompleteItem;
+import com.nextcloud.talk.api.NcApi;
+import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.models.database.UserEntity;
+import com.nextcloud.talk.models.json.mention.Mention;
+import com.nextcloud.talk.models.json.mention.MentionOverall;
+import com.nextcloud.talk.utils.ApiUtils;
+import com.nextcloud.talk.utils.database.user.UserUtils;
+import com.otaliastudios.autocomplete.RecyclerViewPresenter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import autodagger.AutoInjector;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+@AutoInjector(NextcloudTalkApplication.class)
+public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention> implements FlexibleAdapter.OnItemClickListener {
+    @Inject
+    NcApi ncApi;
+
+    @Inject
+    UserUtils userUtils;
+
+    private FlexibleAdapter<AbstractFlexibleItem> adapter;
+    private Context context;
+
+    private String roomToken;
+    private List<AbstractFlexibleItem> userItemList = new ArrayList<>();
+
+    public MentionAutocompletePresenter(Context context) {
+        super(context);
+        this.context = context;
+        NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
+    }
+
+    public MentionAutocompletePresenter(Context context, String roomToken) {
+        super(context);
+        this.roomToken = roomToken;
+        this.context = context;
+        NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
+    }
+
+    @Override
+    protected RecyclerView.Adapter instantiateAdapter() {
+        adapter = new FlexibleAdapter<>(userItemList, context, true);
+        adapter.addListener(this);
+        return adapter;
+    }
+
+    @Override
+    protected void onQuery(@Nullable CharSequence query) {
+        if (query != null && query.length() > 0) {
+            UserEntity currentUser = userUtils.getCurrentUser();
+
+            ncApi.getMentionAutocompleteSuggestions(ApiUtils.getCredentials(currentUser.getUserId(), 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();
+                            userItemList = new ArrayList<>();
+                            if (mentionsList.size() == 1 && mentionsList.get(0).getId().equals(query.toString())) {
+                                adapter.updateDataSet(userItemList, false);
+                                clearRecycledPool();
+                            } else {
+                                for (Mention mention : mentionsList) {
+                                    userItemList.add(new MentionAutocompleteItem(mention.getId(), mention
+                                            .getLabel(), currentUser));
+                                }
+                                adapter.updateDataSet(userItemList, true);
+                                clearRecycledPool();
+                            }
+                        }
+
+                        @Override
+                        public void onError(Throwable e) {
+                            userItemList = new ArrayList<>();
+                            adapter.updateDataSet(userItemList, false);
+                            clearRecycledPool();
+                        }
+
+                        @Override
+                        public void onComplete() {
+
+                        }
+                    });
+        } else {
+            userItemList = new ArrayList<>();
+            adapter.updateDataSet(userItemList, false);
+            clearRecycledPool();
+        }
+    }
+
+    private void clearRecycledPool() {
+        if (getRecyclerView() != null) {
+            getRecyclerView().getRecycledViewPool().clear();
+        }
+    }
+
+    @Override
+    public boolean onItemClick(View view, int position) {
+        Mention mention = new Mention();
+        MentionAutocompleteItem mentionAutocompleteItem = (MentionAutocompleteItem) userItemList.get(position);
+        mention.setId(mentionAutocompleteItem.getUserId());
+        mention.setLabel(mentionAutocompleteItem.getDisplayName());
+        mention.setSource("users");
+        dispatchClick(mention);
+        return true;
+    }
+}

+ 4 - 0
app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java

@@ -130,6 +130,10 @@ public class ApiUtils {
         return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token;
     }
 
+    public static String getUrlForMentionSuggestions(String baseUrl, String token) {
+        return getUrlForChat(baseUrl, token) + "/mentions";
+    }
+
     public static String getUrlForSignaling(String baseUrl, @Nullable String token) {
         String signalingUrl = baseUrl + ocsApiVersion + spreedApiVersion + "/signaling";
         if (token == null) {

+ 1 - 2
app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java

@@ -245,8 +245,7 @@ public class UserUtils {
 
         return dataStore.upsert(user)
                 .toObservable()
-                .subscribeOn(Schedulers.newThread())
-                .observeOn(AndroidSchedulers.mainThread());
+                .subscribeOn(Schedulers.newThread());
     }
 
 }

+ 78 - 0
app/src/main/res/layout/rv_item_mention.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Mario Danic
+  ~ @author Andy Scherzinger
+  ~ Copyright (C) 2017-2018 Mario Danic
+  ~ Copyright (C) 2017 Andy Scherzinger
+  ~
+  ~ 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/>.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                xmlns:app="http://schemas.android.com/apk/res-auto"
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/item_height"
+                android:orientation="vertical">
+
+    <FrameLayout
+        android:id="@+id/frame_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="@dimen/activity_horizontal_margin">
+
+        <com.nextcloud.talk.utils.MagicFlipView
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            android:id="@+id/avatar_flip_view"
+            android:layout_width="@dimen/avatar_size"
+            android:layout_height="@dimen/avatar_size"
+            app:animationDuration="170"
+            app:checked="false"
+            app:enableInitialAnimation="false"
+            app:rearBackgroundColor="@color/colorPrimary"/>
+
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/linear_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:layout_marginStart="@dimen/margin_between_elements"
+        android:layout_toEndOf="@id/frame_layout"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/name_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="middle"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            tools:text="Call item text"/>
+
+        <TextView
+            android:id="@+id/secondary_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorSecondary"
+            tools:text="A week ago"/>
+
+    </LinearLayout>
+
+</RelativeLayout>