diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java index 0052a3445..6def9f1ec 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java @@ -20,6 +20,7 @@ package com.nextcloud.talk.adapters.items; +import android.text.TextUtils; import android.view.View; import android.widget.TextView; @@ -122,23 +123,36 @@ public class UserItem extends AbstractFlexibleItem holder.contactDisplayName.setText(participant.getName()); } - GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - participant.getUserId(), R.dimen.avatar_size), new LazyHeaders.Builder() - .setHeader("Accept", "image/*") - .setHeader("User-Agent", ApiUtils.getUserAgent()) - .build()); + if (TextUtils.isEmpty(participant.getSource()) || participant.getSource().equals("users")) { + GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + participant.getUserId(), R.dimen.avatar_size), new LazyHeaders.Builder() + .setHeader("Accept", "image/*") + .setHeader("User-Agent", ApiUtils.getUserAgent()) + .build()); - int avatarSize = Math.round(NextcloudTalkApplication - .getSharedApplication().getResources().getDimension(R.dimen.avatar_size)); + int avatarSize = Math.round(NextcloudTalkApplication + .getSharedApplication().getResources().getDimension(R.dimen.avatar_size)); - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(glideUrl) - .centerInside() - .override(avatarSize, avatarSize) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(holder.avatarFlipView.getFrontImageView()); + GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) + .asBitmap() + .diskCacheStrategy(DiskCacheStrategy.NONE) + .load(glideUrl) + .centerInside() + .override(avatarSize, avatarSize) + .apply(RequestOptions.bitmapTransform(new CircleCrop())) + .into(holder.avatarFlipView.getFrontImageView()); + } else if (participant.getSource().equals("groups")) { + int avatarSize = Math.round(NextcloudTalkApplication.getSharedApplication().getApplicationContext().getResources().getDimension(R.dimen.avatar_size)); + + GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) + .asBitmap() + .diskCacheStrategy(DiskCacheStrategy.NONE) + .load(R.drawable.ic_people_group_white_24px) + .centerInside() + .override(avatarSize, avatarSize) + .apply(RequestOptions.bitmapTransform(new CircleCrop())) + .into(holder.avatarFlipView.getFrontImageView()); + } } @Override diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index ece33c723..9631b103c 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -39,9 +39,11 @@ import com.nextcloud.talk.models.json.signaling.SignalingOverall; import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; +import java.util.List; import java.util.Map; import io.reactivex.Observable; +import okhttp3.ResponseBody; import retrofit2.Response; import retrofit2.http.DELETE; import retrofit2.http.Field; @@ -65,10 +67,15 @@ public interface NcApi { - "itemType" : "call" Server URL is: baseUrl + ocsApiVersion + /apps/files_sharing/api/v1/sharees + + or if we're on 14 and up: + + baseUrl + ocsApiVersion + "/core/autocomplete/get"); + */ @GET - Observable> getContactsWithSearchParam(@Header("Authorization") String authorization, @Url String url, - @QueryMap Map options); + Observable getContactsWithSearchParam(@Header("Authorization") String authorization, @Url String url, + @Nullable @Query("shareTypes[]") List listOfShareTypes, @QueryMap Map options); /* diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java index 366b0a8ef..b2c38f49f 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -27,6 +27,8 @@ import android.os.Bundle; import android.os.Handler; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + +import com.bluelinelabs.logansquare.LoganSquare; import com.google.android.material.bottomnavigation.BottomNavigationView; import androidx.core.view.MenuItemCompat; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -34,6 +36,7 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.appcompat.widget.SearchView; import android.text.InputType; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -60,6 +63,8 @@ import com.nextcloud.talk.controllers.bottomsheet.OperationsMenuController; import com.nextcloud.talk.events.BottomSheetLockEvent; import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall; +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser; import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.models.json.rooms.Conversation; import com.nextcloud.talk.models.json.rooms.RoomOverall; @@ -99,6 +104,7 @@ import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import okhttp3.ResponseBody; import retrofit2.HttpException; import retrofit2.Response; @@ -413,6 +419,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ alreadyFetching = true; Set shareeHashSet = new HashSet<>(); + Set autocompleteUsersHashSet = new HashSet<>(); userHeaderItems = new HashMap<>(); @@ -424,8 +431,15 @@ public class ContactsController extends BaseController implements SearchView.OnQ contactItems = new ArrayList<>(); } - RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(currentUser.getBaseUrl(), - query); + RetrofitBucket retrofitBucket; + boolean serverIs14OrUp = false; + if (currentUser.hasSpreedCapabilityWithName("last-room-activity")) { + // a hack to see if we're on 14 or not + retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser.getBaseUrl(), query); + serverIs14OrUp = true; + } else { + retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(currentUser.getBaseUrl(), query); + } int page = 1; if (!startFromScratch) { @@ -439,73 +453,127 @@ public class ContactsController extends BaseController implements SearchView.OnQ Map modifiedQueryMap = new HashMap<>(retrofitBucket.getQueryMap()); modifiedQueryMap.put("page", page); modifiedQueryMap.put("perPage", 100); + + List shareTypesList = null; + + if (serverIs14OrUp) { + shareTypesList = new ArrayList<>(); + // users + shareTypesList.add("0"); + // groups + shareTypesList.add("1"); + + modifiedQueryMap.put("shareTypes[]", shareTypesList); + } + + boolean finalServerIs14OrUp = serverIs14OrUp; ncApi.getContactsWithSearchParam( credentials, - retrofitBucket.getUrl(), modifiedQueryMap) + retrofitBucket.getUrl(), shareTypesList, modifiedQueryMap) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .retry(3) - .subscribe(new Observer() { + .subscribe(new Observer() { @Override public void onSubscribe(Disposable d) { contactsQueryDisposable = d; } @Override - public void onNext(Response response) { - if (response.body() != null) { - ShareesOverall shareesOverall = (ShareesOverall) response.body(); - - if (shareesOverall.getOcs().getData().getUsers() != null) { - shareeHashSet.addAll(shareesOverall.getOcs().getData().getUsers()); - } - - if (shareesOverall.getOcs().getData().getExactUsers() != null && - shareesOverall.getOcs().getData().getExactUsers().getExactSharees() != null) { - shareeHashSet.addAll(shareesOverall.getOcs().getData(). - getExactUsers().getExactSharees()); - } - - if (TextUtils.isEmpty((CharSequence) modifiedQueryMap.get("search"))) { - canFetchFurther = !shareeHashSet.isEmpty(); - currentPage = (int) modifiedQueryMap.get("page"); - } else { - canFetchSearchFurther = !shareeHashSet.isEmpty(); - currentSearchPage = (int) modifiedQueryMap.get("page"); - } - - + public void onNext(ResponseBody responseBody) { + if (responseBody != null) { Participant participant; List newUserItemList = new ArrayList<>(); newUserItemList.addAll(contactItems); - for (Sharee sharee : shareeHashSet) { - if (!sharee.getValue().getShareWith().equals(currentUser.getUsername())) { - participant = new Participant(); - participant.setName(sharee.getLabel()); - String headerTitle; - headerTitle = sharee.getLabel().substring(0, 1).toUpperCase(); + try { + if (!finalServerIs14OrUp) { + ShareesOverall shareesOverall = LoganSquare.parse(responseBody.string(), ShareesOverall.class); - UserHeaderItem userHeaderItem; - if (!userHeaderItems.containsKey(headerTitle)) { - userHeaderItem = new UserHeaderItem(headerTitle); - userHeaderItems.put(headerTitle, userHeaderItem); + if (shareesOverall.getOcs().getData().getUsers() != null) { + shareeHashSet.addAll(shareesOverall.getOcs().getData().getUsers()); } - participant.setUserId(sharee.getValue().getShareWith()); - - UserItem newContactItem = new UserItem(participant, currentUser, - userHeaderItems.get(headerTitle)); - - if (!contactItems.contains(newContactItem)) { - newUserItemList.add(newContactItem); + if (shareesOverall.getOcs().getData().getExactUsers() != null && + shareesOverall.getOcs().getData().getExactUsers().getExactSharees() != null) { + shareeHashSet.addAll(shareesOverall.getOcs().getData(). + getExactUsers().getExactSharees()); } + for (Sharee sharee : shareeHashSet) { + if (!sharee.getValue().getShareWith().equals(currentUser.getUserId())) { + 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()); + + UserItem newContactItem = new UserItem(participant, currentUser, + userHeaderItems.get(headerTitle)); + + if (!contactItems.contains(newContactItem)) { + newUserItemList.add(newContactItem); + } + + } + + } + + } else { + AutocompleteOverall autocompleteOverall = LoganSquare.parse(responseBody.string(), AutocompleteOverall.class); + autocompleteUsersHashSet.addAll(autocompleteOverall.getOcs().getData()); + + for (AutocompleteUser autocompleteUser : autocompleteUsersHashSet) { + if (!autocompleteUser.getId().equals(currentUser.getUserId())) { + participant = new Participant(); + participant.setName(autocompleteUser.getLabel()); + participant.setUserId(autocompleteUser.getId()); + participant.setSource(autocompleteUser.getSource()); + + String headerTitle; + + headerTitle = participant.getName().substring(0, 1).toUpperCase(); + + UserHeaderItem userHeaderItem; + if (!userHeaderItems.containsKey(headerTitle)) { + userHeaderItem = new UserHeaderItem(headerTitle); + userHeaderItems.put(headerTitle, userHeaderItem); + } + + participant.setUserId(participant.getUserId()); + + UserItem newContactItem = new UserItem(participant, currentUser, + userHeaderItems.get(headerTitle)); + + if (!contactItems.contains(newContactItem)) { + newUserItemList.add(newContactItem); + } + + } + } } - + } catch (Exception exception) { + Log.e(TAG, "Parsing response body failed while getting contacts"); } + if (TextUtils.isEmpty((CharSequence) modifiedQueryMap.get("search"))) { + canFetchFurther = !shareeHashSet.isEmpty() || (finalServerIs14OrUp && !autocompleteUsersHashSet.isEmpty()); + currentPage = (int) modifiedQueryMap.get("page"); + } else { + canFetchSearchFurther = !shareeHashSet.isEmpty() || (finalServerIs14OrUp && !autocompleteUsersHashSet.isEmpty()) ; + currentSearchPage = (int) modifiedQueryMap.get("page"); + } + + boolean shouldFilterManually = false; if (newUserItemList.size() == contactItems.size()) { @@ -554,7 +622,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ checkAndHandleDoneMenuItem(); } } - ; + } @Override diff --git a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java new file mode 100644 index 000000000..c426fca1e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java @@ -0,0 +1,40 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 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 . + */ + +package com.nextcloud.talk.models.json.autocomplete; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.generic.GenericOCS; +import com.nextcloud.talk.models.json.sharees.SharesData; + +import org.parceler.Parcel; + +import java.util.List; + +import lombok.Data; + +@Parcel +@Data +@JsonObject +public class AutocompleteOCS extends GenericOCS { + @JsonField(name = "data") + List data; +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java new file mode 100644 index 000000000..97fbb1ac0 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java @@ -0,0 +1,37 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 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 . + */ + +package com.nextcloud.talk.models.json.autocomplete; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.sharees.ShareesOCS; + +import org.parceler.Parcel; + +import lombok.Data; + +@Parcel +@Data +@JsonObject +public class AutocompleteOverall { + @JsonField(name = "ocs") + AutocompleteOCS ocs; +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java new file mode 100644 index 000000000..1a6e5ac10 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java @@ -0,0 +1,43 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 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 . + */ + +package com.nextcloud.talk.models.json.autocomplete; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.sharees.Value; + +import org.parceler.Parcel; + +import lombok.Data; + +@Parcel +@Data +@JsonObject +public class AutocompleteUser { + @JsonField(name = "id") + String id; + + @JsonField(name = "label") + String label; + + @JsonField(name = "source") + String source; +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java b/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java index 72009a2c1..2bd20c79b 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java @@ -52,6 +52,8 @@ public class Participant { @JsonField(name = "inCall") boolean inCall; + String source; + public enum ParticipantType { DUMMY, OWNER, diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 7e22c9345..8c6e01c71 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -28,7 +28,9 @@ import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.RetrofitBucket; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import okhttp3.Credentials; @@ -61,6 +63,16 @@ public class ApiUtils { return retrofitBucket; } + public static RetrofitBucket getRetrofitBucketForContactsSearchFor14(String baseUrl, @Nullable String searchQuery) { + RetrofitBucket retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery); + retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/core/autocomplete/get"); + + retrofitBucket.getQueryMap().put("itemId", "new"); + + return retrofitBucket; + } + + public static String getUrlForSettingMyselfAsActiveParticipant(String baseUrl, String token) { return getRoom(baseUrl, token) + "/participants/active"; }