Selaa lähdekoodia

Bunch of work on new conversation

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 7 vuotta sitten
vanhempi
commit
20b80b36b3
33 muutettua tiedostoa jossa 854 lisäystä ja 173 poistoa
  1. 2 1
      app/build.gradle
  2. 10 9
      app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java
  3. 11 7
      app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java
  4. 78 0
      app/src/main/java/com/nextcloud/talk/adapters/items/EmptyFooterItem.java
  5. 103 0
      app/src/main/java/com/nextcloud/talk/adapters/items/UserHeaderItem.java
  6. 34 7
      app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java
  7. 0 1
      app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java
  8. 1 1
      app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java
  9. 55 7
      app/src/main/java/com/nextcloud/talk/controllers/CallsListController.java
  10. 230 102
      app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java
  11. 1 1
      app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java
  12. 22 0
      app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java
  13. 1 0
      app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java
  14. 1 1
      app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java
  15. 1 1
      app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java
  16. 1 1
      app/src/main/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.java
  17. 45 0
      app/src/main/java/com/nextcloud/talk/utils/MagicFlipView.java
  18. 1 0
      app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.java
  19. 2 2
      app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java
  20. 1 1
      app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java
  21. 25 0
      app/src/main/res/drawable/ic_add_white_24px.xml
  22. 52 0
      app/src/main/res/layout/bottom_buttons.xml
  23. 4 4
      app/src/main/res/layout/controller_call_menu.xml
  24. 1 1
      app/src/main/res/layout/controller_entry_menu.xml
  25. 32 12
      app/src/main/res/layout/controller_generic_rv.xml
  26. 38 0
      app/src/main/res/layout/fast_scroller.xml
  27. 13 4
      app/src/main/res/layout/rv_item_call.xml
  28. 9 5
      app/src/main/res/layout/rv_item_contact.xml
  29. 27 0
      app/src/main/res/layout/rv_item_empty_footer.xml
  30. 39 0
      app/src/main/res/layout/rv_item_title_header.xml
  31. 3 3
      app/src/main/res/layout/surface_renderer.xml
  32. 7 2
      app/src/main/res/menu/menu_conversation_plus_filter.xml
  33. 4 0
      app/src/main/res/values/strings.xml

+ 2 - 1
app/build.gradle

@@ -111,7 +111,7 @@ dependencies {
 
     debugImplementation "javax.transaction:transaction-api:1.1-rev-1"
 
-    implementation 'com.github.HITGIF:TextFieldBoxes:1.3.8'
+    implementation 'com.github.HITGIF:TextFieldBoxes:1.3.9'
 
     implementation 'eu.davidea:flexible-adapter:5.0.0-rc4'
     implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1'
@@ -138,6 +138,7 @@ dependencies {
     implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
 
     implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'
+    implementation 'eu.davidea:flipview:1.1.3'
     testImplementation 'junit:junit:4.12'
     androidTestImplementation ('com.android.support.test.espresso:espresso-core:3.0.1', {
         exclude group: 'com.android.support', module: 'support-annotations'

+ 10 - 9
app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java

@@ -51,6 +51,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
 import eu.davidea.flexibleadapter.items.IFilterable;
 import eu.davidea.flexibleadapter.utils.FlexibleUtils;
+import eu.davidea.flipview.FlipView;
 import eu.davidea.viewholders.FlexibleViewHolder;
 
 public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.UserItemViewHolder> implements IFilterable {
@@ -119,24 +120,23 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
 
         if (userEntity.getBaseUrl().startsWith("http://") || userEntity.getBaseUrl().startsWith("https://")) {
             holder.avatarImageView.setVisibility(View.VISIBLE);
-            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.linearLayout.getLayoutParams();
-            layoutParams.setMarginStart(0);
-            layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
-            holder.linearLayout.setLayoutParams(layoutParams);
             GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(),
                     participant.getUserId(), 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()
-                    .skipMemoryCache(true)
                     .diskCacheStrategy(DiskCacheStrategy.NONE)
                     .load(glideUrl)
                     .centerInside()
+                    .override(avatarSize, avatarSize)
                     .apply(RequestOptions.bitmapTransform(new CircleCrop()))
-                    .into(holder.avatarImageView);
+                    .into(holder.avatarImageView.getFrontImageView());
         } else {
             holder.avatarImageView.setVisibility(View.GONE);
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.linearLayout.getLayoutParams();
@@ -161,11 +161,13 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
         @BindView(R.id.secondary_text)
         public TextView serverUrl;
         @BindView(R.id.avatar_image)
-        public ImageView avatarImageView;
+        public FlipView avatarImageView;
         @BindView(R.id.linear_layout)
         LinearLayout linearLayout;
         @BindView(R.id.more_menu)
         ImageButton moreMenuButton;
+        @BindView(R.id.password_protected_image_view)
+        ImageView passwordProtectedImageView;
 
         /**
          * Default constructor.
@@ -174,8 +176,7 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
             super(view, adapter);
             ButterKnife.bind(this, view);
             moreMenuButton.setVisibility(View.GONE);
+            passwordProtectedImageView.setVisibility(View.GONE);
         }
     }
-
-
 }

+ 11 - 7
app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java

@@ -51,6 +51,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
 import eu.davidea.flexibleadapter.items.IFilterable;
 import eu.davidea.flexibleadapter.utils.FlexibleUtils;
+import eu.davidea.flipview.FlipView;
 import eu.davidea.viewholders.FlexibleViewHolder;
 
 public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder> implements IFilterable {
@@ -120,6 +121,9 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
             holder.passwordProtectedImageView.setVisibility(View.GONE);
         }
 
+        int avatarSize = Math.round(NextcloudTalkApplication
+                .getSharedApplication().getResources().getDimension(R.dimen.avatar_size));
+
         switch (room.getType()) {
             case ROOM_TYPE_ONE_TO_ONE_CALL:
                 holder.avatarImageView.setVisibility(View.VISIBLE);
@@ -133,12 +137,12 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
 
                     GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
                             .asBitmap()
-                            .skipMemoryCache(true)
                             .diskCacheStrategy(DiskCacheStrategy.NONE)
                             .load(glideUrl)
                             .centerInside()
+                            .override(avatarSize, avatarSize)
                             .apply(RequestOptions.bitmapTransform(new CircleCrop()))
-                            .into(holder.avatarImageView);
+                            .into(holder.avatarImageView.getFrontImageView());
 
                 } else {
                     holder.avatarImageView.setVisibility(View.GONE);
@@ -147,23 +151,23 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
             case ROOM_GROUP_CALL:
                 GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
                         .asBitmap()
-                        .skipMemoryCache(true)
                         .diskCacheStrategy(DiskCacheStrategy.NONE)
                         .load(R.drawable.ic_group_white_24px)
                         .centerInside()
+                        .override(avatarSize, avatarSize)
                         .apply(RequestOptions.bitmapTransform(new CircleCrop()))
-                        .into(holder.avatarImageView);
+                        .into(holder.avatarImageView.getFrontImageView());
                 holder.avatarImageView.setVisibility(View.VISIBLE);
                 break;
             case ROOM_PUBLIC_CALL:
                 GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
                         .asBitmap()
-                        .skipMemoryCache(true)
                         .diskCacheStrategy(DiskCacheStrategy.NONE)
                         .load(R.drawable.ic_link_white_24px)
                         .centerInside()
+                        .override(avatarSize, avatarSize)
                         .apply(RequestOptions.bitmapTransform(new CircleCrop()))
-                        .into(holder.avatarImageView);
+                        .into(holder.avatarImageView.getFrontImageView());
                 holder.avatarImageView.setVisibility(View.VISIBLE);
                 break;
             default:
@@ -188,7 +192,7 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
         @BindView(R.id.secondary_text)
         public TextView roomLastPing;
         @BindView(R.id.avatar_image)
-        public ImageView avatarImageView;
+        public FlipView avatarImageView;
         @BindView(R.id.more_menu)
         public ImageButton moreMenuButton;
         @BindView(R.id.password_protected_image_view)

+ 78 - 0
app/src/main/java/com/nextcloud/talk/adapters/items/EmptyFooterItem.java

@@ -0,0 +1,78 @@
+/*
+ * 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.nextcloud.talk.R;
+
+import java.util.List;
+
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+import eu.davidea.viewholders.FlexibleViewHolder;
+
+public class EmptyFooterItem extends AbstractFlexibleItem<EmptyFooterItem.EmptyFooterItemViewHolder> {
+    int id;
+
+    public EmptyFooterItem(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof EmptyFooterItem) {
+            EmptyFooterItem inItem = (EmptyFooterItem) o;
+            return id == inItem.getModel();
+        }
+        return false;
+    }
+
+    public int getModel() {
+        return id;
+    }
+
+
+    @Override
+    public int getLayoutRes() {
+        return R.layout.rv_item_empty_footer;
+    }
+
+    @Override
+    public EmptyFooterItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
+        return new EmptyFooterItemViewHolder(view, adapter);
+    }
+
+    @Override
+    public void bindViewHolder(FlexibleAdapter adapter, EmptyFooterItemViewHolder holder, int position, List<Object> payloads) {
+
+    }
+
+    static class EmptyFooterItemViewHolder extends FlexibleViewHolder {
+        /**
+         * Default constructor.
+         */
+        EmptyFooterItemViewHolder(View view, FlexibleAdapter adapter) {
+            super(view, adapter);
+        }
+
+    }
+}

+ 103 - 0
app/src/main/java/com/nextcloud/talk/adapters/items/UserHeaderItem.java

@@ -0,0 +1,103 @@
+/*
+ * 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.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.nextcloud.talk.R;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractHeaderItem;
+import eu.davidea.flexibleadapter.items.IFilterable;
+import eu.davidea.viewholders.FlexibleViewHolder;
+
+public class UserHeaderItem extends AbstractHeaderItem<UserHeaderItem.HeaderViewHolder> implements IFilterable {
+    private static final String TAG = "UserHeaderItem";
+
+    private String title;
+
+    public UserHeaderItem(String title) {
+        super();
+        setHidden(false);
+        setSelectable(false);
+        this.title = title;
+    }
+
+    @Override
+    public boolean filter(String constraint) {
+        return StringUtils.containsIgnoreCase(title, constraint);
+    }
+
+    public String getModel() {
+        return title;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof UserHeaderItem) {
+            UserHeaderItem inItem = (UserHeaderItem) o;
+            return title.equals(inItem.getModel());
+        }
+        return false;
+    }
+
+    @Override
+    public int getLayoutRes() {
+        return R.layout.rv_item_title_header;
+    }
+
+    @Override
+    public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
+        return new HeaderViewHolder(view, adapter);
+    }
+
+    @Override
+    public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List<Object> payloads) {
+        if (payloads.size() > 0) {
+            Log.d(TAG, "We have payloads, so ignoring!");
+        } else {
+            holder.titleTextView.setText(title);
+        }
+    }
+
+    static class HeaderViewHolder extends FlexibleViewHolder {
+
+        @BindView(R.id.title_text_view)
+        public TextView titleTextView;
+
+        /**
+         * Default constructor.
+         */
+        HeaderViewHolder(View view, FlexibleAdapter adapter) {
+            super(view, adapter, true);
+            ButterKnife.bind(this, view);
+        }
+    }
+
+}

+ 34 - 7
app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java

@@ -21,7 +21,6 @@
 package com.nextcloud.talk.adapters.items;
 
 import android.view.View;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.bumptech.glide.load.engine.DiskCacheStrategy;
@@ -45,17 +44,25 @@ import butterknife.ButterKnife;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
 import eu.davidea.flexibleadapter.items.IFilterable;
+import eu.davidea.flexibleadapter.items.ISectionable;
 import eu.davidea.flexibleadapter.utils.FlexibleUtils;
+import eu.davidea.flipview.FlipView;
 import eu.davidea.viewholders.FlexibleViewHolder;
 
-public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder> implements IFilterable {
+public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder> implements
+        ISectionable<UserItem.UserItemViewHolder, UserHeaderItem>, IFilterable {
 
     private Participant participant;
     private UserEntity userEntity;
+    private UserHeaderItem header;
 
-    public UserItem(Participant participant, UserEntity userEntity) {
+    private FlipView flipView;
+
+
+    public UserItem(Participant participant, UserEntity userEntity, UserHeaderItem userHeaderItem) {
         this.participant = participant;
         this.userEntity = userEntity;
+        this.header = userHeaderItem;
     }
 
     @Override
@@ -84,6 +91,10 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
         return userEntity;
     }
 
+    public void flipItemSelection() {
+        flipView.flip(!flipView.isFlipped());
+    }
+
     @Override
     public int getLayoutRes() {
         return R.layout.rv_item_contact;
@@ -96,6 +107,9 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
 
     @Override
     public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) {
+
+        flipView = holder.avatarFlipView;
+
         if (adapter.hasSearchText()) {
             FlexibleUtils.highlightText(holder.contactDisplayName, participant.getName(), adapter.getSearchText());
         } else {
@@ -108,14 +122,17 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
                 .setHeader("User-Agent", ApiUtils.getUserAgent())
                 .build());
 
+        int avatarSize = Math.round(NextcloudTalkApplication
+                .getSharedApplication().getResources().getDimension(R.dimen.avatar_size));
+
         GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
                 .asBitmap()
-                .skipMemoryCache(true)
                 .diskCacheStrategy(DiskCacheStrategy.NONE)
                 .load(glideUrl)
                 .centerInside()
+                .override(avatarSize, avatarSize)
                 .apply(RequestOptions.bitmapTransform(new CircleCrop()))
-                .into(holder.avatarImageView);
+                .into(holder.avatarFlipView.getFrontImageView());
     }
 
     @Override
@@ -124,13 +141,23 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
                 StringUtils.containsIgnoreCase(participant.getName().trim(), constraint);
     }
 
+    @Override
+    public UserHeaderItem getHeader() {
+        return header;
+    }
+
+    @Override
+    public void setHeader(UserHeaderItem header) {
+        this.header = header;
+    }
+
 
     static class UserItemViewHolder extends FlexibleViewHolder {
 
         @BindView(R.id.name_text)
         public TextView contactDisplayName;
-        @BindView(R.id.avatar_image)
-        public ImageView avatarImageView;
+        @BindView(R.id.avatar_flip_view)
+        public FlipView avatarFlipView;
 
         /**
          * Default constructor.

+ 0 - 1
app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java

@@ -117,7 +117,6 @@ public class NextcloudTalkApplication extends MultiDexApplication {
         initializeWebRtc();
         DisplayUtils.useCompatVectorIfNeeded();
 
-
         try {
             buildComponent();
         } catch (final GeneralSecurityException exception) {

+ 1 - 1
app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java

@@ -36,10 +36,10 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
 import com.evernote.android.job.JobRequest;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.api.NcApi;
-import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.controllers.base.BaseController;
 import com.nextcloud.talk.jobs.PushRegistrationJob;
+import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.ApplicationWideMessageHolder;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.database.user.UserUtils;

+ 55 - 7
app/src/main/java/com/nextcloud/talk/controllers/CallsListController.java

@@ -31,7 +31,6 @@ import android.support.design.widget.BottomNavigationView;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.widget.DividerItemDecoration;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.SearchView;
 import android.text.InputType;
@@ -81,6 +80,7 @@ import javax.inject.Inject;
 
 import autodagger.AutoInjector;
 import butterknife.BindView;
+import eu.davidea.fastscroller.FastScroller;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
 import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -90,7 +90,7 @@ import retrofit2.HttpException;
 
 @AutoInjector(NextcloudTalkApplication.class)
 public class CallsListController extends BaseController implements SearchView.OnQueryTextListener,
-        FlexibleAdapter.OnItemClickListener {
+        FlexibleAdapter.OnItemClickListener, FastScroller.OnScrollStateChangeListener {
 
     public static final String TAG = "CallsListController";
 
@@ -110,6 +110,11 @@ public class CallsListController extends BaseController implements SearchView.On
     @BindView(R.id.swipe_refresh_layout)
     SwipeRefreshLayout swipeRefreshLayout;
 
+    @BindView(R.id.fast_scroller)
+    FastScroller fastScroller;
+
+    private SmoothScrollLinearLayoutManager layoutManager;
+
     private UserEntity userEntity;
     private Disposable roomsQueryDisposable;
     private FlexibleAdapter<CallItem> adapter;
@@ -117,6 +122,7 @@ public class CallsListController extends BaseController implements SearchView.On
 
     private BottomSheet bottomSheet;
     private MenuItem searchItem;
+    private Menu menuVariable;
     private SearchView searchView;
     private String searchQuery;
 
@@ -158,7 +164,6 @@ public class CallsListController extends BaseController implements SearchView.On
 
         adapter.addListener(this);
         prepareViews();
-
     }
 
     @Override
@@ -222,10 +227,32 @@ public class CallsListController extends BaseController implements SearchView.On
 
     }
 
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_new_conversation:
+                searchItem.setVisible(false);
+                menuVariable.findItem(R.id.action_new_conversation).setVisible(false);
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true);
+                if (getParentController() != null) {
+                    getParentController().getRouter().pushController(
+                            (RouterTransaction.with(new ContactsController(bundle))
+                                    .pushChangeHandler(new VerticalChangeHandler())
+                                    .popChangeHandler(new VerticalChangeHandler())));
+                }
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
-        inflater.inflate(R.menu.menu_filter, menu);
+
+        inflater.inflate(R.menu.menu_conversation_plus_filter, menu);
+        menuVariable = menu;
         searchItem = menu.findItem(R.id.action_search);
         initSearchView();
     }
@@ -312,7 +339,7 @@ public class CallsListController extends BaseController implements SearchView.On
     }
 
     private void prepareViews() {
-        LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
+        layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
         recyclerView.setLayoutManager(layoutManager);
         recyclerView.setHasFixedSize(true);
 
@@ -325,6 +352,16 @@ public class CallsListController extends BaseController implements SearchView.On
 
         swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false));
         swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
+
+        fastScroller.addOnScrollStateChangeListener(this);
+        adapter.setFastScroller(fastScroller);
+        fastScroller.setBubbleTextCreator(position -> {
+            String displayName = adapter.getItem(position).getModel().getDisplayName();
+            if(displayName.length() > 8) {
+                displayName = displayName.substring(0, 4) + "...";
+            }
+            return displayName;
+        });
     }
 
     private void dispose(@Nullable Disposable disposable) {
@@ -427,8 +464,14 @@ public class CallsListController extends BaseController implements SearchView.On
                             .pushChangeHandler(new HorizontalChangeHandler()));
         }
 
-        bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create();
-        if (bottomSheet.getWindow() != null) {
+        boolean isNew = false;
+
+        if (bottomSheet == null) {
+            bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create();
+            isNew = true;
+        }
+
+        if (bottomSheet.getWindow() != null && isNew) {
             bottomSheet.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
         }
 
@@ -465,4 +508,9 @@ public class CallsListController extends BaseController implements SearchView.On
     protected String getTitle() {
         return getResources().getString(R.string.nc_app_name);
     }
+
+    @Override
+    public void onFastScrollerStateChange(boolean scrolling) {
+        swipeRefreshLayout.setEnabled(!scrolling);
+    }
 }

+ 230 - 102
app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java

@@ -31,12 +31,10 @@ import android.support.design.widget.BottomNavigationView;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.widget.DividerItemDecoration;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.SearchView;
 import android.text.InputType;
 import android.text.TextUtils;
-import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -45,6 +43,8 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.LinearLayout;
 
 import com.bluelinelabs.conductor.RouterTransaction;
 import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
@@ -52,6 +52,8 @@ import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
 import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.activities.CallActivity;
+import com.nextcloud.talk.adapters.items.EmptyFooterItem;
+import com.nextcloud.talk.adapters.items.UserHeaderItem;
 import com.nextcloud.talk.adapters.items.UserItem;
 import com.nextcloud.talk.api.NcApi;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
@@ -61,6 +63,7 @@ import com.nextcloud.talk.models.database.UserEntity;
 import com.nextcloud.talk.models.json.participants.Participant;
 import com.nextcloud.talk.models.json.rooms.RoomOverall;
 import com.nextcloud.talk.models.json.sharees.Sharee;
+import com.nextcloud.talk.models.json.sharees.ShareesOverall;
 import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.database.user.UserUtils;
@@ -69,6 +72,7 @@ import org.parceler.Parcels;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -77,9 +81,15 @@ import javax.inject.Inject;
 
 import autodagger.AutoInjector;
 import butterknife.BindView;
+import butterknife.OnClick;
+import butterknife.Optional;
+import eu.davidea.fastscroller.FastScroller;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.SelectableAdapter;
 import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+import eu.davidea.flexibleadapter.items.IFlexible;
+import eu.davidea.flipview.FlipView;
 import io.reactivex.Observer;
 import io.reactivex.android.schedulers.AndroidSchedulers;
 import io.reactivex.disposables.Disposable;
@@ -88,7 +98,7 @@ import retrofit2.HttpException;
 
 @AutoInjector(NextcloudTalkApplication.class)
 public class ContactsController extends BaseController implements SearchView.OnQueryTextListener,
-        ActionMode.Callback, FlexibleAdapter.OnItemClickListener {
+        FlexibleAdapter.OnItemClickListener, FastScroller.OnScrollStateChangeListener {
 
     public static final String TAG = "ContactsController";
 
@@ -105,33 +115,67 @@ public class ContactsController extends BaseController implements SearchView.OnQ
     @BindView(R.id.swipe_refresh_layout)
     SwipeRefreshLayout swipeRefreshLayout;
 
+    @Nullable
+    @BindView(R.id.bottom_buttons_layout)
+    LinearLayout bottomButtonsLinearLayout;
+
+    @BindView(R.id.fast_scroller)
+    FastScroller fastScroller;
+
+    @Nullable
+    @BindView(R.id.clear_button)
+    Button clearButton;
+
     private UserEntity userEntity;
     private Disposable contactsQueryDisposable;
     private Disposable cacheQueryDisposable;
-    private FlexibleAdapter<UserItem> adapter;
-    private List<UserItem> contactItems = new ArrayList<>();
+    private FlexibleAdapter adapter;
+    private List<AbstractFlexibleItem> contactItems = new ArrayList<>();
+
+    private SmoothScrollLinearLayoutManager layoutManager;
 
     private MenuItem searchItem;
     private SearchView searchView;
     private String searchQuery;
 
-    private ActionMode actionMode;
+    private boolean isNewConversationView;
+
+    private HashMap<String, UserHeaderItem> userHeaderItems = new HashMap<String, UserHeaderItem>();
 
     public ContactsController() {
         super();
         setHasOptionsMenu(true);
     }
 
+    public ContactsController(Bundle args) {
+        super(args);
+        setHasOptionsMenu(true);
+        if (args.containsKey(BundleKeys.KEY_NEW_CONVERSATION)) {
+            isNewConversationView = true;
+        }
+    }
+
     @Override
     protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
         return inflater.inflate(R.layout.controller_generic_rv, container, false);
     }
 
+    @Override
+    protected void onAttach(@NonNull View view) {
+        super.onAttach(view);
+        if (getActionBar() != null && isNewConversationView) {
+            getActionBar().setDisplayHomeAsUpEnabled(true);
+        }
+    }
+
     @Override
     protected void onViewBound(@NonNull View view) {
         super.onViewBound(view);
         NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
 
+        FlipView.resetLayoutAnimationDelay(true, 1000L);
+        FlipView.stopLayoutAnimation();
+
         userEntity = userUtils.getCurrentUser();
 
         if (userEntity == null) {
@@ -144,15 +188,47 @@ public class ContactsController extends BaseController implements SearchView.OnQ
 
         if (adapter == null) {
             adapter = new FlexibleAdapter<>(contactItems, getActivity(), false);
+            adapter.setNotifyChangeOfUnfilteredItems(true)
+                    .setMode(SelectableAdapter.Mode.MULTI);
+
+
             if (userEntity != null) {
                 fetchData();
             }
         }
 
+        adapter.setStickyHeaderElevation(5)
+                .setUnlinkAllItemsOnRemoveHeaders(true)
+                .setDisplayHeadersAtStartUp(true)
+                .setStickyHeaders(true);
+
         adapter.addListener(this);
         prepareViews();
     }
 
+    @Optional
+    @OnClick(R.id.clear_button)
+    public void onClearButtonClick() {
+        if (adapter != null) {
+            List<Integer> selectedPositions = adapter.getSelectedPositions();
+            for (Integer position : selectedPositions) {
+                UserItem userItem = (UserItem) adapter.getItem(position);
+                adapter.toggleSelection(position);
+                if (userItem != null) {
+                    userItem.flipItemSelection();
+                }
+            }
+        }
+
+        checkAndHandleBottomButtons();
+    }
+
+    @Optional
+    @OnClick(R.id.done_button)
+    public void onDoneButtonClick() {
+
+    }
+
     private void initSearchView() {
         if (getActivity() != null) {
             SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
@@ -172,10 +248,14 @@ public class ContactsController extends BaseController implements SearchView.OnQ
         final View mSearchEditFrame = searchView
                 .findViewById(android.support.v7.appcompat.R.id.search_edit_frame);
 
-        BottomNavigationView bottomNavigationView = getParentController().getView().findViewById(R.id.navigation);
+        BottomNavigationView bottomNavigationView = null;
+        if (getParentController() != null && getParentController().getView() != null) {
+            bottomNavigationView = getParentController().getView().findViewById(R.id.navigation);
+        }
 
         Handler handler = new Handler();
         ViewTreeObserver vto = mSearchEditFrame.getViewTreeObserver();
+        BottomNavigationView finalBottomNavigationView = bottomNavigationView;
         vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
             int oldVisibility = -1;
 
@@ -186,10 +266,14 @@ public class ContactsController extends BaseController implements SearchView.OnQ
 
                 if (currentVisibility != oldVisibility) {
                     if (currentVisibility == View.VISIBLE) {
-                        handler.postDelayed(() -> bottomNavigationView.setVisibility(View.GONE), 100);
+                        if (finalBottomNavigationView != null) {
+                            handler.postDelayed(() -> finalBottomNavigationView.setVisibility(View.GONE), 100);
+                        }
                     } else {
                         handler.postDelayed(() -> {
-                            bottomNavigationView.setVisibility(View.VISIBLE);
+                            if (finalBottomNavigationView != null) {
+                                finalBottomNavigationView.setVisibility(View.VISIBLE);
+                            }
                             searchItem.setVisible(contactItems.size() > 0);
                         }, 500);
                     }
@@ -202,11 +286,23 @@ public class ContactsController extends BaseController implements SearchView.OnQ
 
     }
 
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                getRouter().popCurrentController();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
-        inflater.inflate(R.menu.menu_filter, menu);
+        inflater.inflate(R.menu.menu_conversation_plus_filter, menu);
         searchItem = menu.findItem(R.id.action_search);
+        menu.findItem(R.id.action_new_conversation).setVisible(false);
         initSearchView();
     }
 
@@ -218,6 +314,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
             searchItem.expandActionView();
             searchView.setQuery(adapter.getSearchText(), false);
         }
+
     }
 
     private void fetchData() {
@@ -226,6 +323,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
         Set<Sharee> shareeHashSet = new HashSet<>();
 
         contactItems = new ArrayList<>();
+        userHeaderItems = new HashMap<>();
 
         RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(userEntity.getBaseUrl(),
                 "");
@@ -234,7 +332,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
                 retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
                 .subscribeOn(Schedulers.newThread())
                 .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(shareesOverall -> {
+                .subscribe((ShareesOverall shareesOverall) -> {
                             if (shareesOverall != null) {
 
                                 if (shareesOverall.getOcs().getData().getUsers() != null) {
@@ -252,18 +350,52 @@ public class ContactsController extends BaseController implements SearchView.OnQ
                                     if (!sharee.getValue().getShareWith().equals(userEntity.getUsername())) {
                                         participant = new Participant();
                                         participant.setName(sharee.getLabel());
+                                        String headerTitle;
+
+                                        headerTitle = sharee.getLabel().substring(0, 1).toUpperCase();
+
+                                        UserHeaderItem userHeaderItem;
+                                        if (!userHeaderItems.containsKey(headerTitle)) {
+                                            userHeaderItem = new UserHeaderItem(headerTitle);
+                                            userHeaderItems.put(headerTitle, userHeaderItem);
+                                        }
+
                                         participant.setUserId(sharee.getValue().getShareWith());
-                                        contactItems.add(new UserItem(participant, userEntity));
+                                        contactItems.add(new UserItem(participant, userEntity,
+                                                userHeaderItems.get(headerTitle)));
                                     }
 
                                 }
 
-                                Collections.sort(contactItems, (userItem, t1) ->
-                                        userItem.getModel().getName().compareToIgnoreCase(t1.getModel().getName()));
+
+                                userHeaderItems = new HashMap<>();
+
+                                Collections.sort(contactItems, (o1, o2) -> {
+                                    String firstName;
+                                    String secondName;
+
+                                    if (o1 instanceof UserItem) {
+                                        firstName = ((UserItem) o1).getModel().getName();
+                                    } else {
+                                        firstName = ((UserHeaderItem) o1).getModel();
+                                    }
+
+                                    if (o2 instanceof UserItem) {
+                                        secondName = ((UserItem) o2).getModel().getName();
+                                    } else {
+                                        secondName = ((UserHeaderItem) o2).getModel();
+                                    }
+
+                                    return firstName.compareToIgnoreCase(secondName);
+                                });
 
                                 adapter.updateDataSet(contactItems, true);
                                 searchItem.setVisible(contactItems.size() > 0);
                                 swipeRefreshLayout.setRefreshing(false);
+
+                                if (isNewConversationView) {
+                                    checkAndHandleBottomButtons();
+                                }
                             }
 
                         }, throwable -> {
@@ -299,7 +431,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
     }
 
     private void prepareViews() {
-        LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
+        layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
         recyclerView.setLayoutManager(layoutManager);
         recyclerView.setHasFixedSize(true);
         recyclerView.setAdapter(adapter);
@@ -311,6 +443,17 @@ public class ContactsController extends BaseController implements SearchView.OnQ
 
         swipeRefreshLayout.setOnRefreshListener(this::fetchData);
         swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
+
+        fastScroller.addOnScrollStateChangeListener(this);
+        adapter.setFastScroller(fastScroller);
+        fastScroller.setBubbleTextCreator(position -> {
+            IFlexible abstractFlexibleItem = adapter.getItem(position);
+            if (abstractFlexibleItem instanceof UserItem) {
+                return ((UserItem)adapter.getItem(position)).getHeader().getModel();
+            } else {
+                return ((UserHeaderItem)adapter.getItem(position)).getModel();
+            }
+        });
     }
 
     private void dispose(@Nullable Disposable disposable) {
@@ -330,8 +473,10 @@ public class ContactsController extends BaseController implements SearchView.OnQ
         }
     }
 
+
     @Override
     public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
+        adapter.onSaveInstanceState(outState);
         super.onSaveViewState(view, outState);
         if (searchView != null && !TextUtils.isEmpty(searchView.getQuery())) {
             outState.putString(KEY_SEARCH_QUERY, searchView.getQuery().toString());
@@ -342,6 +487,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
     public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
         super.onRestoreViewState(view, savedViewState);
         searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, "");
+        if (adapter != null) {
+            adapter.onRestoreInstanceState(savedViewState);
+        }
     }
 
     @Override
@@ -377,110 +525,90 @@ public class ContactsController extends BaseController implements SearchView.OnQ
     }
 
     @Override
-    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
-        adapter.setMode(SelectableAdapter.Mode.MULTI);
-        return true;
-    }
+    public boolean onItemClick(int position) {
+        if (adapter.getItem(position) instanceof UserItem) {
+            if (!isNewConversationView) {
+                UserItem userItem = (UserItem) adapter.getItem(position);
+                RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1",
+                        userItem.getModel().getUserId());
+                ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()),
+                        retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
+                        .subscribeOn(Schedulers.newThread())
+                        .observeOn(AndroidSchedulers.mainThread())
+                        .subscribe(new Observer<RoomOverall>() {
+                            @Override
+                            public void onSubscribe(Disposable d) {
 
-    @Override
-    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
-        return false;
-    }
+                            }
 
-    @Override
-    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
-        return false;
-    }
+                            @Override
+                            public void onNext(RoomOverall roomOverall) {
+                                overridePushHandler(new NoOpControllerChangeHandler());
+                                overridePopHandler(new NoOpControllerChangeHandler());
+                                Intent callIntent = new Intent(getActivity(), CallActivity.class);
+                                Bundle bundle = new Bundle();
+                                bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken());
+                                bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity));
+                                callIntent.putExtras(bundle);
+                                startActivity(callIntent);
+                            }
 
-    @Override
-    public void onDestroyActionMode(ActionMode actionMode) {
-        adapter.setMode(SelectableAdapter.Mode.IDLE);
-        actionMode = null;
-    }
+                            @Override
+                            public void onError(Throwable e) {
 
-    /*@Override
-    public boolean onItemClick(int position) {
-        if (actionMode != null && position != RecyclerView.NO_POSITION) {
-            // Mark the position selected
-            toggleSelection(position);
-            return true;
-        } else {
-            // Handle the item click listener
-            // We don't need to activate anything
-            return false;
-        }
-    }*/
+                            }
 
-    private void toggleSelection(int position) {
-        adapter.toggleSelection(position);
+                            @Override
+                            public void onComplete() {
 
-        int count = adapter.getSelectedItemCount();
+                            }
+                        });
+            } else {
+                ((UserItem) adapter.getItem(position)).flipItemSelection();
+                adapter.toggleSelection(position);
 
-        if (count == 0) {
-            actionMode.finish();
-        } else {
-            //setContextTitle(count);
+                checkAndHandleBottomButtons();
+            }
         }
+        return true;
     }
 
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        adapter.onSaveInstanceState(outState);
-        super.onSaveInstanceState(outState);
-    }
+    private void checkAndHandleBottomButtons() {
+        if (adapter != null && bottomButtonsLinearLayout != null && clearButton != null) {
+            if (adapter.getSelectedItemCount() > 0) {
+                if (bottomButtonsLinearLayout.getVisibility() != View.VISIBLE) {
+                    bottomButtonsLinearLayout.setVisibility(View.VISIBLE);
+                }
+            } else {
+                bottomButtonsLinearLayout.setVisibility(View.GONE);
+            }
+        } else if (bottomButtonsLinearLayout != null) {
+            bottomButtonsLinearLayout.setVisibility(View.GONE);
+        }
 
-    @Override
-    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
-        super.onRestoreInstanceState(savedInstanceState);
-        if (adapter != null) {
-            adapter.onRestoreInstanceState(savedInstanceState);
+        if (bottomButtonsLinearLayout != null && bottomButtonsLinearLayout.getVisibility() == View.VISIBLE) {
+            if (adapter.getScrollableFooters().size() == 0) {
+                adapter.addScrollableFooterWithDelay(new EmptyFooterItem(999), 0, layoutManager
+                        .findLastVisibleItemPosition() == adapter.getItemCount() - 1);
+            }
+        } else {
+            if (adapter != null) {
+                adapter.removeAllScrollableFooters();
+            }
         }
     }
 
     @Override
-    public boolean onItemClick(int position) {
-        UserItem userItem = adapter.getItem(position);
-        RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1",
-                userItem.getModel().getUserId());
-        ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()),
-                retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
-                .subscribeOn(Schedulers.newThread())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(new Observer<RoomOverall>() {
-                    @Override
-                    public void onSubscribe(Disposable d) {
-
-                    }
-
-                    @Override
-                    public void onNext(RoomOverall roomOverall) {
-                        overridePushHandler(new NoOpControllerChangeHandler());
-                        overridePopHandler(new NoOpControllerChangeHandler());
-                        Intent callIntent = new Intent(getActivity(), CallActivity.class);
-                        Bundle bundle = new Bundle();
-                        bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken());
-                        bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity));
-                        callIntent.putExtras(bundle);
-                        startActivity(callIntent);
-                    }
-
-                    @Override
-                    public void onError(Throwable e) {
-
-                    }
-
-                    @Override
-                    public void onComplete() {
-
-                    }
-                });
-
-        return true;
+    protected String getTitle() {
+        if (!isNewConversationView) {
+            return getResources().getString(R.string.nc_app_name);
+        } else {
+            return getResources().getString(R.string.nc_select_contacts);
+        }
     }
 
     @Override
-    protected String getTitle() {
-        return getResources().getString(R.string.nc_app_name);
+    public void onFastScrollerStateChange(boolean scrolling) {
+        swipeRefreshLayout.setEnabled(!scrolling);
     }
-
 }

+ 1 - 1
app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java

@@ -39,10 +39,10 @@ import com.bluelinelabs.conductor.RouterTransaction;
 import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.api.NcApi;
-import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.controllers.base.BaseController;
 import com.nextcloud.talk.utils.AccountUtils;
+import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.ApplicationWideMessageHolder;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.database.user.UserUtils;

+ 22 - 0
app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java

@@ -31,6 +31,7 @@ import android.support.v7.widget.DividerItemDecoration;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -129,16 +130,29 @@ public class SwitchAccountController extends BaseController {
             };
 
     public SwitchAccountController() {
+        setHasOptionsMenu(true);
     }
 
     public SwitchAccountController(Bundle args) {
         super(args);
+        setHasOptionsMenu(true);
 
         if (args.containsKey(BundleKeys.KEY_IS_ACCOUNT_IMPORT)) {
             isAccountImport = true;
         }
     }
 
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                getRouter().popCurrentController();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
     @Override
     protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
         return inflater.inflate(R.layout.controller_generic_rv, container, false);
@@ -204,6 +218,14 @@ public class SwitchAccountController extends BaseController {
         prepareViews();
     }
 
+    @Override
+    protected void onAttach(@NonNull View view) {
+        super.onAttach(view);
+        if (getActionBar() != null) {
+            getActionBar().setDisplayHomeAsUpEnabled(true);
+        }
+    }
+
     private void prepareViews() {
         LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
         recyclerView.setLayoutManager(layoutManager);

+ 1 - 0
app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java

@@ -53,6 +53,7 @@ public abstract class BaseController extends RefWatchingController {
     @Override
     protected void onAttach(@NonNull View view) {
         setTitle();
+        getActionBar().setDisplayHomeAsUpEnabled(false);
         super.onAttach(view);
     }
 

+ 1 - 1
app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java

@@ -36,10 +36,10 @@ import com.kennyc.bottomsheet.adapters.AppAdapter;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.adapters.items.AppItem;
 import com.nextcloud.talk.adapters.items.MenuItem;
-import com.nextcloud.talk.models.json.rooms.Room;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.controllers.base.BaseController;
 import com.nextcloud.talk.events.BottomSheetLockEvent;
+import com.nextcloud.talk.models.json.rooms.Room;
 import com.nextcloud.talk.utils.ShareUtils;
 import com.nextcloud.talk.utils.bundle.BundleKeys;
 import com.nextcloud.talk.utils.database.user.UserUtils;

+ 1 - 1
app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java

@@ -28,8 +28,8 @@ import android.util.Log;
 import com.github.aurae.retrofit2.LoganSquareConverterFactory;
 import com.nextcloud.talk.BuildConfig;
 import com.nextcloud.talk.api.NcApi;
-import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.utils.ApiUtils;
 import com.nextcloud.talk.utils.preferences.AppPreferences;
 import com.nextcloud.talk.utils.ssl.MagicTrustManager;
 import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat;

+ 1 - 1
app/src/main/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.java

@@ -40,9 +40,9 @@ import com.google.firebase.messaging.FirebaseMessagingService;
 import com.google.firebase.messaging.RemoteMessage;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.activities.CallActivity;
+import com.nextcloud.talk.models.SignatureVerification;
 import com.nextcloud.talk.models.json.push.DecryptedPushMessage;
 import com.nextcloud.talk.models.json.push.PushMessage;
-import com.nextcloud.talk.models.SignatureVerification;
 import com.nextcloud.talk.utils.NotificationUtils;
 import com.nextcloud.talk.utils.PushUtils;
 import com.nextcloud.talk.utils.bundle.BundleKeys;

+ 45 - 0
app/src/main/java/com/nextcloud/talk/utils/MagicFlipView.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.utils;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import eu.davidea.flipview.FlipView;
+
+public class MagicFlipView extends FlipView {
+    public MagicFlipView(Context context) {
+        super(context);
+    }
+
+    public MagicFlipView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        try {
+            super.onDetachedFromWindow();
+        } catch (IllegalArgumentException e) {
+            stopFlipping();
+        }
+    }
+}

+ 1 - 0
app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.java

@@ -36,4 +36,5 @@ public class BundleKeys {
     public static final String KEY_CALL_SESSION = "KEY_CALL_SESSION";
     public static final String KEY_ROOM_TOKEN = "KEY_ROOM_TOKEN";
     public static final String KEY_USER_ENTITY = "KEY_USER_ENTITY";
+    public static final String KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION";
 }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java

@@ -26,12 +26,12 @@ import android.util.Log;
 
 import com.bluelinelabs.logansquare.LoganSquare;
 import com.nextcloud.talk.R;
-import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
-import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.events.MediaStreamEvent;
 import com.nextcloud.talk.events.PeerConnectionEvent;
 import com.nextcloud.talk.events.SessionDescriptionSendEvent;
+import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
+import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
 
 import org.greenrobot.eventbus.EventBus;
 import org.webrtc.DataChannel;

+ 1 - 1
app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java

@@ -17,7 +17,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- * Original code:
+ * Part of the code related to codec handling is originally:
  *
  *
  * Copyright 2016 The WebRTC Project Authors. All rights reserved.

+ 25 - 0
app/src/main/res/drawable/ic_add_white_24px.xml

@@ -0,0 +1,25 @@
+<!--
+  ~ 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/>.
+  -->
+
+<vector android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>

+ 52 - 0
app/src/main/res/layout/bottom_buttons.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/bottom_buttons_layout"
+              android:layout_width="match_parent"
+              android:layout_height="32dp"
+              android:alpha="0.7"
+              android:animateLayoutChanges="true"
+              android:gravity="bottom"
+              android:orientation="horizontal"
+              android:visibility="gone">
+
+    <Button
+        android:id="@+id/clear_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0.6"
+        android:background="@color/nc_darkRed"
+        android:text="@string/nc_contacts_clear"
+        android:textAlignment="center"
+        android:textColor="@color/nc_white_color_complete"/>
+
+    <Button
+        android:id="@+id/done_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0.4"
+        android:background="@color/nc_darkGreen"
+        android:text="@string/nc_contacts_done"
+        android:textAlignment="center"
+        android:textColor="@color/nc_white_color_complete"/>
+
+</LinearLayout>

+ 4 - 4
app/src/main/res/layout/controller_call_menu.xml

@@ -20,10 +20,10 @@
   -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                                              xmlns:tools="http://schemas.android.com/tools"
-                                              android:layout_width="match_parent"
-                                              android:layout_height="wrap_content"
-                                              android:background="@color/nc_white_color">
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@color/nc_white_color">
 
     <android.support.v7.widget.RecyclerView
         android:id="@+id/recycler_view"

+ 1 - 1
app/src/main/res/layout/controller_entry_menu.xml

@@ -57,9 +57,9 @@
         android:layout_marginBottom="12dp"
         android:layout_marginEnd="8dp"
         android:layout_marginTop="8dp"
+        android:alpha="0.7"
         android:background="#0000"
         android:enabled="false"
-        android:alpha="0.7"
         android:text="@string/nc_proceed"
         android:textColor="@color/colorPrimary"/>
 

+ 32 - 12
app/src/main/res/layout/controller_generic_rv.xml

@@ -19,17 +19,37 @@
   ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 
-<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                                              xmlns:tools="http://schemas.android.com/tools"
-                                              android:id="@+id/swipe_refresh_layout"
-                                              android:layout_width="match_parent"
-                                              android:layout_height="match_parent"
-                                              android:background="@color/nc_white_color">
-
-    <android.support.v7.widget.RecyclerView
-        android:id="@+id/recycler_view"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:background="@color/nc_white_color">
+
+    <android.support.v4.widget.SwipeRefreshLayout
+        android:id="@+id/swipe_refresh_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <android.support.v7.widget.RecyclerView
+                android:id="@+id/recycler_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                tools:listitem="@layout/rv_item_call"/>
+
+        </FrameLayout>
+
+    </android.support.v4.widget.SwipeRefreshLayout>
+
+    <include
+        layout="@layout/bottom_buttons"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        tools:listitem="@layout/rv_item_call"/>
+        android:layout_height="32dp"
+        android:layout_gravity="bottom"
+        android:visibility="gone"/>
 
-</android.support.v4.widget.SwipeRefreshLayout>
+    <include layout="@layout/fast_scroller"/>
+</FrameLayout>

+ 38 - 0
app/src/main/res/layout/fast_scroller.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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/>.
+  -->
+
+<eu.davidea.fastscroller.FastScroller 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:id="@+id/fast_scroller"
+                                      android:layout_width="wrap_content"
+                                      android:layout_height="match_parent"
+                                      android:layout_alignBottom="@+id/swipe_refresh_layout"
+                                      android:layout_alignParentEnd="true"
+                                      android:layout_alignTop="@+id/swipe_refresh_layout"
+                                      android:layout_centerHorizontal="true"
+                                      app:fastScrollerAutoHideDelayInMillis="1000"
+                                      app:fastScrollerAutoHideEnabled="true"
+                                      app:fastScrollerBubbleEnabled="true"
+                                      app:fastScrollerBubblePosition="adjacent"
+                                      app:fastScrollerIgnoreTouchesOutsideHandle="false"
+                                      tools:visibility="visible">
+</eu.davidea.fastscroller.FastScroller>

+ 13 - 4
app/src/main/res/layout/rv_item_call.xml

@@ -34,10 +34,15 @@
         android:layout_centerVertical="true"
         android:layout_marginStart="@dimen/activity_horizontal_margin">
 
-        <ImageView
+        <com.nextcloud.talk.utils.MagicFlipView
+            xmlns:app="http://schemas.android.com/apk/res-auto"
             android:id="@+id/avatar_image"
             android:layout_width="@dimen/avatar_size"
-            android:layout_height="@dimen/avatar_size"/>
+            android:layout_height="@dimen/avatar_size"
+            app:animationDuration="170"
+            app:checked="false"
+            app:enableInitialAnimation="true"
+            app:rearBackgroundColor="@color/colorPrimary"/>
 
         <ImageView
             android:id="@+id/password_protected_image_view"
@@ -83,11 +88,15 @@
     <ImageButton
         android:id="@+id/more_menu"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
         android:layout_alignParentEnd="true"
         android:layout_centerVertical="true"
         android:layout_marginEnd="@dimen/activity_horizontal_margin"
         android:layout_marginStart="@dimen/margin_between_elements"
-        android:background="@drawable/ic_more_horiz_black_24dp"/>
+        android:background="?android:attr/selectableItemBackground"
+        android:paddingEnd="8dp"
+        android:paddingStart="8dp"
+        android:scaleType="center"
+        android:src="@drawable/ic_more_horiz_black_24dp"/>
 
 </RelativeLayout>

+ 9 - 5
app/src/main/res/layout/rv_item_contact.xml

@@ -28,23 +28,27 @@
                 android:layout_height="@dimen/item_height"
                 android:orientation="vertical">
 
-    <ImageView
-        android:id="@+id/avatar_image"
+    <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"
         android:layout_centerVertical="true"
         android:layout_marginEnd="@dimen/activity_horizontal_margin"
-        android:layout_marginStart="@dimen/activity_horizontal_margin"/>
+        android:layout_marginStart="@dimen/activity_horizontal_margin"
+        app:animationDuration="170"
+        app:enableInitialAnimation="true"
+        app:rearBackgroundColor="@color/colorPrimary"/>
 
     <TextView
         android:id="@+id/name_text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_centerVertical="true"
-        android:ellipsize="end"
         android:layout_marginEnd="@dimen/activity_horizontal_margin"
+        android:layout_toEndOf="@id/avatar_flip_view"
+        android:ellipsize="end"
         android:textAppearance="?android:attr/textAppearanceListItem"
-        android:layout_toEndOf="@id/avatar_image"
         tools:text="Contact item text"/>
 
 </RelativeLayout>

+ 27 - 0
app/src/main/res/layout/rv_item_empty_footer.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="32dp"
+              android:orientation="vertical">
+
+</LinearLayout>

+ 39 - 0
app/src/main/res/layout/rv_item_title_header.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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/>.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:alpha="0.7"
+                android:background="@color/colorPrimary"
+                android:elevation="5dp">
+
+    <TextView
+        android:id="@+id/title_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="8dp"
+        android:layout_marginEnd="24dp"
+        android:layout_marginStart="12dp"
+        android:layout_marginTop="8dp"
+        android:textColor="@color/nc_white_color_complete"/>
+
+</RelativeLayout>

+ 3 - 3
app/src/main/res/layout/surface_renderer.xml

@@ -36,8 +36,8 @@
         android:layout_width="16dp"
         android:layout_height="16dp"
         android:layout_above="@id/peer_nick_text_view"
-        android:layout_marginStart="8dp"
         android:layout_marginEnd="8dp"
+        android:layout_marginStart="8dp"
         android:src="@drawable/ic_videocam_off_white_24px"
         android:visibility="invisible"/>
 
@@ -56,9 +56,9 @@
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
         android:layout_alignParentStart="true"
-        android:layout_marginTop="4dp"
-        android:layout_marginStart="8dp"
         android:layout_marginBottom="8dp"
+        android:layout_marginStart="8dp"
+        android:layout_marginTop="4dp"
         android:textColor="@color/nc_white_color_complete"/>
 
 </RelativeLayout>

+ 7 - 2
app/src/main/res/menu/menu_filter.xml → app/src/main/res/menu/menu_conversation_plus_filter.xml

@@ -28,6 +28,11 @@
 	      android:icon="@drawable/ic_search_white_24dp"
 	      app:showAsAction="collapseActionView|always"
 	      android:animateLayoutChanges="true"
-	      app:actionViewClass="android.support.v7.widget.SearchView"/>
+	      app:actionViewClass="android.support.v7.widget.SearchView"
+        />
 
-</menu>
+    <item android:id="@+id/action_new_conversation"
+          android:title="@string/nc_new_conversation"
+          android:icon="@drawable/ic_add_white_24px"
+          app:showAsAction="ifRoom"/>
+</menu>

+ 4 - 0
app/src/main/res/values/strings.xml

@@ -28,6 +28,7 @@
 
     <string name="nc_never">Never joined</string>
     <string name="nc_search">Search</string>
+    <string name="nc_new_conversation">Create a new conversation</string>
 
     <string name="nc_certificate_dialog_title">Check out the certificate</string>
     <string name="nc_certificate_dialog_text">Do you trust the until now unknown SSL certificate, issued by %1$s for %2$s, valid from %3$s to %4$s?</string>
@@ -84,8 +85,11 @@
     <string name="nc_delete_call">Delete call</string>
 
     <!-- Contacts -->
+    <string name="nc_select_contacts">Select contacts</string>
     <string name="nc_one_contact_selected">contact selected</string>
     <string name="nc_more_contacts_selected">contacts selected</string>
+    <string name="nc_contacts_clear">Clear</string>
+    <string name="nc_contacts_done">Done</string>
 
     <!-- Permissions -->
     <string name="nc_permissions">Permissions need to be granted to establish a video and/or audio call. Please click \"ALLOW\" in the upcoming system dialog.</string>