Bladeren bron

Work on menu & few other things

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 7 jaren geleden
bovenliggende
commit
1dde5029f4
22 gewijzigde bestanden met toevoegingen van 635 en 96 verwijderingen
  1. 6 4
      app/build.gradle
  2. 0 1
      app/src/main/java/com/nextcloud/talk/activities/CallActivity.java
  3. 12 4
      app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java
  4. 90 0
      app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java
  5. 13 13
      app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java
  6. 0 40
      app/src/main/java/com/nextcloud/talk/api/models/User.java
  7. 65 0
      app/src/main/java/com/nextcloud/talk/api/models/json/converters/EnumParticipantTypeConverter.java
  8. 15 0
      app/src/main/java/com/nextcloud/talk/api/models/json/participants/Participant.java
  9. 24 3
      app/src/main/java/com/nextcloud/talk/api/models/json/rooms/Room.java
  10. 61 15
      app/src/main/java/com/nextcloud/talk/controllers/CallsListController.java
  11. 8 6
      app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java
  12. 121 0
      app/src/main/java/com/nextcloud/talk/controllers/RoomMenuController.java
  13. 6 6
      app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java
  14. 29 0
      app/src/main/java/com/nextcloud/talk/events/MenuItemClickEvent.java
  15. 34 0
      app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java
  16. 25 0
      app/src/main/res/drawable/ic_more_horiz_black_24dp.xml
  17. 31 0
      app/src/main/res/layout/bottom_sheet.xml
  18. 35 0
      app/src/main/res/layout/controller_room_menu.xml
  19. 14 3
      app/src/main/res/layout/rv_item_call.xml
  20. 33 0
      app/src/main/res/layout/rv_item_menu.xml
  21. 12 0
      app/src/main/res/values/strings.xml
  22. 1 1
      build.gradle

+ 6 - 4
app/build.gradle

@@ -11,13 +11,13 @@ versioning {
 version = versioning.name
 
 android {
-    compileSdkVersion 26
-    buildToolsVersion '26.0.2'
+    compileSdkVersion 27
+    buildToolsVersion '27.0.0'
     defaultConfig {
         applicationId "com.nextcloud.talk"
         versionName version
         minSdkVersion 21
-        targetSdkVersion 26
+        targetSdkVersion 27
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 
         // Enabling multidex support.
@@ -52,7 +52,7 @@ android {
 }
 
 ext {
-    supportLibraryVersion = '26.1.0'
+    supportLibraryVersion = '27.0.0'
     googleLibraryVersion = '11.6.0'
 }
 
@@ -141,6 +141,8 @@ dependencies {
     implementation 'ru.alexbykov:nopermission:1.1.1'
     implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
 
+    implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'
+
     testImplementation 'junit:junit:4.12'
     androidTestImplementation ('com.android.support.test.espresso:espresso-core:3.0.1', {
         exclude group: 'com.android.support', module: 'support-annotations'

+ 0 - 1
app/src/main/java/com/nextcloud/talk/activities/CallActivity.java

@@ -764,5 +764,4 @@ public class CallActivity extends AppCompatActivity {
                     }
                 });
     }
-
 }

+ 12 - 4
app/src/main/java/com/nextcloud/talk/adapters/items/RoomItem.java → app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java

@@ -23,6 +23,7 @@ package com.nextcloud.talk.adapters.items;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.view.View;
+import android.widget.ImageButton;
 import android.widget.TextView;
 
 import com.bumptech.glide.load.engine.DiskCacheStrategy;
@@ -32,10 +33,13 @@ import com.nextcloud.talk.R;
 import com.nextcloud.talk.api.helpers.api.ApiHelper;
 import com.nextcloud.talk.api.models.json.rooms.Room;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.events.MoreMenuClickEvent;
 import com.nextcloud.talk.persistence.entities.UserEntity;
 import com.nextcloud.talk.utils.ColorUtils;
 import com.nextcloud.talk.utils.glide.GlideApp;
 
+import org.greenrobot.eventbus.EventBus;
+
 import java.util.List;
 
 import butterknife.BindView;
@@ -47,20 +51,20 @@ import eu.davidea.flexibleadapter.items.IFilterable;
 import eu.davidea.flexibleadapter.utils.FlexibleUtils;
 import eu.davidea.viewholders.FlexibleViewHolder;
 
-public class RoomItem extends AbstractFlexibleItem<RoomItem.RoomItemViewHolder> implements IFilterable {
+public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder> implements IFilterable {
 
     private Room room;
     private UserEntity userEntity;
 
-    public RoomItem(Room room, UserEntity userEntity) {
+    public CallItem(Room room, UserEntity userEntity) {
         this.room = room;
         this.userEntity = userEntity;
     }
 
     @Override
     public boolean equals(Object o) {
-        if (o instanceof RoomItem) {
-            RoomItem inItem = (RoomItem) o;
+        if (o instanceof CallItem) {
+            CallItem inItem = (CallItem) o;
             return room.equals(inItem.getModel());
         }
         return false;
@@ -148,6 +152,8 @@ public class RoomItem extends AbstractFlexibleItem<RoomItem.RoomItemViewHolder>
                 holder.avatarImageView.setVisibility(View.GONE);
 
         }
+
+        holder.moreMenuButton.setOnClickListener(view -> EventBus.getDefault().post(new MoreMenuClickEvent(room)));
     }
 
     @Override
@@ -165,6 +171,8 @@ public class RoomItem extends AbstractFlexibleItem<RoomItem.RoomItemViewHolder>
         public TextView roomLastPing;
         @BindView(R.id.avatar_image)
         public AvatarImageView avatarImageView;
+        @BindView(R.id.more_menu)
+        public ImageButton moreMenuButton;
 
         /**
          * Default constructor.

+ 90 - 0
app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java

@@ -0,0 +1,90 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 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 android.widget.TextView;
+
+import com.nextcloud.talk.R;
+import com.nextcloud.talk.events.MenuItemClickEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+import eu.davidea.viewholders.FlexibleViewHolder;
+
+public class MenuItem extends AbstractFlexibleItem<MenuItem.MenuItemViewHolder>  {
+    private String title;
+
+    public MenuItem(String title) {
+        this.title = title;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof MenuItem) {
+            MenuItem inItem = (MenuItem) o;
+            return title.equals(inItem.getModel());
+        }
+        return false;
+    }
+
+    private String getModel() {
+        return title;
+    }
+
+    @Override
+    public int getLayoutRes() {
+        return R.layout.rv_item_menu;
+    }
+
+    @Override
+    public MenuItem.MenuItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
+        return new MenuItemViewHolder(view, adapter);
+    }
+
+    @Override
+    public void bindViewHolder(FlexibleAdapter adapter, MenuItem.MenuItemViewHolder holder, int position, List payloads) {
+        holder.menuTitle.setText(title);
+
+        holder.menuTitle.setOnClickListener(view -> EventBus.getDefault().post(new MenuItemClickEvent(title)));
+    }
+
+    static class MenuItemViewHolder extends FlexibleViewHolder {
+
+        @BindView(R.id.menu_text)
+        public TextView menuTitle;
+
+        /**
+         * Default constructor.
+         */
+        MenuItemViewHolder(View view, FlexibleAdapter adapter) {
+            super(view, adapter);
+            ButterKnife.bind(this, view);
+        }
+    }
+}

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

@@ -28,7 +28,7 @@ import com.bumptech.glide.load.model.GlideUrl;
 import com.bumptech.glide.load.model.LazyHeaders;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.api.helpers.api.ApiHelper;
-import com.nextcloud.talk.api.models.User;
+import com.nextcloud.talk.api.models.json.participants.Participant;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.persistence.entities.UserEntity;
 import com.nextcloud.talk.utils.ColorUtils;
@@ -47,11 +47,11 @@ import eu.davidea.viewholders.FlexibleViewHolder;
 
 public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder> implements IFilterable {
 
-    private User user;
+    private Participant participant;
     private UserEntity userEntity;
 
-    public UserItem(User user, UserEntity userEntity) {
-        this.user = user;
+    public UserItem(Participant participant, UserEntity userEntity) {
+        this.participant = participant;
         this.userEntity = userEntity;
     }
 
@@ -59,22 +59,22 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
     public boolean equals(Object o) {
         if (o instanceof UserItem) {
             UserItem inItem = (UserItem) o;
-            return user.equals(inItem.getModel());
+            return participant.equals(inItem.getModel());
         }
         return false;
     }
 
     @Override
     public int hashCode() {
-        return user.hashCode();
+        return participant.hashCode();
     }
 
     /**
      * @return the model object
      */
 
-    public User getModel() {
-        return user;
+    public Participant getModel() {
+        return participant;
     }
 
     public UserEntity getEntity() {
@@ -94,17 +94,17 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
     @Override
     public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) {
         if (adapter.hasSearchText()) {
-            FlexibleUtils.highlightText(holder.contactDisplayName, user.getName(), adapter.getSearchText());
+            FlexibleUtils.highlightText(holder.contactDisplayName, participant.getName(), adapter.getSearchText());
         } else {
-            holder.contactDisplayName.setText(user.getName());
+            holder.contactDisplayName.setText(participant.getName());
         }
 
         // Awful hack
-        holder.avatarImageView.setTextAndColorSeed(String.valueOf(user.getName().
+        holder.avatarImageView.setTextAndColorSeed(String.valueOf(participant.getName().
                 toUpperCase().charAt(0)), ColorUtils.colorSeed);
 
         GlideUrl glideUrl = new GlideUrl(ApiHelper.getUrlForAvatarWithName(userEntity.getBaseUrl(),
-                user.getUserId()), new LazyHeaders.Builder()
+                participant.getUserId()), new LazyHeaders.Builder()
                 .setHeader("Accept", "image/*")
                 .setHeader("User-Agent", ApiHelper.getUserAgent())
                 .build());
@@ -121,7 +121,7 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
 
     @Override
     public boolean filter(String constraint) {
-        return user.getName() != null && user.getName().toLowerCase().trim().contains(constraint.toLowerCase());
+        return participant.getName() != null && participant.getName().toLowerCase().trim().contains(constraint.toLowerCase());
     }
 
 

+ 0 - 40
app/src/main/java/com/nextcloud/talk/api/models/User.java

@@ -1,40 +0,0 @@
-/*
- *
- *   Nextcloud Talk application
- *
- *   @author Mario Danic
- *   Copyright (C) 2017 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.api.models;
-
-import com.bluelinelabs.logansquare.annotation.JsonField;
-import com.bluelinelabs.logansquare.annotation.JsonObject;
-
-import org.parceler.Parcel;
-
-import lombok.Data;
-
-@Parcel
-@Data
-@JsonObject
-public class User {
-    @JsonField(name = "userId")
-    String userId;
-    @JsonField(name = "type")
-    int type;
-    @JsonField(name = "name")
-    String name;
-}

+ 65 - 0
app/src/main/java/com/nextcloud/talk/api/models/json/converters/EnumParticipantTypeConverter.java

@@ -0,0 +1,65 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 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.api.models.json.converters;
+
+import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter;
+import com.nextcloud.talk.api.models.json.participants.Participant;
+
+public class EnumParticipantTypeConverter extends IntBasedTypeConverter<Participant.ParticipantType> {
+    @Override
+    public Participant.ParticipantType getFromInt(int i) {
+        switch (i) {
+            case 1:
+                return Participant.ParticipantType.OWNER;
+            case 2:
+                return Participant.ParticipantType.MODERATOR;
+            case 3:
+                return Participant.ParticipantType.USER;
+            case 4:
+                return Participant.ParticipantType.GUEST;
+            case 5:
+                return Participant.ParticipantType.USER_FOLLOWING_LINK;
+            default:
+                return Participant.ParticipantType.DUMMY;
+        }
+
+    }
+
+    @Override
+    public int convertToInt(Participant.ParticipantType object) {
+        switch (object) {
+            case DUMMY:
+                return 0;
+            case OWNER:
+                return 1;
+            case MODERATOR:
+                return 2;
+            case USER:
+                return 3;
+            case GUEST:
+                return 4;
+            case USER_FOLLOWING_LINK:
+                return 5;
+            default:
+                return 0;
+        }
+    }
+}

+ 15 - 0
app/src/main/java/com/nextcloud/talk/api/models/json/participants/Participant.java

@@ -34,6 +34,12 @@ public class Participant {
     @JsonField(name = "userId")
     String userId;
 
+    @JsonField(name = "type")
+    ParticipantType type;
+
+    @JsonField(name = "name")
+    String name;
+
     @JsonField(name = "lastPing")
     long lastPing;
 
@@ -45,4 +51,13 @@ public class Participant {
 
     @JsonField(name = "inCall")
     boolean inCall;
+
+    public enum ParticipantType {
+        DUMMY,
+        OWNER,
+        MODERATOR,
+        USER,
+        GUEST,
+        USER_FOLLOWING_LINK
+    }
 }

+ 24 - 3
app/src/main/java/com/nextcloud/talk/api/models/json/rooms/Room.java

@@ -22,8 +22,9 @@ package com.nextcloud.talk.api.models.json.rooms;
 
 import com.bluelinelabs.logansquare.annotation.JsonField;
 import com.bluelinelabs.logansquare.annotation.JsonObject;
-import com.nextcloud.talk.api.models.User;
+import com.nextcloud.talk.api.models.json.converters.EnumParticipantTypeConverter;
 import com.nextcloud.talk.api.models.json.converters.EnumRoomTypeConverter;
+import com.nextcloud.talk.api.models.json.participants.Participant;
 
 import org.parceler.Parcel;
 
@@ -52,9 +53,11 @@ public class Room {
     @JsonField(name = "numGuests")
     public long numberOfGuests;
     @JsonField(name = "guestList")
-    public List<User> guestList;
+    public List<Participant> guestList;
     @JsonField(name = "participants")
-    public List<User> participants;
+    public List<Participant> participants;
+    @JsonField(name = "participantType", typeConverter = EnumParticipantTypeConverter.class)
+    public Participant.ParticipantType participantType;
     @JsonField(name = "hasPassword")
     public boolean hasPassword;
     @JsonField(name = "sessionId")
@@ -66,4 +69,22 @@ public class Room {
         ROOM_GROUP_CALL,
         ROOM_PUBLIC_CALL
     }
+
+    public boolean isPublic() {
+        return (RoomType.ROOM_PUBLIC_CALL.equals(type));
+    }
+
+    public boolean canModerate() {
+        return (Participant.ParticipantType.OWNER.equals(participantType)
+                || Participant.ParticipantType.MODERATOR.equals(participantType));
+    }
+
+    public boolean isNameEditable() {
+        return (canModerate() && !RoomType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type));
+    }
+
+    public boolean isDeletable() {
+        return (canModerate() && ((participants != null && participants.size() > 2) || numberOfGuests > 0));
+    }
+
 }

+ 61 - 15
app/src/main/java/com/nextcloud/talk/controllers/CallsListController.java

@@ -49,17 +49,23 @@ import com.bluelinelabs.conductor.RouterTransaction;
 import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
 import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
 import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
+import com.kennyc.bottomsheet.BottomSheet;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.activities.CallActivity;
-import com.nextcloud.talk.adapters.items.RoomItem;
+import com.nextcloud.talk.adapters.items.CallItem;
 import com.nextcloud.talk.api.NcApi;
 import com.nextcloud.talk.api.helpers.api.ApiHelper;
+import com.nextcloud.talk.api.models.json.rooms.Room;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.controllers.base.BaseController;
+import com.nextcloud.talk.events.MoreMenuClickEvent;
 import com.nextcloud.talk.persistence.entities.UserEntity;
 import com.nextcloud.talk.utils.bundle.BundleBuilder;
 import com.nextcloud.talk.utils.database.user.UserUtils;
 
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
 import org.parceler.Parcels;
 
 import java.util.ArrayList;
@@ -87,6 +93,9 @@ public class CallsListController extends BaseController implements SearchView.On
     @Inject
     UserUtils userUtils;
 
+    @Inject
+    EventBus eventBus;
+
     @Inject
     NcApi ncApi;
     @BindView(R.id.recycler_view)
@@ -97,9 +106,10 @@ public class CallsListController extends BaseController implements SearchView.On
 
     private UserEntity userEntity;
     private Disposable roomsQueryDisposable;
-    private FlexibleAdapter<RoomItem> adapter;
-    private List<RoomItem> roomItems = new ArrayList<>();
+    private FlexibleAdapter<CallItem> adapter;
+    private List<CallItem> callItems = new ArrayList<>();
 
+    private BottomSheet bottomSheet;
     private MenuItem searchItem;
     private SearchView searchView;
     private String searchQuery;
@@ -108,13 +118,13 @@ public class CallsListController extends BaseController implements SearchView.On
             new FlexibleAdapter.OnItemClickListener() {
                 @Override
                 public boolean onItemClick(int position) {
-                    if (roomItems.size() > position) {
+                    if (callItems.size() > position) {
                         overridePushHandler(new NoOpControllerChangeHandler());
                         overridePopHandler(new NoOpControllerChangeHandler());
-                        RoomItem roomItem = roomItems.get(position);
+                        CallItem callItem = callItems.get(position);
                         Intent callIntent = new Intent(getActivity(), CallActivity.class);
                         BundleBuilder bundleBuilder = new BundleBuilder(new Bundle());
-                        bundleBuilder.putString("roomToken", roomItem.getModel().getToken());
+                        bundleBuilder.putString("roomToken", callItem.getModel().getToken());
                         bundleBuilder.putParcelable("userEntity", Parcels.wrap(userEntity));
                         callIntent.putExtras(bundleBuilder.build());
                         startActivity(callIntent);
@@ -142,7 +152,7 @@ public class CallsListController extends BaseController implements SearchView.On
         userEntity = userUtils.getCurrentUser();
 
         if (adapter == null) {
-            adapter = new FlexibleAdapter<>(roomItems, getActivity(), false);
+            adapter = new FlexibleAdapter<>(callItems, getActivity(), false);
             if (userEntity != null) {
                 fetchData();
             }
@@ -160,6 +170,17 @@ public class CallsListController extends BaseController implements SearchView.On
         }
     }
 
+    @Override
+    protected void onAttach(@NonNull View view) {
+        super.onAttach(view);
+        eventBus.register(this);
+    }
+
+    @Override
+    protected void onDetach(@NonNull View view) {
+        super.onDetach(view);
+        eventBus.unregister(this);
+    }
 
     private void initSearchView() {
         if (getActivity() != null) {
@@ -198,7 +219,7 @@ public class CallsListController extends BaseController implements SearchView.On
                     } else {
                         handler.postDelayed(() -> {
                             bottomNavigationView.setVisibility(View.VISIBLE);
-                            searchItem.setVisible(roomItems.size() > 0);
+                            searchItem.setVisible(callItems.size() > 0);
                         }, 500);
                     }
 
@@ -221,7 +242,7 @@ public class CallsListController extends BaseController implements SearchView.On
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
-        searchItem.setVisible(roomItems.size() > 0);
+        searchItem.setVisible(callItems.size() > 0);
         if (adapter.hasSearchText()) {
             searchItem.expandActionView();
             searchView.setQuery(adapter.getSearchText(), false);
@@ -231,7 +252,7 @@ public class CallsListController extends BaseController implements SearchView.On
     private void fetchData() {
         dispose(null);
 
-        roomItems = new ArrayList<>();
+        callItems = new ArrayList<>();
 
         roomsQueryDisposable = ncApi.getRooms(ApiHelper.getCredentials(userEntity.getUsername(),
                 userEntity.getToken()), ApiHelper.getUrlForGetRooms(userEntity.getBaseUrl()))
@@ -241,18 +262,21 @@ public class CallsListController extends BaseController implements SearchView.On
 
                     if (roomsOverall != null) {
                         for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) {
-                            roomItems.add(new RoomItem(roomsOverall.getOcs().getData().get(i), userEntity));
+                            callItems.add(new CallItem(roomsOverall.getOcs().getData().get(i), userEntity));
                         }
 
-                        adapter.updateDataSet(roomItems, true);
+                        adapter.updateDataSet(callItems, true);
 
-                        Collections.sort(roomItems, (roomItem, t1) ->
-                                Long.compare(t1.getModel().getLastPing(), roomItem.getModel().getLastPing()));
+                        Collections.sort(callItems, (callItem, t1) ->
+                                Long.compare(t1.getModel().getLastPing(), callItem.getModel().getLastPing()));
 
                         if (searchItem != null) {
-                            searchItem.setVisible(roomItems.size() > 0);
+                            searchItem.setVisible(callItems.size() > 0);
                         }
                     }
+
+                    swipeRefreshLayout.setRefreshing(false);
+
                 }, throwable -> {
                     if (searchItem != null) {
                         searchItem.setVisible(false);
@@ -275,6 +299,7 @@ public class CallsListController extends BaseController implements SearchView.On
                                 break;
                         }
                     }
+                    swipeRefreshLayout.setRefreshing(false);
                     dispose(roomsQueryDisposable);
                 }, () -> {
                     dispose(roomsQueryDisposable);
@@ -358,4 +383,25 @@ public class CallsListController extends BaseController implements SearchView.On
         return onQueryTextChange(query);
     }
 
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onMessageEvent(MoreMenuClickEvent moreMenuClickEvent) {
+        BundleBuilder bundleBuilder = new BundleBuilder(new Bundle());
+        Room room = moreMenuClickEvent.getRoom();
+        bundleBuilder.putParcelable("room", Parcels.wrap(room));
+
+        View view = getActivity().getLayoutInflater().inflate(R.layout.bottom_sheet, null, false);
+
+        getChildRouter((ViewGroup) view).setRoot(
+                RouterTransaction.with(new RoomMenuController(bundleBuilder.build()))
+                        .popChangeHandler(new HorizontalChangeHandler())
+                        .pushChangeHandler(new HorizontalChangeHandler()));
+
+        if (bottomSheet == null) {
+            bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create();
+        }
+
+        bottomSheet.show();
+    }
+
+
 }

+ 8 - 6
app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java

@@ -51,7 +51,7 @@ import com.nextcloud.talk.R;
 import com.nextcloud.talk.adapters.items.UserItem;
 import com.nextcloud.talk.api.NcApi;
 import com.nextcloud.talk.api.helpers.api.ApiHelper;
-import com.nextcloud.talk.api.models.User;
+import com.nextcloud.talk.api.models.json.participants.Participant;
 import com.nextcloud.talk.api.models.json.sharees.Sharee;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.controllers.base.BaseController;
@@ -239,13 +239,13 @@ public class ContactsController extends BaseController implements SearchView.OnQ
                                             getExactUsers().getExactSharees());
                                 }
 
-                                User user;
+                                Participant participant;
                                 for (Sharee sharee : shareeHashSet) {
                                     if (!sharee.getValue().getShareWith().equals(userEntity.getUsername())) {
-                                        user = new User();
-                                        user.setName(sharee.getLabel());
-                                        user.setUserId(sharee.getValue().getShareWith());
-                                        contactItems.add(new UserItem(user, userEntity));
+                                        participant = new Participant();
+                                        participant.setName(sharee.getLabel());
+                                        participant.setUserId(sharee.getValue().getShareWith());
+                                        contactItems.add(new UserItem(participant, userEntity));
                                     }
 
                                 }
@@ -255,6 +255,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
 
                                 adapter.updateDataSet(contactItems, true);
                                 searchItem.setVisible(contactItems.size() > 0);
+                                swipeRefreshLayout.setRefreshing(false);
                             }
 
                         }, throwable -> {
@@ -280,6 +281,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
                                 }
                             }
 
+                            swipeRefreshLayout.setRefreshing(false);
                             dispose(contactsQueryDisposable);
                         }
                         , () -> {

+ 121 - 0
app/src/main/java/com/nextcloud/talk/controllers/RoomMenuController.java

@@ -0,0 +1,121 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 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.controllers;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+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.View;
+import android.view.ViewGroup;
+
+import com.nextcloud.talk.R;
+import com.nextcloud.talk.adapters.items.MenuItem;
+import com.nextcloud.talk.api.models.json.rooms.Room;
+import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.controllers.base.BaseController;
+
+import org.parceler.Parcels;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import autodagger.AutoInjector;
+import butterknife.BindView;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+
+@AutoInjector(NextcloudTalkApplication.class)
+public class RoomMenuController extends BaseController {
+    private Room room;
+
+    @BindView(R.id.recycler_view)
+    RecyclerView recyclerView;
+
+    private List<AbstractFlexibleItem> menuItems;
+    private FlexibleAdapter<AbstractFlexibleItem> adapter;
+
+    public RoomMenuController(Bundle args) {
+        super(args);
+        this.room = Parcels.unwrap(args.getParcelable("room"));
+    }
+
+    @Override
+    protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
+        return inflater.inflate(R.layout.controller_room_menu, container, false);
+    }
+
+    @Override
+    protected void onViewBound(@NonNull View view) {
+        super.onViewBound(view);
+        NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
+        prepareViews();
+    }
+
+    private void prepareViews() {
+        LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
+        recyclerView.setLayoutManager(layoutManager);
+        recyclerView.setHasFixedSize(true);
+
+        prepareMenu();
+        if (adapter == null) {
+            adapter = new FlexibleAdapter<>(menuItems, getActivity(), false);
+        }
+
+        recyclerView.setAdapter(adapter);
+
+        recyclerView.addItemDecoration(new DividerItemDecoration(
+                recyclerView.getContext(),
+                layoutManager.getOrientation()
+        ));
+    }
+
+    private void prepareMenu() {
+        menuItems = new ArrayList<>();
+        menuItems.add(new MenuItem(getResources().getString(R.string.nc_leave)));
+
+        if (room.isNameEditable()) {
+            menuItems.add(new MenuItem(getResources().getString(R.string.nc_rename)));
+        }
+
+        if (!room.isPublic()) {
+            menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_public)));
+        } else {
+            if (room.isHasPassword()) {
+                menuItems.add(new MenuItem(getResources().getString(R.string.nc_change_password)));
+            } else {
+                menuItems.add(new MenuItem(getResources().getString(R.string.nc_set_password)));
+            }
+
+            menuItems.add(new MenuItem(getResources().getString(R.string.nc_share_link)));
+            menuItems.add(new MenuItem(getResources().getString(R.string.nc_stop_sharing)));
+
+        }
+
+        if (room.isDeletable()) {
+            menuItems.add(new MenuItem(getResources().getString(R.string.nc_delete_call)));
+        }
+    }
+
+}

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

@@ -31,7 +31,7 @@ import android.view.ViewGroup;
 
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.adapters.items.UserItem;
-import com.nextcloud.talk.api.models.User;
+import com.nextcloud.talk.api.models.json.participants.Participant;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.controllers.base.BaseController;
 import com.nextcloud.talk.persistence.entities.UserEntity;
@@ -115,15 +115,15 @@ public class SwitchAccountController extends BaseController {
             adapter = new FlexibleAdapter<>(userItems, getActivity(), false);
 
             UserEntity userEntity;
-            User user;
+            Participant participant;
 
             for (Object userEntityObject : userUtils.getUsers()) {
                 userEntity = (UserEntity) userEntityObject;
                 if (!userEntity.getCurrent()) {
-                    user = new User();
-                    user.setName(userEntity.getDisplayName());
-                    user.setUserId(userEntity.getUsername());
-                    userItems.add(new UserItem(user, userEntity));
+                    participant = new Participant();
+                    participant.setName(userEntity.getDisplayName());
+                    participant.setUserId(userEntity.getUsername());
+                    userItems.add(new UserItem(participant, userEntity));
                 }
             }
 

+ 29 - 0
app/src/main/java/com/nextcloud/talk/events/MenuItemClickEvent.java

@@ -0,0 +1,29 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 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.events;
+
+public class MenuItemClickEvent {
+    private String menuTitle;
+
+    public MenuItemClickEvent(String menuTitle) {
+        this.menuTitle = menuTitle;
+    }
+}

+ 34 - 0
app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java

@@ -0,0 +1,34 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 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.events;
+
+import com.nextcloud.talk.api.models.json.rooms.Room;
+
+import lombok.Data;
+
+@Data
+public class MoreMenuClickEvent {
+    private final Room room;
+
+    public MoreMenuClickEvent(Room room) {
+        this.room = room;
+    }
+}

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

@@ -0,0 +1,25 @@
+<!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Mario Danic
+  ~ Copyright (C) 2017 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="#FF000000" android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>

+ 31 - 0
app/src/main/res/layout/bottom_sheet.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Mario Danic
+  ~ Copyright (C) 2017 Mario Danic
+  ~
+  ~ 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/>.
+  -->
+
+<com.bluelinelabs.conductor.ChangeHandlerFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                                                     xmlns:app="http://schemas.android.com/apk/res-auto"
+                                                     android:id="@+id/bottom_sheet"
+                                                     android:layout_width="match_parent"
+                                                     android:layout_height="wrap_content"
+                                                     android:background="?android:attr/windowBackground"
+                                                     app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+
+</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>

+ 35 - 0
app/src/main/res/layout/controller_room_menu.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Mario Danic
+  ~ Copyright (C) 2017 Mario Danic
+  ~
+  ~ 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: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"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        tools:listitem="@layout/rv_item_call"/>
+
+
+</RelativeLayout>

+ 14 - 3
app/src/main/res/layout/rv_item_call.xml

@@ -32,7 +32,7 @@
         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_marginEnd="@dimen/margin_between_elements"
         android:layout_marginStart="@dimen/activity_horizontal_margin"
         android:scaleType="centerInside"
         app:aiv_CornerRadius="@dimen/avatar_corner_radius"
@@ -40,11 +40,12 @@
         app:aiv_TextSizeRatio="0.5"/>
 
     <LinearLayout
-        android:layout_width="match_parent"
+        android:id="@+id/linear_layout"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerInParent="true"
         android:layout_toEndOf="@id/avatar_image"
-        android:layout_marginEnd="@dimen/activity_horizontal_margin"
+        android:layout_toStartOf="@+id/more_menu"
         android:orientation="vertical">
 
         <TextView
@@ -66,4 +67,14 @@
 
     </LinearLayout>
 
+    <ImageButton
+        android:id="@+id/more_menu"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        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"/>
+
 </RelativeLayout>

+ 33 - 0
app/src/main/res/layout/rv_item_menu.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Mario Danic
+  ~ Copyright (C) 2017 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="match_parent"
+                android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/menu_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/activity_horizontal_margin"/>
+
+</RelativeLayout>

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

@@ -60,4 +60,16 @@
     <string name="nc_license_summary">GNU General Public License, Version 3</string>
 
     <string name="nc_select_an_account">Select an account</string>
+
+    <!-- Room menu -->
+    <string name="nc_what">What would you like to do?</string>
+    <string name="nc_leave">Leave call</string>
+    <string name="nc_rename">Rename call</string>
+    <string name="nc_set_password">Set the password</string>
+    <string name="nc_change_password">Change the password</string>
+    <string name="nc_share_link">Share link</string>
+    <string name="nc_make_call_public">Make call public</string>
+    <string name="nc_stop_sharing">Stop sharing the call</string>
+    <string name="nc_delete_call">Delete call</string>
+
 </resources>

+ 1 - 1
build.gradle

@@ -12,7 +12,7 @@ buildscript {
         maven { url "http://dl.bintray.com/davideas/maven" }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.0.0'
+        classpath 'com.android.tools.build:gradle:3.0.1'
         classpath 'eu.davidea:grabver:0.6.0'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
         classpath 'com.google.gms:google-services:3.1.0'