From 05586ccf47c8bfc24499fd6c06fe87543200a3f9 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 28 Dec 2021 15:10:29 +0100 Subject: [PATCH 01/56] add user status option to account dialog (WIP) Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/api/NcApi.java | 7 + .../models/database/CapabilitiesUtil.java | 18 +- .../json/capabilities/Capabilities.java | 160 +++++++ .../capabilities/UserStatusCapability.java | 32 ++ .../talk/models/json/status/Status.java | 159 ++++++ .../talk/models/json/status/StatusOCS.java | 69 +++ .../models/json/status/StatusOverall.java | 64 +++ .../dialog/ChooseAccountDialogFragment.java | 117 +++-- .../talk/ui/dialog/SetStatusDialogFragment.kt | 420 ++++++++++++++++ .../com/nextcloud/talk/utils/ApiUtils.java | 8 + app/src/main/res/drawable/ic_edit.xml | 34 ++ .../main/res/drawable/ic_user_status_away.xml | 32 ++ .../main/res/drawable/ic_user_status_dnd.xml | 38 ++ .../res/drawable/ic_user_status_invisible.xml | 34 ++ app/src/main/res/drawable/online_status.xml | 23 + .../main/res/layout/dialog_choose_account.xml | 39 +- app/src/main/res/layout/dialog_set_status.xml | 453 ++++++++++++++++++ app/src/main/res/layout/predefined_status.xml | 62 +++ app/src/main/res/values-night/colors.xml | 2 + app/src/main/res/values/colors.xml | 7 + app/src/main/res/values/dimens.xml | 6 + app/src/main/res/values/strings.xml | 21 + app/src/main/res/values/styles.xml | 8 + 23 files changed, 1781 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/Status.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/StatusOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/StatusOverall.java create mode 100644 app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt create mode 100644 app/src/main/res/drawable/ic_edit.xml create mode 100644 app/src/main/res/drawable/ic_user_status_away.xml create mode 100644 app/src/main/res/drawable/ic_user_status_dnd.xml create mode 100644 app/src/main/res/drawable/ic_user_status_invisible.xml create mode 100644 app/src/main/res/drawable/online_status.xml create mode 100644 app/src/main/res/layout/dialog_set_status.xml create mode 100644 app/src/main/res/layout/predefined_status.xml 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 6f61c6f97..74c17649a 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -40,6 +40,7 @@ import com.nextcloud.talk.models.json.push.PushRegistrationOverall; import com.nextcloud.talk.models.json.search.ContactsByNumberOverall; import com.nextcloud.talk.models.json.signaling.SignalingOverall; import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; +import com.nextcloud.talk.models.json.status.StatusOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; @@ -443,4 +444,10 @@ public interface NcApi { @GET Observable getOpenConversations(@Header("Authorization") String authorization, @Url String url); + + /* + * OCS Status API + */ + @GET + Observable status(@Header("Authorization") String authorization, @Url String url); } diff --git a/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java b/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java index b5ba0a04b..3d8375bcc 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java @@ -56,7 +56,7 @@ public abstract class CapabilitiesUtil { Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities.getExternalCapability() != null && capabilities.getExternalCapability().containsKey("v1")) { - return capabilities.getExternalCapability().get("v1").contains("capabilityName"); + return capabilities.getExternalCapability().get("v1").contains(capabilityName); } } catch (IOException e) { Log.e(TAG, "Failed to get capabilities for the user"); @@ -175,6 +175,22 @@ public abstract class CapabilitiesUtil { return false; } + public static boolean isUserStatusAvailable(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { + try { + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); + if (capabilities.getUserStatusCapability() != null && + capabilities.getUserStatusCapability().isEnabled() && + capabilities.getUserStatusCapability().isSupportsEmoji()) { + return true; + } + } catch (IOException e) { + Log.e(TAG, "Failed to get capabilities for the user"); + } + } + return false; + } + public static String getAttachmentFolder(@Nullable UserEntity user) { if (user != null && user.getCapabilities() != null) { try { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java new file mode 100644 index 000000000..ca0fa9492 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java @@ -0,0 +1,160 @@ +/* + * 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.capabilities; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.HashMap; +import java.util.List; + +@Parcel +@JsonObject +public class Capabilities { + @JsonField(name = "spreed") + SpreedCapability spreedCapability; + + @JsonField(name = "notifications") + NotificationsCapability notificationsCapability; + + @JsonField(name = "theming") + ThemingCapability themingCapability; + + @JsonField(name = "external") + HashMap> externalCapability; + + @JsonField(name = "provisioning_api") + ProvisioningCapability provisioningCapability; + + @JsonField(name = "user_status") + UserStatusCapability userStatusCapability; + + public SpreedCapability getSpreedCapability() { + return this.spreedCapability; + } + + public NotificationsCapability getNotificationsCapability() { + return this.notificationsCapability; + } + + public ThemingCapability getThemingCapability() { + return this.themingCapability; + } + + public HashMap> getExternalCapability() { + return this.externalCapability; + } + + public ProvisioningCapability getProvisioningCapability() { + return this.provisioningCapability; + } + + public UserStatusCapability getUserStatusCapability() { + return userStatusCapability; + } + + public void setSpreedCapability(SpreedCapability spreedCapability) { + this.spreedCapability = spreedCapability; + } + + public void setNotificationsCapability(NotificationsCapability notificationsCapability) { + this.notificationsCapability = notificationsCapability; + } + + public void setThemingCapability(ThemingCapability themingCapability) { + this.themingCapability = themingCapability; + } + + public void setExternalCapability(HashMap> externalCapability) { + this.externalCapability = externalCapability; + } + + public void setProvisioningCapability(ProvisioningCapability provisioningCapability) { + this.provisioningCapability = provisioningCapability; + } + + public void setUserStatusCapability(UserStatusCapability userStatusCapability) { + this.userStatusCapability = userStatusCapability; + } + + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Capabilities)) { + return false; + } + final Capabilities other = (Capabilities) o; + if (!other.canEqual((Object) this)) { + return false; + } + final Object this$spreedCapability = this.getSpreedCapability(); + final Object other$spreedCapability = other.getSpreedCapability(); + if (this$spreedCapability == null ? other$spreedCapability != null : !this$spreedCapability.equals(other$spreedCapability)) { + return false; + } + final Object this$notificationsCapability = this.getNotificationsCapability(); + final Object other$notificationsCapability = other.getNotificationsCapability(); + if (this$notificationsCapability == null ? other$notificationsCapability != null : !this$notificationsCapability.equals(other$notificationsCapability)) { + return false; + } + final Object this$themingCapability = this.getThemingCapability(); + final Object other$themingCapability = other.getThemingCapability(); + if (this$themingCapability == null ? other$themingCapability != null : !this$themingCapability.equals(other$themingCapability)) { + return false; + } + final Object this$externalCapability = this.getExternalCapability(); + final Object other$externalCapability = other.getExternalCapability(); + if (this$externalCapability == null ? other$externalCapability != null : !this$externalCapability.equals(other$externalCapability)) { + return false; + } + final Object this$provisioningCapability = this.getProvisioningCapability(); + final Object other$provisioningCapability = other.getProvisioningCapability(); + + return this$provisioningCapability == null ? other$provisioningCapability == null : this$provisioningCapability.equals(other$provisioningCapability); + } + + protected boolean canEqual(final Object other) { + return other instanceof Capabilities; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $spreedCapability = this.getSpreedCapability(); + result = result * PRIME + ($spreedCapability == null ? 43 : $spreedCapability.hashCode()); + final Object $notificationsCapability = this.getNotificationsCapability(); + result = result * PRIME + ($notificationsCapability == null ? 43 : $notificationsCapability.hashCode()); + final Object $themingCapability = this.getThemingCapability(); + result = result * PRIME + ($themingCapability == null ? 43 : $themingCapability.hashCode()); + final Object $externalCapability = this.getExternalCapability(); + result = result * PRIME + ($externalCapability == null ? 43 : $externalCapability.hashCode()); + final Object $provisioningCapability = this.getProvisioningCapability(); + result = result * PRIME + ($provisioningCapability == null ? 43 : $provisioningCapability.hashCode()); + return result; + } + + public String toString() { + return "Capabilities(spreedCapability=" + this.getSpreedCapability() + ", notificationsCapability=" + this.getNotificationsCapability() + ", themingCapability=" + this.getThemingCapability() + ", externalCapability=" + this.getExternalCapability() + ", provisioningCapability=" + this.getProvisioningCapability() + ")"; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java new file mode 100644 index 000000000..9c344e852 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java @@ -0,0 +1,32 @@ +package com.nextcloud.talk.models.json.capabilities; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +@Parcel +@JsonObject +public class UserStatusCapability { + @JsonField(name = "enabled") + boolean enabled; + + @JsonField(name = "supports_emoji") + boolean supportsEmoji; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isSupportsEmoji() { + return supportsEmoji; + } + + public void setSupportsEmoji(boolean supportsEmoji) { + this.supportsEmoji = supportsEmoji; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/Status.java b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.java new file mode 100644 index 000000000..d6f17d220 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.java @@ -0,0 +1,159 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.status; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.Objects; + +@Parcel +@JsonObject +public class Status { + + @JsonField(name = "userId") + public String userId; + + @JsonField(name = "message") + public String message; + + // TODO: Change to enum + @JsonField(name = "messageId") + public String messageId; + + @JsonField(name = "messageIsPredefined") + public boolean messageIsPredefined; + + @JsonField(name = "icon") + public String icon; + + @JsonField(name = "clearAt") + public long clearAt; + + // TODO: Change to enum + @JsonField(name = "status") + public String status; + + @JsonField(name = "statusIsUserDefined") + public boolean statusIsUserDefined; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessageId() { + return messageId; + } + + public void setMessageId(String messageId) { + this.messageId = messageId; + } + + public boolean isMessageIsPredefined() { + return messageIsPredefined; + } + + public void setMessageIsPredefined(boolean messageIsPredefined) { + this.messageIsPredefined = messageIsPredefined; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public long getClearAt() { + return clearAt; + } + + public void setClearAt(long clearAt) { + this.clearAt = clearAt; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public boolean isStatusIsUserDefined() { + return statusIsUserDefined; + } + + public void setStatusIsUserDefined(boolean statusIsUserDefined) { + this.statusIsUserDefined = statusIsUserDefined; + } + + public String getUserId() { + return this.userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Status status1 = (Status) o; + return messageIsPredefined == status1.messageIsPredefined && + clearAt == status1.clearAt && + statusIsUserDefined == status1.statusIsUserDefined && + Objects.equals(userId, status1.userId) && Objects.equals(message, status1.message) && + Objects.equals(messageId, status1.messageId) && Objects.equals(icon, status1.icon) && + Objects.equals(status, status1.status); + } + + @Override + public int hashCode() { + return Objects.hash(userId, message, messageId, messageIsPredefined, icon, clearAt, status, statusIsUserDefined); + } + + @Override + public String toString() { + return "Status{" + + "userId='" + userId + '\'' + + ", message='" + message + '\'' + + ", messageId='" + messageId + '\'' + + ", messageIsPredefined=" + messageIsPredefined + + ", icon='" + icon + '\'' + + ", clearAt=" + clearAt + + ", status='" + status + '\'' + + ", statusIsUserDefined=" + statusIsUserDefined + + '}'; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/StatusOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusOCS.java new file mode 100644 index 000000000..620e1084a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusOCS.java @@ -0,0 +1,69 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.status; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.generic.GenericOCS; + +import java.util.Objects; + +@JsonObject +public class StatusOCS extends GenericOCS { + @JsonField(name = "data") + public Status data; + + public Status getData() { + return this.data; + } + + public void setData(Status data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + StatusOCS that = (StatusOCS) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), data); + } + + @Override + public String toString() { + return "StatusOCS{" + + "data=" + data + + '}'; + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/StatusOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusOverall.java new file mode 100644 index 000000000..1107fc91c --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusOverall.java @@ -0,0 +1,64 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.status; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import java.util.Objects; + +@JsonObject +public class StatusOverall { + @JsonField(name = "ocs") + public StatusOCS ocs; + + public StatusOCS getOcs() { + return this.ocs; + } + + public void setOcs(StatusOCS ocs) { + this.ocs = ocs; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StatusOverall that = (StatusOverall) o; + return Objects.equals(ocs, that.ocs); + } + + @Override + public int hashCode() { + return Objects.hash(ocs); + } + + @Override + public String toString() { + return "StatusOverall{" + + "ocs=" + ocs + + '}'; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 2e72e943c..e497972e7 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -40,11 +40,14 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MainActivity; import com.nextcloud.talk.adapters.items.AdvancedUserItem; +import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.databinding.DialogChooseAccountBinding; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.User; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.participants.Participant; +import com.nextcloud.talk.models.json.status.StatusOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -61,8 +64,11 @@ import androidx.recyclerview.widget.LinearLayoutManager; import autodagger.AutoInjector; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; +import io.reactivex.Observable; import io.reactivex.Observer; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; @AutoInjector(NextcloudTalkApplication.class) public class ChooseAccountDialogFragment extends DialogFragment { @@ -74,6 +80,9 @@ public class ChooseAccountDialogFragment extends DialogFragment { @Inject CookieManager cookieManager; + @Inject + NcApi ncApi; + private DialogChooseAccountBinding binding; private View dialogView; @@ -106,24 +115,27 @@ public class ChooseAccountDialogFragment extends DialogFragment { binding.currentAccount.account.setText((Uri.parse(user.getBaseUrl()).getHost())); if (user.getBaseUrl() != null && - (user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) { + (user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) { binding.currentAccount.userIcon.setVisibility(View.VISIBLE); DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(binding.currentAccount.userIcon.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl( - ApiUtils.getUrlForAvatarWithName( - user.getBaseUrl(), - user.getUserId(), - R.dimen.small_item_height), - null)) - .build(); + .setOldController(binding.currentAccount.userIcon.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName( + user.getBaseUrl(), + user.getUserId(), + R.dimen.small_item_height), + null)) + .build(); binding.currentAccount.userIcon.setController(draweeController); } else { binding.currentAccount.userIcon.setVisibility(View.INVISIBLE); } + + + loadCurrentStatus(user); } // Creating listeners for quick-actions @@ -140,6 +152,16 @@ public class ChooseAccountDialogFragment extends DialogFragment { }); } + binding.setStatus.setOnClickListener(v -> { + dismiss(); + SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user); + setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status"); + }); + + if (CapabilitiesUtil.isUserStatusAvailable(userUtils.getCurrentUser())) { + binding.statusView.setVisibility(View.VISIBLE); + } + if (adapter == null) { adapter = new FlexibleAdapter<>(userItems, getActivity(), false); @@ -171,6 +193,39 @@ public class ChooseAccountDialogFragment extends DialogFragment { prepareViews(); } + private void loadCurrentStatus(User user) { + String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); + ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())). + subscribeOn(Schedulers.io()). + observeOn(AndroidSchedulers.mainThread()). + subscribe(new Observer() { + + private StatusOverall statusOverall; + + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.d("x", "onSubscribe"); + } + + @Override + public void onNext(@NonNull StatusOverall statusOverall) { + Log.d("x", "onNext"); + this.statusOverall = statusOverall; + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("x", "Läuft net", e); + } + + @Override + public void onComplete() { + Log.d("x", "complete"); + + } + }); + } + private void prepareViews() { if (getActivity() != null) { LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); @@ -196,21 +251,21 @@ public class ChooseAccountDialogFragment extends DialogFragment { } private final FlexibleAdapter.OnItemClickListener onSwitchItemClickListener = - new FlexibleAdapter.OnItemClickListener() { - @Override - public boolean onItemClick(View view, int position) { - if (userItems.size() > position) { - UserEntity userEntity = (userItems.get(position)).getEntity(); - userUtils.createOrUpdateUser(null, - null, - null, - null, - null, - Boolean.TRUE, - null, userEntity.getId(), - null, - null, - null) + new FlexibleAdapter.OnItemClickListener() { + @Override + public boolean onItemClick(View view, int position) { + if (userItems.size() > position) { + UserEntity userEntity = (userItems.get(position)).getEntity(); + userUtils.createOrUpdateUser(null, + null, + null, + null, + null, + Boolean.TRUE, + null, userEntity.getId(), + null, + null, + null) .subscribe(new Observer() { @Override public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { @@ -223,7 +278,7 @@ public class ChooseAccountDialogFragment extends DialogFragment { userUtils.disableAllUsersWithoutId(userEntity.getId()); if (getActivity() != null) { getActivity().runOnUiThread( - () -> ((MainActivity) getActivity()).resetConversationsList()); + () -> ((MainActivity) getActivity()).resetConversationsList()); } dismiss(); } @@ -238,9 +293,11 @@ public class ChooseAccountDialogFragment extends DialogFragment { // DONE } }); - } + } + + return true; + } + }; + - return true; - } - }; } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt new file mode 100644 index 000000000..6ed134c93 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -0,0 +1,420 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ + +package com.nextcloud.talk.ui.dialog + +import android.annotation.SuppressLint +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.nextcloud.talk.databinding.DialogSetStatusBinding +import com.nextcloud.talk.models.database.User +import com.nextcloud.talk.models.json.status.StatusOverall +import com.vanniktech.emoji.EmojiManager +import com.vanniktech.emoji.google.GoogleEmojiProvider + +private const val ARG_CURRENT_USER_PARAM = "currentUser" +private const val ARG_CURRENT_STATUS_PARAM = "currentStatus" + +private const val POS_DONT_CLEAR = 0 +private const val POS_HALF_AN_HOUR = 1 +private const val POS_AN_HOUR = 2 +private const val POS_FOUR_HOURS = 3 +private const val POS_TODAY = 4 +private const val POS_END_OF_WEEK = 5 + +private const val ONE_SECOND_IN_MILLIS = 1000 +private const val ONE_MINUTE_IN_SECONDS = 60 +private const val THIRTY_MINUTES = 30 +private const val FOUR_HOURS = 4 +private const val LAST_HOUR_OF_DAY = 23 +private const val LAST_MINUTE_OF_HOUR = 59 +private const val LAST_SECOND_OF_MINUTE = 59 + +class SetStatusDialogFragment : + DialogFragment() { + + private lateinit var binding: DialogSetStatusBinding + + // private var currentUser: User? = null + // private var currentStatus: Status? = null + // private lateinit var accountManager: UserAccountManager + // private lateinit var predefinedStatus: ArrayList + // private lateinit var adapter: PredefinedStatusListAdapter + // private var selectedPredefinedMessageId: String? = null + // private var clearAt: Long? = -1 + // private lateinit var popup: EmojiPopup + // + // @Inject + // lateinit var arbitraryDataProvider: ArbitraryDataProvider + // + // @Inject + // lateinit var asyncRunner: AsyncRunner + // + // @Inject + // lateinit var clientFactory: ClientFactory + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + // currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM) + // currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) + + // val json = arbitraryDataProvider.getValue(currentUser, ArbitraryDataProvider.PREDEFINED_STATUS) + + // if (json.isNotEmpty()) { + // val myType = object : TypeToken>() {}.type + // predefinedStatus = Gson().fromJson(json, myType) + // } + } + + + + EmojiManager.install(GoogleEmojiProvider()) + } + + @SuppressLint("InflateParams") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogSetStatusBinding.inflate(LayoutInflater.from(context)) + + return AlertDialog.Builder(requireContext()) + .setView(binding.root) + .create() + } + + @SuppressLint("DefaultLocale") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + + // accountManager = (activity as BaseActivity).userAccountManager + // + // currentStatus?.let { + // binding.emoji.setText(it.icon) + // binding.customStatusInput.text?.clear() + // binding.customStatusInput.setText(it.message) + // visualizeStatus(it.status) + // + // if (it.clearAt > 0) { + // binding.clearStatusAfterSpinner.visibility = View.GONE + // binding.remainingClearTime.apply { + // binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message) + // visibility = View.VISIBLE + // text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true) + // .toString() + // .decapitalize(Locale.getDefault()) + // setOnClickListener { + // visibility = View.GONE + // binding.clearStatusAfterSpinner.visibility = View.VISIBLE + // binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) + // } + // } + // } + // } + // + // adapter = PredefinedStatusListAdapter(this, requireContext()) + // if (this::predefinedStatus.isInitialized) { + // adapter.list = predefinedStatus + // } + // binding.predefinedStatusList.adapter = adapter + // binding.predefinedStatusList.layoutManager = LinearLayoutManager(context) + // + // binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) } + // binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) } + // binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) } + // binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) } + // + // binding.clearStatus.setOnClickListener { clearStatus() } + // binding.setStatus.setOnClickListener { setStatusMessage() } + // binding.emoji.setOnClickListener { openEmojiPopup() } + // + // popup = EmojiPopup.Builder + // .fromRootView(view) + // .setOnEmojiClickListener { _, _ -> + // popup.dismiss() + // binding.emoji.clearFocus() + // val imm: InputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as + // InputMethodManager + // imm.hideSoftInputFromWindow(binding.emoji.windowToken, 0) + // } + // .build(binding.emoji) + // binding.emoji.disableKeyboardInput(popup) + // binding.emoji.forceSingleEmoji() + // + // val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item) + // adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + // adapter.add(getString(R.string.dontClear)) + // adapter.add(getString(R.string.thirtyMinutes)) + // adapter.add(getString(R.string.oneHour)) + // adapter.add(getString(R.string.fourHours)) + // adapter.add(getString(R.string.today)) + // adapter.add(getString(R.string.thisWeek)) + // + // binding.clearStatusAfterSpinner.apply { + // this.adapter = adapter + // onItemSelectedListener = object : OnItemSelectedListener { + // override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + // setClearStatusAfterValue(position) + // } + // + // override fun onNothingSelected(parent: AdapterView<*>?) { + // // nothing to do + // } + // } + // } + // + // binding.clearStatus.setTextColor(ThemeColorUtils.primaryColor(context, true)) + // ThemeButtonUtils.colorPrimaryButton(binding.setStatus, context) + // ThemeTextInputUtils.colorTextInput( + // binding.customStatusInputContainer, + // binding.customStatusInput, + // ThemeColorUtils.primaryColor(activity) + // ) + } + + // @Suppress("ComplexMethod") + // private fun setClearStatusAfterValue(item: Int) { + // when (item) { + // POS_DONT_CLEAR -> { + // // don't clear + // clearAt = null + // } + // + // POS_HALF_AN_HOUR -> { + // // 30 minutes + // clearAt = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS + // } + // + // POS_AN_HOUR -> { + // // one hour + // clearAt = + // System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS + // } + // + // POS_FOUR_HOURS -> { + // // four hours + // clearAt = + // System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + // +FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS + // } + // + // POS_TODAY -> { + // // today + // val date = Calendar.getInstance().apply { + // set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + // set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + // set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + // } + // clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS + // } + // + // POS_END_OF_WEEK -> { + // // end of week + // val date = Calendar.getInstance().apply { + // set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + // set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + // set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + // } + // + // while (date.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) { + // date.add(Calendar.DAY_OF_YEAR, 1) + // } + // + // clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS + // } + // } + // } + // + // @Suppress("ReturnCount") + // private fun clearAtToUnixTime(clearAt: ClearAt?): Long { + // if (clearAt != null) { + // if (clearAt.type.equals("period")) { + // return System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + clearAt.time.toLong() + // } else if (clearAt.type.equals("end-of")) { + // if (clearAt.time.equals("day")) { + // val date = Calendar.getInstance().apply { + // set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + // set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + // set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + // } + // return date.timeInMillis / ONE_SECOND_IN_MILLIS + // } + // } + // } + // + // return -1 + // } + // + // private fun openEmojiPopup() { + // popup.show() + // } + // + // private fun clearStatus() { + // asyncRunner.postQuickTask( + // ClearStatusTask(accountManager.currentOwnCloudAccount?.savedAccount, context), + // { dismiss(it) } + // ) + // } + // + // private fun setStatus(statusType: StatusType) { + // visualizeStatus(statusType) + // + // asyncRunner.postQuickTask( + // SetStatusTask( + // statusType, + // accountManager.currentOwnCloudAccount?.savedAccount, + // context + // ), + // { + // if (!it) { + // clearTopStatus() + // } + // }, + // { clearTopStatus() } + // ) + // } + // + // private fun visualizeStatus(statusType: StatusType) { + // when (statusType) { + // StatusType.ONLINE -> { + // clearTopStatus() + // binding.onlineStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) + // } + // StatusType.AWAY -> { + // clearTopStatus() + // binding.awayStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) + // } + // StatusType.DND -> { + // clearTopStatus() + // binding.dndStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) + // } + // StatusType.INVISIBLE -> { + // clearTopStatus() + // binding.invisibleStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) + // } + // else -> clearTopStatus() + // } + // } + // + // private fun clearTopStatus() { + // context?.let { + // val grey = it.resources.getColor(R.color.grey_200) + // binding.onlineStatus.setBackgroundColor(grey) + // binding.awayStatus.setBackgroundColor(grey) + // binding.dndStatus.setBackgroundColor(grey) + // binding.invisibleStatus.setBackgroundColor(grey) + // } + // } + // + // private fun setStatusMessage() { + // if (selectedPredefinedMessageId != null) { + // asyncRunner.postQuickTask( + // SetPredefinedCustomStatusTask( + // selectedPredefinedMessageId!!, + // clearAt, + // accountManager.currentOwnCloudAccount?.savedAccount, + // context + // ), + // { dismiss(it) } + // ) + // } else { + // asyncRunner.postQuickTask( + // SetUserDefinedCustomStatusTask( + // binding.customStatusInput.text.toString(), + // binding.emoji.text.toString(), + // clearAt, + // accountManager.currentOwnCloudAccount?.savedAccount, + // context + // ), + // { dismiss(it) } + // ) + // } + // } + + private fun dismiss(boolean: Boolean) { + if (boolean) { + dismiss() + } + } + + /** + * Fragment creator + */ + companion object { + @JvmStatic + fun newInstance(user: User): SetStatusDialogFragment { + val args = Bundle() + args.putParcelable(ARG_CURRENT_USER_PARAM, user) + //args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) + + val dialogFragment = SetStatusDialogFragment() + dialogFragment.arguments = args + // dialogFragment.setStyle(STYLE_NORMAL, R.style.Theme_ownCloud_Dialog) + return dialogFragment + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return binding.root + } + + // override fun onClick(predefinedStatus: PredefinedStatus) { + // selectedPredefinedMessageId = predefinedStatus.id + // clearAt = clearAtToUnixTime(predefinedStatus.clearAt) + // binding.emoji.setText(predefinedStatus.icon) + // binding.customStatusInput.text?.clear() + // binding.customStatusInput.text?.append(predefinedStatus.message) + // + // binding.remainingClearTime.visibility = View.GONE + // binding.clearStatusAfterSpinner.visibility = View.VISIBLE + // binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) + // + // if (predefinedStatus.clearAt == null) { + // binding.clearStatusAfterSpinner.setSelection(0) + // } else { + // val clearAt = predefinedStatus.clearAt!! + // if (clearAt.type.equals("period")) { + // when (clearAt.time) { + // "1800" -> binding.clearStatusAfterSpinner.setSelection(POS_HALF_AN_HOUR) + // "3600" -> binding.clearStatusAfterSpinner.setSelection(POS_AN_HOUR) + // "14400" -> binding.clearStatusAfterSpinner.setSelection(POS_FOUR_HOURS) + // else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR) + // } + // } else if (clearAt.type.equals("end-of")) { + // when (clearAt.time) { + // "day" -> binding.clearStatusAfterSpinner.setSelection(POS_TODAY) + // "week" -> binding.clearStatusAfterSpinner.setSelection(POS_END_OF_WEEK) + // else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR) + // } + // } + // } + // setClearStatusAfterValue(binding.clearStatusAfterSpinner.selectedItemPosition) + // } + // + // @VisibleForTesting + // fun setPredefinedStatus(predefinedStatus: ArrayList) { + // adapter.list = predefinedStatus + // binding.predefinedStatusList.adapter?.notifyDataSetChanged() + // } +} 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 01c5c7f74..94ed1ac0d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -416,4 +416,12 @@ public class ApiUtils { public static String getUrlForSetChatReadMarker(int version, String baseUrl, String roomToken) { return getUrlForChat(version, baseUrl, roomToken) + "/read"; } + + /* + * OCS Status API + */ + + public static String getUrlForStatus(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status"; + } } diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 000000000..406f0b5f6 --- /dev/null +++ b/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,34 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_user_status_away.xml b/app/src/main/res/drawable/ic_user_status_away.xml new file mode 100644 index 000000000..ab5ca9642 --- /dev/null +++ b/app/src/main/res/drawable/ic_user_status_away.xml @@ -0,0 +1,32 @@ + + + + diff --git a/app/src/main/res/drawable/ic_user_status_dnd.xml b/app/src/main/res/drawable/ic_user_status_dnd.xml new file mode 100644 index 000000000..27cfc1066 --- /dev/null +++ b/app/src/main/res/drawable/ic_user_status_dnd.xml @@ -0,0 +1,38 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_user_status_invisible.xml b/app/src/main/res/drawable/ic_user_status_invisible.xml new file mode 100644 index 000000000..18a35e8e1 --- /dev/null +++ b/app/src/main/res/drawable/ic_user_status_invisible.xml @@ -0,0 +1,34 @@ + + + + + diff --git a/app/src/main/res/drawable/online_status.xml b/app/src/main/res/drawable/online_status.xml new file mode 100644 index 000000000..fb042c2d3 --- /dev/null +++ b/app/src/main/res/drawable/online_status.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/layout/dialog_choose_account.xml b/app/src/main/res/layout/dialog_choose_account.xml index b15e29920..702a7392a 100644 --- a/app/src/main/res/layout/dialog_choose_account.xml +++ b/app/src/main/res/layout/dialog_choose_account.xml @@ -17,6 +17,7 @@ --> @@ -31,6 +32,42 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + app:layout_constraintTop_toBottomOf="@id/statusView" /> . +--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/predefined_status.xml b/app/src/main/res/layout/predefined_status.xml new file mode 100644 index 000000000..69e080c4f --- /dev/null +++ b/app/src/main/res/layout/predefined_status.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 622e1a854..6b62f1190 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -65,4 +65,6 @@ #4B4B4B #282828 + + #222222 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8b059a897..a152cde85 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -97,4 +97,11 @@ #99121212 + #eeeeee + #818181 + #D6D7D7 + #000000 + #007cc2 + #ffffff + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 2bcef5826..753129d95 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -63,4 +63,10 @@ 180dp 110dp 0dp + + 52dp + 4dp + 16sp + 48dp + 2dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 345a31a82..05c9d63d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -268,6 +268,27 @@ Remove group and members Pin: %1$s + + Set status + Online status + Status message + What is your status? + Clear status message after + Clear status message + Set status message + Online + Do not disturb + Away + Invisible + + 😃 + Don\'t clear + Today + 30 minutes + 1 hour + 4 hours + This week + Unread mentions Conversations diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 89d41e79b..d97926026 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -240,4 +240,12 @@ adjustResize + + From a94f0f1bf12126f31f293464032d70a48973d190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Wed, 12 Jan 2022 17:33:37 +0100 Subject: [PATCH 02/56] Adopt more parts from files app [WIP] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- app/build.gradle | 4 +- .../adapters/PredefinedStatusClickListener.kt | 29 + .../adapters/PredefinedStatusListAdapter.kt | 50 ++ .../adapters/PredefinedStatusViewHolder.kt | 59 ++ .../java/com/nextcloud/talk/api/NcApi.java | 5 + .../application/NextcloudTalkApplication.kt | 4 +- .../talk/models/json/status/ClearAt.kt | 4 + .../models/json/status/PredefinedStatus.kt | 3 + .../talk/models/json/status/Status.java | 159 ----- .../talk/models/json/status/Status.kt | 52 ++ .../talk/models/json/status/StatusType.kt | 11 + .../dialog/ChooseAccountDialogFragment.java | 25 +- .../talk/ui/dialog/SetStatusDialogFragment.kt | 628 ++++++++++-------- .../com/nextcloud/talk/utils/ApiUtils.java | 4 + .../nextcloud/talk/utils/DisplayUtils.java | 67 ++ app/src/main/res/values/strings.xml | 1 + 16 files changed, 647 insertions(+), 458 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/Status.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt diff --git a/app/build.gradle b/app/build.gradle index 6b894348c..20d0d23c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -194,7 +194,9 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - implementation 'com.github.vanniktech:Emoji:0.6.0' // 0.7.0 has display issue - don't update to 0.7.0 + // implementation 'com.github.vanniktech:Emoji:0.6.0' // 0.7.0 has display issue - don't update to 0.7.0 + implementation "com.vanniktech:emoji-google:0.8.0" + // implementation "com.vanniktech:emoji-google-compat:0.8.0" implementation group: 'androidx.emoji', name: 'emoji-bundled', version: '1.1.0' implementation 'org.michaelevans.colorart:library:0.0.3' implementation "androidx.work:work-runtime:${workVersion}" diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt new file mode 100644 index 000000000..fa2485dcd --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt @@ -0,0 +1,29 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.adapters + +import com.nextcloud.talk.models.json.status.PredefinedStatus + +interface PredefinedStatusClickListener { + fun onClick(predefinedStatus: PredefinedStatus) +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt new file mode 100644 index 000000000..253304af6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt @@ -0,0 +1,50 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.databinding.PredefinedStatusBinding +import com.nextcloud.talk.models.json.status.PredefinedStatus + +class PredefinedStatusListAdapter( + private val clickListener: PredefinedStatusClickListener, + val context: Context +) : RecyclerView.Adapter() { + internal var list: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PredefinedStatusViewHolder { + val itemBinding = PredefinedStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return PredefinedStatusViewHolder(itemBinding) + } + + override fun onBindViewHolder(holder: PredefinedStatusViewHolder, position: Int) { + holder.bind(list[position], clickListener, context) + } + + override fun getItemCount(): Int { + return list.size + } +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt new file mode 100644 index 000000000..2671896c5 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt @@ -0,0 +1,59 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.adapters + +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.R +import com.nextcloud.talk.databinding.PredefinedStatusBinding +import com.nextcloud.talk.models.json.status.PredefinedStatus +import com.nextcloud.talk.utils.DisplayUtils + +private const val ONE_SECOND_IN_MILLIS = 1000 + +class PredefinedStatusViewHolder(private val binding: PredefinedStatusBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(status: PredefinedStatus, clickListener: PredefinedStatusClickListener, context: Context) { + binding.root.setOnClickListener { clickListener.onClick(status) } + binding.icon.text = status.icon + binding.name.text = status.message + + if (status.clearAt == null) { + binding.clearAt.text = context.getString(R.string.dontClear) + } else { + val clearAt = status.clearAt!! + if (clearAt.type.equals("period")) { + binding.clearAt.text = DisplayUtils.getRelativeTimestamp( + context, + System.currentTimeMillis() + clearAt.time.toInt() * ONE_SECOND_IN_MILLIS, + true + ) + } else { + // end-of + if (clearAt.time.equals("day")) { + binding.clearAt.text = context.getString(R.string.today) + } + } + } + } +} 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 74c17649a..e693731f3 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -450,4 +450,9 @@ public interface NcApi { */ @GET Observable status(@Header("Authorization") String authorization, @Url String url); + + @DELETE + Observable statusDeleteMessage(@Header("Authorization") String authorization, @Url String url); + + } diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt index 4c391d3b5..2ae6de462 100644 --- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt +++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt @@ -66,7 +66,7 @@ import com.nextcloud.talk.utils.database.user.UserModule import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.webrtc.MagicWebRTCUtils import com.vanniktech.emoji.EmojiManager -import com.vanniktech.emoji.googlecompat.GoogleCompatEmojiProvider +import com.vanniktech.emoji.google.GoogleEmojiProvider import de.cotech.hw.SecurityKeyManager import de.cotech.hw.SecurityKeyManagerConfig import okhttp3.OkHttpClient @@ -188,7 +188,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { config.setReplaceAll(true) val emojiCompat = EmojiCompat.init(config) - EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat)) + EmojiManager.install(GoogleEmojiProvider()) NotificationUtils.registerNotificationChannels(applicationContext, appPreferences) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt new file mode 100644 index 000000000..aa6107447 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt @@ -0,0 +1,4 @@ +package com.nextcloud.talk.models.json.status + + +class ClearAt(val type: String, val time: String) \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt new file mode 100644 index 000000000..0f689df2f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt @@ -0,0 +1,3 @@ +package com.nextcloud.talk.models.json.status + +class PredefinedStatus(val id: String, val icon: String, val message: String, val clearAt: ClearAt?) \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/Status.java b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.java deleted file mode 100644 index d6f17d220..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/Status.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * - * Nextcloud Talk application - * - * @author Tim Krüger - * Copyright (C) 2021 Tim Krüger - * - * 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.status; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; - -import org.parceler.Parcel; - -import java.util.Objects; - -@Parcel -@JsonObject -public class Status { - - @JsonField(name = "userId") - public String userId; - - @JsonField(name = "message") - public String message; - - // TODO: Change to enum - @JsonField(name = "messageId") - public String messageId; - - @JsonField(name = "messageIsPredefined") - public boolean messageIsPredefined; - - @JsonField(name = "icon") - public String icon; - - @JsonField(name = "clearAt") - public long clearAt; - - // TODO: Change to enum - @JsonField(name = "status") - public String status; - - @JsonField(name = "statusIsUserDefined") - public boolean statusIsUserDefined; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public String getMessageId() { - return messageId; - } - - public void setMessageId(String messageId) { - this.messageId = messageId; - } - - public boolean isMessageIsPredefined() { - return messageIsPredefined; - } - - public void setMessageIsPredefined(boolean messageIsPredefined) { - this.messageIsPredefined = messageIsPredefined; - } - - public String getIcon() { - return icon; - } - - public void setIcon(String icon) { - this.icon = icon; - } - - public long getClearAt() { - return clearAt; - } - - public void setClearAt(long clearAt) { - this.clearAt = clearAt; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public boolean isStatusIsUserDefined() { - return statusIsUserDefined; - } - - public void setStatusIsUserDefined(boolean statusIsUserDefined) { - this.statusIsUserDefined = statusIsUserDefined; - } - - public String getUserId() { - return this.userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Status status1 = (Status) o; - return messageIsPredefined == status1.messageIsPredefined && - clearAt == status1.clearAt && - statusIsUserDefined == status1.statusIsUserDefined && - Objects.equals(userId, status1.userId) && Objects.equals(message, status1.message) && - Objects.equals(messageId, status1.messageId) && Objects.equals(icon, status1.icon) && - Objects.equals(status, status1.status); - } - - @Override - public int hashCode() { - return Objects.hash(userId, message, messageId, messageIsPredefined, icon, clearAt, status, statusIsUserDefined); - } - - @Override - public String toString() { - return "Status{" + - "userId='" + userId + '\'' + - ", message='" + message + '\'' + - ", messageId='" + messageId + '\'' + - ", messageIsPredefined=" + messageIsPredefined + - ", icon='" + icon + '\'' + - ", clearAt=" + clearAt + - ", status='" + status + '\'' + - ", statusIsUserDefined=" + statusIsUserDefined + - '}'; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt new file mode 100644 index 000000000..f66a09724 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt @@ -0,0 +1,52 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.status + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class Status( + @JsonField(name = ["userId"]) + var userId: String?, + @JsonField(name = ["message"]) + var message: String?, + /* TODO: Change to enum */ + @JsonField(name = ["messageId"]) + var messageId: String?, + @JsonField(name = ["messageIsPredefined"]) + var messageIsPredefined: Boolean, + @JsonField(name = ["icon"]) + var icon: String?, + @JsonField(name = ["clearAt"]) + var clearAt: Long = 0, + /* TODO: Change to enum */ + @JsonField(name = ["status"]) + var status: String = "offline", + @JsonField(name = ["statusIsUserDefined"]) + var statusIsUserDefined: Boolean +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null, null, false, null, 0, "offline", false) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt new file mode 100644 index 000000000..3f11936de --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt @@ -0,0 +1,11 @@ +package com.nextcloud.talk.models.json.status + + + +enum class StatusType(val string: String) { + ONLINE("online"), + OFFLINE("offline"), + DND("dnd"), + AWAY("away"), + INVISIBLE("invisible"); +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index e497972e7..bdca62698 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -47,6 +47,7 @@ import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.User; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.participants.Participant; +import com.nextcloud.talk.models.json.status.Status; import com.nextcloud.talk.models.json.status.StatusOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; @@ -55,6 +56,7 @@ import com.nextcloud.talk.utils.database.user.UserUtils; import java.net.CookieManager; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.inject.Inject; @@ -89,6 +91,8 @@ public class ChooseAccountDialogFragment extends DialogFragment { private FlexibleAdapter adapter; private final List userItems = new ArrayList<>(); + private Status status; + @SuppressLint("InflateParams") @NonNull @Override @@ -154,8 +158,12 @@ public class ChooseAccountDialogFragment extends DialogFragment { binding.setStatus.setOnClickListener(v -> { dismiss(); - SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user); - setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status"); + + // TODO: better solution + if(status != null) { + SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user, status); + setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status"); + } }); if (CapabilitiesUtil.isUserStatusAvailable(userUtils.getCurrentUser())) { @@ -200,28 +208,21 @@ public class ChooseAccountDialogFragment extends DialogFragment { observeOn(AndroidSchedulers.mainThread()). subscribe(new Observer() { - private StatusOverall statusOverall; - @Override - public void onSubscribe(@NonNull Disposable d) { - Log.d("x", "onSubscribe"); - } + public void onSubscribe(@NonNull Disposable d) {} @Override public void onNext(@NonNull StatusOverall statusOverall) { - Log.d("x", "onNext"); - this.statusOverall = statusOverall; + status = statusOverall.ocs.data; } @Override public void onError(@NonNull Throwable e) { - Log.e("x", "Läuft net", e); + Log.e(TAG, "Can't receive user status from server. ", e); } @Override public void onComplete() { - Log.d("x", "complete"); - } }); } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 6ed134c93..94ee5aaf0 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -22,17 +22,42 @@ package com.nextcloud.talk.ui.dialog import android.annotation.SuppressLint import android.app.Dialog +import android.content.Context import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.AdapterView +import android.widget.AdapterView.OnItemSelectedListener +import android.widget.ArrayAdapter import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.LinearLayoutManager +import autodagger.AutoInjector +import com.nextcloud.talk.R +import com.nextcloud.talk.adapters.PredefinedStatusClickListener +import com.nextcloud.talk.adapters.PredefinedStatusListAdapter +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogSetStatusBinding import com.nextcloud.talk.models.database.User -import com.nextcloud.talk.models.json.status.StatusOverall -import com.vanniktech.emoji.EmojiManager -import com.vanniktech.emoji.google.GoogleEmojiProvider +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.status.ClearAt +import com.nextcloud.talk.models.json.status.PredefinedStatus +import com.nextcloud.talk.models.json.status.Status +import com.nextcloud.talk.models.json.status.StatusType +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils +import com.vanniktech.emoji.EmojiPopup +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import java.util.Calendar +import java.util.Locale +import javax.inject.Inject private const val ARG_CURRENT_USER_PARAM = "currentUser" private const val ARG_CURRENT_STATUS_PARAM = "currentStatus" @@ -52,19 +77,23 @@ private const val LAST_HOUR_OF_DAY = 23 private const val LAST_MINUTE_OF_HOUR = 59 private const val LAST_SECOND_OF_MINUTE = 59 +@AutoInjector(NextcloudTalkApplication::class) class SetStatusDialogFragment : - DialogFragment() { + DialogFragment(), PredefinedStatusClickListener { + + private val logTag = ChooseAccountDialogFragment::class.java.simpleName + private lateinit var binding: DialogSetStatusBinding - // private var currentUser: User? = null - // private var currentStatus: Status? = null + private var currentUser: User? = null + private var currentStatus: Status? = null // private lateinit var accountManager: UserAccountManager - // private lateinit var predefinedStatus: ArrayList - // private lateinit var adapter: PredefinedStatusListAdapter - // private var selectedPredefinedMessageId: String? = null - // private var clearAt: Long? = -1 - // private lateinit var popup: EmojiPopup + private lateinit var predefinedStatus: ArrayList + private lateinit var adapter: PredefinedStatusListAdapter + private var selectedPredefinedMessageId: String? = null + private var clearAt: Long? = -1 + private lateinit var popup: EmojiPopup // // @Inject // lateinit var arbitraryDataProvider: ArbitraryDataProvider @@ -75,11 +104,14 @@ class SetStatusDialogFragment : // @Inject // lateinit var clientFactory: ClientFactory + @Inject + lateinit var ncApi: NcApi + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { - // currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM) - // currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) + currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM) + currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) // val json = arbitraryDataProvider.getValue(currentUser, ArbitraryDataProvider.PREDEFINED_STATUS) @@ -91,7 +123,7 @@ class SetStatusDialogFragment : - EmojiManager.install(GoogleEmojiProvider()) + // EmojiManager.install(GoogleEmojiProvider()) } @SuppressLint("InflateParams") @@ -110,82 +142,85 @@ class SetStatusDialogFragment : // accountManager = (activity as BaseActivity).userAccountManager // - // currentStatus?.let { - // binding.emoji.setText(it.icon) - // binding.customStatusInput.text?.clear() - // binding.customStatusInput.setText(it.message) - // visualizeStatus(it.status) - // - // if (it.clearAt > 0) { - // binding.clearStatusAfterSpinner.visibility = View.GONE - // binding.remainingClearTime.apply { - // binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message) - // visibility = View.VISIBLE - // text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true) - // .toString() - // .decapitalize(Locale.getDefault()) - // setOnClickListener { - // visibility = View.GONE - // binding.clearStatusAfterSpinner.visibility = View.VISIBLE - // binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) - // } - // } - // } - // } - // - // adapter = PredefinedStatusListAdapter(this, requireContext()) - // if (this::predefinedStatus.isInitialized) { - // adapter.list = predefinedStatus - // } - // binding.predefinedStatusList.adapter = adapter - // binding.predefinedStatusList.layoutManager = LinearLayoutManager(context) - // - // binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) } - // binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) } - // binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) } - // binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) } - // - // binding.clearStatus.setOnClickListener { clearStatus() } - // binding.setStatus.setOnClickListener { setStatusMessage() } - // binding.emoji.setOnClickListener { openEmojiPopup() } - // - // popup = EmojiPopup.Builder - // .fromRootView(view) - // .setOnEmojiClickListener { _, _ -> - // popup.dismiss() - // binding.emoji.clearFocus() - // val imm: InputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as - // InputMethodManager - // imm.hideSoftInputFromWindow(binding.emoji.windowToken, 0) - // } - // .build(binding.emoji) - // binding.emoji.disableKeyboardInput(popup) - // binding.emoji.forceSingleEmoji() - // - // val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item) - // adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - // adapter.add(getString(R.string.dontClear)) - // adapter.add(getString(R.string.thirtyMinutes)) - // adapter.add(getString(R.string.oneHour)) - // adapter.add(getString(R.string.fourHours)) - // adapter.add(getString(R.string.today)) - // adapter.add(getString(R.string.thisWeek)) - // - // binding.clearStatusAfterSpinner.apply { - // this.adapter = adapter - // onItemSelectedListener = object : OnItemSelectedListener { - // override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { - // setClearStatusAfterValue(position) - // } - // - // override fun onNothingSelected(parent: AdapterView<*>?) { - // // nothing to do - // } - // } - // } - // - // binding.clearStatus.setTextColor(ThemeColorUtils.primaryColor(context, true)) + currentStatus?.let { + binding.emoji.setText(it.icon) + binding.customStatusInput.text?.clear() + binding.customStatusInput.setText(it.message) + visualizeStatus(it.status) + + if (it.clearAt > 0) { + binding.clearStatusAfterSpinner.visibility = View.GONE + binding.remainingClearTime.apply { + binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message) + visibility = View.VISIBLE + text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true) + .toString() + .decapitalize(Locale.getDefault()) + setOnClickListener { + visibility = View.GONE + binding.clearStatusAfterSpinner.visibility = View.VISIBLE + binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) + } + } + } + } + + adapter = PredefinedStatusListAdapter(this, requireContext()) + if (this::predefinedStatus.isInitialized) { + adapter.list = predefinedStatus + } + binding.predefinedStatusList.adapter = adapter + binding.predefinedStatusList.layoutManager = LinearLayoutManager(context) + + binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) } + binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) } + binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) } + binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) } + + binding.clearStatus.setOnClickListener { clearStatus() } + binding.setStatus.setOnClickListener { setStatusMessage() } + binding.emoji.setOnClickListener { openEmojiPopup() } + + popup = EmojiPopup.Builder + .fromRootView(view) + .setOnEmojiClickListener { _, _ -> + popup.dismiss() + binding.emoji.clearFocus() + val imm: InputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as + InputMethodManager + imm.hideSoftInputFromWindow(binding.emoji.windowToken, 0) + } + .build(binding.emoji) + binding.emoji.disableKeyboardInput(popup) + binding.emoji.forceSingleEmoji() + + val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + adapter.add(getString(R.string.dontClear)) + adapter.add(getString(R.string.thirtyMinutes)) + adapter.add(getString(R.string.oneHour)) + adapter.add(getString(R.string.fourHours)) + adapter.add(getString(R.string.today)) + adapter.add(getString(R.string.thisWeek)) + + binding.clearStatusAfterSpinner.apply { + this.adapter = adapter + onItemSelectedListener = object : OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + setClearStatusAfterValue(position) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + // nothing to do + } + } + } + + binding.clearStatus.setTextColor(resources.getColor(R.color.colorPrimary)) + binding.setStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) // ThemeButtonUtils.colorPrimaryButton(binding.setStatus, context) + + binding.customStatusInput.highlightColor = resources.getColor(R.color.colorPrimary) // ThemeTextInputUtils.colorTextInput( // binding.customStatusInputContainer, // binding.customStatusInput, @@ -193,180 +228,202 @@ class SetStatusDialogFragment : // ) } - // @Suppress("ComplexMethod") - // private fun setClearStatusAfterValue(item: Int) { - // when (item) { - // POS_DONT_CLEAR -> { - // // don't clear - // clearAt = null - // } - // - // POS_HALF_AN_HOUR -> { - // // 30 minutes - // clearAt = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS - // } - // - // POS_AN_HOUR -> { - // // one hour - // clearAt = - // System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS - // } - // - // POS_FOUR_HOURS -> { - // // four hours - // clearAt = - // System.currentTimeMillis() / ONE_SECOND_IN_MILLIS - // +FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS - // } - // - // POS_TODAY -> { - // // today - // val date = Calendar.getInstance().apply { - // set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) - // set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) - // set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) - // } - // clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS - // } - // - // POS_END_OF_WEEK -> { - // // end of week - // val date = Calendar.getInstance().apply { - // set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) - // set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) - // set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) - // } - // - // while (date.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) { - // date.add(Calendar.DAY_OF_YEAR, 1) - // } - // - // clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS - // } - // } - // } - // - // @Suppress("ReturnCount") - // private fun clearAtToUnixTime(clearAt: ClearAt?): Long { - // if (clearAt != null) { - // if (clearAt.type.equals("period")) { - // return System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + clearAt.time.toLong() - // } else if (clearAt.type.equals("end-of")) { - // if (clearAt.time.equals("day")) { - // val date = Calendar.getInstance().apply { - // set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) - // set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) - // set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) - // } - // return date.timeInMillis / ONE_SECOND_IN_MILLIS - // } - // } - // } - // - // return -1 - // } - // - // private fun openEmojiPopup() { - // popup.show() - // } - // - // private fun clearStatus() { - // asyncRunner.postQuickTask( - // ClearStatusTask(accountManager.currentOwnCloudAccount?.savedAccount, context), - // { dismiss(it) } - // ) - // } - // - // private fun setStatus(statusType: StatusType) { - // visualizeStatus(statusType) - // - // asyncRunner.postQuickTask( - // SetStatusTask( - // statusType, - // accountManager.currentOwnCloudAccount?.savedAccount, - // context - // ), - // { - // if (!it) { - // clearTopStatus() - // } - // }, - // { clearTopStatus() } - // ) - // } - // - // private fun visualizeStatus(statusType: StatusType) { - // when (statusType) { - // StatusType.ONLINE -> { - // clearTopStatus() - // binding.onlineStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) - // } - // StatusType.AWAY -> { - // clearTopStatus() - // binding.awayStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) - // } - // StatusType.DND -> { - // clearTopStatus() - // binding.dndStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) - // } - // StatusType.INVISIBLE -> { - // clearTopStatus() - // binding.invisibleStatus.setBackgroundColor(ThemeColorUtils.primaryColor(context)) - // } - // else -> clearTopStatus() - // } - // } - // - // private fun clearTopStatus() { - // context?.let { - // val grey = it.resources.getColor(R.color.grey_200) - // binding.onlineStatus.setBackgroundColor(grey) - // binding.awayStatus.setBackgroundColor(grey) - // binding.dndStatus.setBackgroundColor(grey) - // binding.invisibleStatus.setBackgroundColor(grey) - // } - // } - // - // private fun setStatusMessage() { - // if (selectedPredefinedMessageId != null) { - // asyncRunner.postQuickTask( - // SetPredefinedCustomStatusTask( - // selectedPredefinedMessageId!!, - // clearAt, - // accountManager.currentOwnCloudAccount?.savedAccount, - // context - // ), - // { dismiss(it) } - // ) - // } else { - // asyncRunner.postQuickTask( - // SetUserDefinedCustomStatusTask( - // binding.customStatusInput.text.toString(), - // binding.emoji.text.toString(), - // clearAt, - // accountManager.currentOwnCloudAccount?.savedAccount, - // context - // ), - // { dismiss(it) } - // ) + @Suppress("ComplexMethod") + private fun setClearStatusAfterValue(item: Int) { + when (item) { + POS_DONT_CLEAR -> { + // don't clear + clearAt = null + } + + POS_HALF_AN_HOUR -> { + // 30 minutes + clearAt = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS + } + + POS_AN_HOUR -> { + // one hour + clearAt = + System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS + } + + POS_FOUR_HOURS -> { + // four hours + clearAt = + System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + +FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS + } + + POS_TODAY -> { + // today + val date = Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + } + clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS + } + + POS_END_OF_WEEK -> { + // end of week + val date = Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + } + + while (date.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) { + date.add(Calendar.DAY_OF_YEAR, 1) + } + + clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS + } + } + } + + @Suppress("ReturnCount") + private fun clearAtToUnixTime(clearAt: ClearAt?): Long { + if (clearAt != null) { + if (clearAt.type.equals("period")) { + return System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + clearAt.time.toLong() + } else if (clearAt.type.equals("end-of")) { + if (clearAt.time.equals("day")) { + val date = Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + } + return date.timeInMillis / ONE_SECOND_IN_MILLIS + } + } + } + + return -1 + } + + private fun openEmojiPopup() { + popup.show() + } + + + private fun clearStatus() { + // asyncRunner.postQuickTask( + // ClearStatusTask(accountManager.currentOwnCloudAccount?.savedAccount, context), + // { dismiss(it) } + // ) + + val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) + ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatus(currentUser?.baseUrl)).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { + override fun onSubscribe(d: Disposable) {} + override fun onNext(statusOverall: GenericOverall) {} + override fun onError(e: Throwable) { + Log.e(logTag, "Error removing attendee from conversation", e) + } + override fun onComplete() { + dismiss() + } + }) + } + + private fun setStatus(statusType: StatusType) { + visualizeStatus(statusType) + + // asyncRunner.postQuickTask( + // SetStatusTask( + // statusType, + // accountManager.currentOwnCloudAccount?.savedAccount, + // context + // ), + // { + // if (!it) { + // clearTopStatus() + // } + // }, + // { clearTopStatus() } + // ) + } + + private fun visualizeStatus(statusType: String) { + StatusType.values().firstOrNull { it.name == statusType.uppercase(Locale.ROOT) }?.let { visualizeStatus(it) } + } + + private fun visualizeStatus(statusType: StatusType) { + when (statusType) { + StatusType.ONLINE -> { + clearTopStatus() + binding.onlineStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + } + StatusType.AWAY -> { + clearTopStatus() + binding.awayStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + } + StatusType.DND -> { + clearTopStatus() + binding.dndStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + } + StatusType.INVISIBLE -> { + clearTopStatus() + binding.invisibleStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + } + else -> clearTopStatus() + } + } + + private fun clearTopStatus() { + context?.let { + val grey = it.resources.getColor(R.color.grey_200) + binding.onlineStatus.setBackgroundColor(grey) + binding.awayStatus.setBackgroundColor(grey) + binding.dndStatus.setBackgroundColor(grey) + binding.invisibleStatus.setBackgroundColor(grey) + } + } + + + private fun setStatusMessage() { + // if (selectedPredefinedMessageId != null) { + // asyncRunner.postQuickTask( + // SetPredefinedCustomStatusTask( + // selectedPredefinedMessageId!!, + // clearAt, + // accountManager.currentOwnCloudAccount?.savedAccount, + // context + // ), + // { dismiss(it) } + // ) + // } else { + // asyncRunner.postQuickTask( + // SetUserDefinedCustomStatusTask( + // binding.customStatusInput.text.toString(), + // binding.emoji.text.toString(), + // clearAt, + // accountManager.currentOwnCloudAccount?.savedAccount, + // context + // ), + // { dismiss(it) } + // ) + // } + } + + // private fun dismiss(boolean: Boolean) { + // if (boolean) { + // dismiss() // } // } - private fun dismiss(boolean: Boolean) { - if (boolean) { - dismiss() - } - } + /** * Fragment creator */ companion object { @JvmStatic - fun newInstance(user: User): SetStatusDialogFragment { + fun newInstance(user: User, status: Status): SetStatusDialogFragment { val args = Bundle() args.putParcelable(ARG_CURRENT_USER_PARAM, user) - //args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) + args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) + val dialogFragment = SetStatusDialogFragment() dialogFragment.arguments = args @@ -379,42 +436,45 @@ class SetStatusDialogFragment : return binding.root } - // override fun onClick(predefinedStatus: PredefinedStatus) { - // selectedPredefinedMessageId = predefinedStatus.id - // clearAt = clearAtToUnixTime(predefinedStatus.clearAt) - // binding.emoji.setText(predefinedStatus.icon) - // binding.customStatusInput.text?.clear() - // binding.customStatusInput.text?.append(predefinedStatus.message) - // - // binding.remainingClearTime.visibility = View.GONE - // binding.clearStatusAfterSpinner.visibility = View.VISIBLE - // binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) - // - // if (predefinedStatus.clearAt == null) { - // binding.clearStatusAfterSpinner.setSelection(0) - // } else { - // val clearAt = predefinedStatus.clearAt!! - // if (clearAt.type.equals("period")) { - // when (clearAt.time) { - // "1800" -> binding.clearStatusAfterSpinner.setSelection(POS_HALF_AN_HOUR) - // "3600" -> binding.clearStatusAfterSpinner.setSelection(POS_AN_HOUR) - // "14400" -> binding.clearStatusAfterSpinner.setSelection(POS_FOUR_HOURS) - // else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR) - // } - // } else if (clearAt.type.equals("end-of")) { - // when (clearAt.time) { - // "day" -> binding.clearStatusAfterSpinner.setSelection(POS_TODAY) - // "week" -> binding.clearStatusAfterSpinner.setSelection(POS_END_OF_WEEK) - // else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR) - // } - // } - // } - // setClearStatusAfterValue(binding.clearStatusAfterSpinner.selectedItemPosition) - // } + override fun onClick(predefinedStatus: PredefinedStatus) { + selectedPredefinedMessageId = predefinedStatus.id + clearAt = clearAtToUnixTime(predefinedStatus.clearAt) + binding.emoji.setText(predefinedStatus.icon) + binding.customStatusInput.text?.clear() + binding.customStatusInput.text?.append(predefinedStatus.message) + + binding.remainingClearTime.visibility = View.GONE + binding.clearStatusAfterSpinner.visibility = View.VISIBLE + binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) + + if (predefinedStatus.clearAt == null) { + binding.clearStatusAfterSpinner.setSelection(0) + } else { + val clearAt = predefinedStatus.clearAt!! + if (clearAt.type.equals("period")) { + when (clearAt.time) { + "1800" -> binding.clearStatusAfterSpinner.setSelection(POS_HALF_AN_HOUR) + "3600" -> binding.clearStatusAfterSpinner.setSelection(POS_AN_HOUR) + "14400" -> binding.clearStatusAfterSpinner.setSelection(POS_FOUR_HOURS) + else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR) + } + } else if (clearAt.type.equals("end-of")) { + when (clearAt.time) { + "day" -> binding.clearStatusAfterSpinner.setSelection(POS_TODAY) + "week" -> binding.clearStatusAfterSpinner.setSelection(POS_END_OF_WEEK) + else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR) + } + } + } + setClearStatusAfterValue(binding.clearStatusAfterSpinner.selectedItemPosition) + } // // @VisibleForTesting // fun setPredefinedStatus(predefinedStatus: ArrayList) { // adapter.list = predefinedStatus // binding.predefinedStatusList.adapter?.notifyDataSetChanged() // } + + + } 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 94ed1ac0d..227f25425 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -424,4 +424,8 @@ public class ApiUtils { public static String getUrlForStatus(String baseUrl) { return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status"; } + + public static String getUrlForStatusMessage(String baseUrl) { + return getUrlForStatus(baseUrl) + "/message"; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index 7309ad701..efcd91436 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -44,6 +44,7 @@ import android.text.SpannableString; import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; +import android.text.format.DateUtils; import android.text.method.LinkMovementMethod; import android.text.style.AbsoluteSizeSpan; import android.text.style.ClickableSpan; @@ -86,6 +87,8 @@ import org.greenrobot.eventbus.EventBus; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.text.DateFormat; +import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; @@ -124,6 +127,8 @@ public class DisplayUtils { private static final String HTTP_PROTOCOL = "http://"; private static final String HTTPS_PROTOCOL = "https://"; + private static final int DATE_TIME_PARTS_SIZE = 2; + public static void setClickableString(String string, String url, TextView textView) { SpannableString spannableString = new SpannableString(string); spannableString.setSpan(new ClickableSpan() { @@ -605,4 +610,66 @@ public class DisplayUtils { return R.string.menu_item_sort_by_name_a_z; } } + + /** + * calculates the relative time string based on the given modification timestamp. + * + * @param context the app's context + * @param modificationTimestamp the UNIX timestamp of the file modification time in milliseconds. + * @return a relative time string + */ + + public static CharSequence getRelativeTimestamp(Context context, long modificationTimestamp, boolean showFuture) { + return getRelativeDateTimeString(context, + modificationTimestamp, + android.text.format.DateUtils.SECOND_IN_MILLIS, + DateUtils.WEEK_IN_MILLIS, + 0, + showFuture); + } + + public static CharSequence getRelativeDateTimeString(Context c, + long time, + long minResolution, + long transitionResolution, + int flags, + boolean showFuture) { + + CharSequence dateString = ""; + + // in Future + if (!showFuture && time > System.currentTimeMillis()) { + return DisplayUtils.unixTimeToHumanReadable(time); + } + // < 60 seconds -> seconds ago + long diff = System.currentTimeMillis() - time; + if (diff > 0 && diff < 60 * 1000 && minResolution == DateUtils.SECOND_IN_MILLIS) { + return c.getString(R.string.secondsAgo); + } else { + dateString = DateUtils.getRelativeDateTimeString(c, time, minResolution, transitionResolution, flags); + } + + String[] parts = dateString.toString().split(","); + if (parts.length == DATE_TIME_PARTS_SIZE) { + if (parts[1].contains(":") && !parts[0].contains(":")) { + return parts[0]; + } else if (parts[0].contains(":") && !parts[1].contains(":")) { + return parts[1]; + } + } + // dateString contains unexpected format. fallback: use relative date time string from android api as is. + return dateString.toString(); + } + + /** + * Converts Unix time to human readable format + * + * @param milliseconds that have passed since 01/01/1970 + * @return The human readable time for the users locale + */ + public static String unixTimeToHumanReadable(long milliseconds) { + Date date = new Date(milliseconds); + DateFormat df = DateFormat.getDateTimeInstance(); + return df.format(date); + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 05c9d63d2..95d700d88 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -288,6 +288,7 @@ 1 hour 4 hours This week + seconds ago Unread mentions From 57a408ac9738f2138a1787e0902819a806ebb7f1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 2 Feb 2022 19:46:07 +0100 Subject: [PATCH 03/56] add predefined statuses Signed-off-by: Marcel Hibbe --- .../adapters/PredefinedStatusClickListener.kt | 2 +- .../adapters/PredefinedStatusListAdapter.kt | 2 +- .../adapters/PredefinedStatusViewHolder.kt | 2 +- .../java/com/nextcloud/talk/api/NcApi.java | 3 + .../talk/models/json/status/ClearAt.kt | 16 +++- .../models/json/status/PredefinedStatus.kt | 3 - .../status/predefined/PredefinedStatus.kt | 53 +++++++++++++ .../predefined/PredefinedStatusOCS.java | 74 +++++++++++++++++++ .../predefined/PredefinedStatusOverall.java | 65 ++++++++++++++++ .../dialog/ChooseAccountDialogFragment.java | 2 - .../talk/ui/dialog/SetStatusDialogFragment.kt | 50 +++++++++---- .../com/nextcloud/talk/utils/ApiUtils.java | 6 ++ 12 files changed, 256 insertions(+), 22 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt index fa2485dcd..cb07e5943 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt @@ -22,7 +22,7 @@ package com.nextcloud.talk.adapters -import com.nextcloud.talk.models.json.status.PredefinedStatus +import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus interface PredefinedStatusClickListener { fun onClick(predefinedStatus: PredefinedStatus) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt index 253304af6..59a8c50c0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt @@ -27,7 +27,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PredefinedStatusBinding -import com.nextcloud.talk.models.json.status.PredefinedStatus +import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus class PredefinedStatusListAdapter( private val clickListener: PredefinedStatusClickListener, diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt index 2671896c5..c8e094bf4 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt @@ -26,7 +26,7 @@ import android.content.Context import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.R import com.nextcloud.talk.databinding.PredefinedStatusBinding -import com.nextcloud.talk.models.json.status.PredefinedStatus +import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus import com.nextcloud.talk.utils.DisplayUtils private const val ONE_SECOND_IN_MILLIS = 1000 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 e693731f3..f247fe9ac 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -451,6 +451,9 @@ public interface NcApi { @GET Observable status(@Header("Authorization") String authorization, @Url String url); + @GET + Observable getPredefinedStatuses(@Header("Authorization") String authorization, @Url String url); + @DELETE Observable statusDeleteMessage(@Header("Authorization") String authorization, @Url String url); diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt index aa6107447..b5a4fbe13 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt @@ -1,4 +1,18 @@ package com.nextcloud.talk.models.json.status +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize -class ClearAt(val type: String, val time: String) \ No newline at end of file +@Parcelize +@JsonObject +data class ClearAt( + @JsonField(name = ["type"]) + var type: String, + @JsonField(name = ["time"]) + var time: String + ) : Parcelable{ + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this("type", "time") +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt deleted file mode 100644 index 0f689df2f..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/PredefinedStatus.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.nextcloud.talk.models.json.status - -class PredefinedStatus(val id: String, val icon: String, val message: String, val clearAt: ClearAt?) \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt new file mode 100644 index 000000000..6cbf4597d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt @@ -0,0 +1,53 @@ +package com.nextcloud.talk.models.json.status.predefined + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.status.ClearAt +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class PredefinedStatus( + @JsonField(name = ["id"]) + var id: String, + @JsonField(name = ["icon"]) + var icon: String, + @JsonField(name = ["message"]) + var message: String, + @JsonField(name = ["clearAt"]) + var clearAt: ClearAt? + ) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this("id", "icon", "message", null) +} + + + + + +// @Parcelize +// @JsonObject +// data class Status( +// @JsonField(name = ["userId"]) +// var userId: String?, +// @JsonField(name = ["message"]) +// var message: String?, +// /* TODO: Change to enum */ +// @JsonField(name = ["messageId"]) +// var messageId: String?, +// @JsonField(name = ["messageIsPredefined"]) +// var messageIsPredefined: Boolean, +// @JsonField(name = ["icon"]) +// var icon: String?, +// @JsonField(name = ["clearAt"]) +// var clearAt: Long = 0, +// /* TODO: Change to enum */ +// @JsonField(name = ["status"]) +// var status: String = "offline", +// @JsonField(name = ["statusIsUserDefined"]) +// var statusIsUserDefined: Boolean +// ) : Parcelable { +// // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' +// constructor() : this(null, null, null, false, null, 0, "offline", false) +// } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java new file mode 100644 index 000000000..e3447b013 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java @@ -0,0 +1,74 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.status.predefined; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser; +import com.nextcloud.talk.models.json.generic.GenericOCS; +import com.nextcloud.talk.models.json.status.Status; + +import java.util.List; +import java.util.Objects; + +import kotlin.jvm.JvmSuppressWildcards; + +@JsonObject +public class PredefinedStatusOCS extends GenericOCS { + @JsonField(name = "data") + List data; + + public List getData() { + return this.data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + PredefinedStatusOCS that = (PredefinedStatusOCS) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), data); + } + + @Override + public String toString() { + return "PredefinedStatusOCS{" + + "data=" + data + + '}'; + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java new file mode 100644 index 000000000..a9ccf8089 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java @@ -0,0 +1,65 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.status.predefined; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.status.StatusOCS; + +import java.util.Objects; + +@JsonObject +public class PredefinedStatusOverall { + @JsonField(name = "ocs") + public PredefinedStatusOCS ocs; + + public PredefinedStatusOCS getOcs() { + return this.ocs; + } + + public void setOcs(PredefinedStatusOCS ocs) { + this.ocs = ocs; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PredefinedStatusOverall that = (PredefinedStatusOverall) o; + return Objects.equals(ocs, that.ocs); + } + + @Override + public int hashCode() { + return Objects.hash(ocs); + } + + @Override + public String toString() { + return "PredefinedStatusOverall{" + + "ocs=" + ocs + + '}'; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index bdca62698..793bc029c 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -56,7 +56,6 @@ import com.nextcloud.talk.utils.database.user.UserUtils; import java.net.CookieManager; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import javax.inject.Inject; @@ -66,7 +65,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import autodagger.AutoInjector; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.reactivex.Observable; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 94ee5aaf0..aacfea605 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -36,6 +36,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector +import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.R import com.nextcloud.talk.adapters.PredefinedStatusClickListener import com.nextcloud.talk.adapters.PredefinedStatusListAdapter @@ -45,9 +46,10 @@ import com.nextcloud.talk.databinding.DialogSetStatusBinding import com.nextcloud.talk.models.database.User import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.status.ClearAt -import com.nextcloud.talk.models.json.status.PredefinedStatus import com.nextcloud.talk.models.json.status.Status import com.nextcloud.talk.models.json.status.StatusType +import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus +import com.nextcloud.talk.models.json.status.predefined.PredefinedStatusOverall import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils import com.vanniktech.emoji.EmojiPopup @@ -55,6 +57,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 java.util.Calendar import java.util.Locale import javax.inject.Inject @@ -88,13 +91,16 @@ class SetStatusDialogFragment : private var currentUser: User? = null private var currentStatus: Status? = null + // private lateinit var accountManager: UserAccountManager - private lateinit var predefinedStatus: ArrayList + // private lateinit var predefinedStatus: ArrayList + val predefinedStatusesList = ArrayList() + private lateinit var adapter: PredefinedStatusListAdapter private var selectedPredefinedMessageId: String? = null private var clearAt: Long? = -1 private lateinit var popup: EmojiPopup - // + // @Inject // lateinit var arbitraryDataProvider: ArbitraryDataProvider // @@ -109,20 +115,37 @@ class SetStatusDialogFragment : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + arguments?.let { currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM) currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) - // val json = arbitraryDataProvider.getValue(currentUser, ArbitraryDataProvider.PREDEFINED_STATUS) + val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) + ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { - // if (json.isNotEmpty()) { - // val myType = object : TypeToken>() {}.type - // predefinedStatus = Gson().fromJson(json, myType) - // } + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(responseBody: ResponseBody) { + val predefinedStatusOverall : PredefinedStatusOverall = LoganSquare.parse(responseBody + .string(), + PredefinedStatusOverall::class.java) + predefinedStatusesList.addAll(predefinedStatusOverall.getOcs().data) + } + + override fun onError(e: Throwable) { + } + + override fun onComplete() {} + + }) } - - // EmojiManager.install(GoogleEmojiProvider()) } @@ -166,9 +189,10 @@ class SetStatusDialogFragment : } adapter = PredefinedStatusListAdapter(this, requireContext()) - if (this::predefinedStatus.isInitialized) { - adapter.list = predefinedStatus - } + + adapter.list = predefinedStatusesList + + binding.predefinedStatusList.adapter = adapter binding.predefinedStatusList.layoutManager = LinearLayoutManager(context) 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 227f25425..8bea73596 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -425,7 +425,13 @@ public class ApiUtils { return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status"; } + public static String getUrlForPredefinedStatuses(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/predefined_statuses"; + } + public static String getUrlForStatusMessage(String baseUrl) { return getUrlForStatus(baseUrl) + "/message"; } + + } From 8117c775a7671344a5802e201ac1820b5f17a0e6 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 4 Feb 2022 10:56:36 +0100 Subject: [PATCH 04/56] notifyDataSetChanged after predefines statuses were loaded Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index aacfea605..ce1b3f19b 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -136,6 +136,8 @@ class SetStatusDialogFragment : .string(), PredefinedStatusOverall::class.java) predefinedStatusesList.addAll(predefinedStatusOverall.getOcs().data) + + adapter.notifyDataSetChanged() } override fun onError(e: Throwable) { From 454c6cd8e670c859dd73d6f614c88191e31e5945 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 4 Feb 2022 15:18:26 +0100 Subject: [PATCH 05/56] add ability to send predefined and custom status Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/api/NcApi.java | 14 +++ .../talk/ui/dialog/SetStatusDialogFragment.kt | 99 +++++++++++++++---- .../com/nextcloud/talk/utils/ApiUtils.java | 7 ++ 3 files changed, 99 insertions(+), 21 deletions(-) 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 f247fe9ac..c35ec464f 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -458,4 +458,18 @@ public interface NcApi { Observable statusDeleteMessage(@Header("Authorization") String authorization, @Url String url); + @FormUrlEncoded + @PUT + Observable setPredefinedStatusMessage(@Header("Authorization") String authorization, + @Url String url, + @Field("messageId") String selectedPredefinedMessageId, + @Field("clearAt") Long clearAt); + + @FormUrlEncoded + @PUT + Observable setCustomStatusMessage(@Header("Authorization") String authorization, + @Url String url, + @Field("statusIcon") String statusIcon, + @Field("message") String message, + @Field("clearAt") Long clearAt); } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index ce1b3f19b..e57bb50ac 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -98,7 +98,7 @@ class SetStatusDialogFragment : private lateinit var adapter: PredefinedStatusListAdapter private var selectedPredefinedMessageId: String? = null - private var clearAt: Long? = -1 + private var clearAt: Long? = null private lateinit var popup: EmojiPopup // @Inject @@ -113,6 +113,8 @@ class SetStatusDialogFragment : @Inject lateinit var ncApi: NcApi + lateinit var credentials: String + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -122,7 +124,7 @@ class SetStatusDialogFragment : currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM) currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) - val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) + credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -408,6 +410,61 @@ class SetStatusDialogFragment : private fun setStatusMessage() { + if (selectedPredefinedMessageId != null) { + + ncApi.setPredefinedStatusMessage( + credentials, + ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl), + selectedPredefinedMessageId, + clearAt) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(t: GenericOverall) { + Log.d(TAG, "PredefinedStatusMessage successfully set") + dismiss() + } + + override fun onError(e: Throwable) { + Log.d(TAG, "failed to set PredefinedStatusMessage", e) + } + + override fun onComplete() {} + + }) + } else { + ncApi.setCustomStatusMessage( + credentials, + ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), + binding.emoji.text.toString(), + binding.customStatusInput.text.toString(), + clearAt) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(t: GenericOverall) { + Log.d(TAG, "CustomStatusMessage successfully set") + dismiss() + } + + override fun onError(e: Throwable) { + Log.d(TAG, "failed to set CustomStatusMessage", e) + } + + override fun onComplete() {} + + }) + } + + // if (selectedPredefinedMessageId != null) { // asyncRunner.postQuickTask( // SetPredefinedCustomStatusTask( @@ -439,25 +496,6 @@ class SetStatusDialogFragment : // } - - /** - * Fragment creator - */ - companion object { - @JvmStatic - fun newInstance(user: User, status: Status): SetStatusDialogFragment { - val args = Bundle() - args.putParcelable(ARG_CURRENT_USER_PARAM, user) - args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) - - - val dialogFragment = SetStatusDialogFragment() - dialogFragment.arguments = args - // dialogFragment.setStyle(STYLE_NORMAL, R.style.Theme_ownCloud_Dialog) - return dialogFragment - } - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return binding.root } @@ -502,5 +540,24 @@ class SetStatusDialogFragment : // } + /** + * Fragment creator + */ + companion object { + private const val TAG = "SetStatusDialogFragment" + + @JvmStatic + fun newInstance(user: User, status: Status): SetStatusDialogFragment { + val args = Bundle() + args.putParcelable(ARG_CURRENT_USER_PARAM, user) + args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) + + + val dialogFragment = SetStatusDialogFragment() + dialogFragment.arguments = args + // dialogFragment.setStyle(STYLE_NORMAL, R.style.Theme_ownCloud_Dialog) + return dialogFragment + } + } } 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 8bea73596..b5cf6ff74 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -434,4 +434,11 @@ public class ApiUtils { } + public static String getUrlForSetPredefinedStatus(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/predefined"; + } + + public static String getUrlForSetCustomStatus(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/custom"; + } } From df5ac647120990324445953240c5d8ffa59f4d50 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 4 Feb 2022 16:17:22 +0100 Subject: [PATCH 06/56] fix to clear the status Signed-off-by: Marcel Hibbe --- .../talk/ui/dialog/SetStatusDialogFragment.kt | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index e57bb50ac..33291ba52 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -335,18 +335,13 @@ class SetStatusDialogFragment : private fun clearStatus() { - // asyncRunner.postQuickTask( - // ClearStatusTask(accountManager.currentOwnCloudAccount?.savedAccount, context), - // { dismiss(it) } - // ) - val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) - ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatus(currentUser?.baseUrl)).subscribeOn(Schedulers.io()) + ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl)).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { override fun onSubscribe(d: Disposable) {} override fun onNext(statusOverall: GenericOverall) {} override fun onError(e: Throwable) { - Log.e(logTag, "Error removing attendee from conversation", e) + Log.e(logTag, "Failed to clear status", e) } override fun onComplete() { dismiss() @@ -463,30 +458,6 @@ class SetStatusDialogFragment : }) } - - - // if (selectedPredefinedMessageId != null) { - // asyncRunner.postQuickTask( - // SetPredefinedCustomStatusTask( - // selectedPredefinedMessageId!!, - // clearAt, - // accountManager.currentOwnCloudAccount?.savedAccount, - // context - // ), - // { dismiss(it) } - // ) - // } else { - // asyncRunner.postQuickTask( - // SetUserDefinedCustomStatusTask( - // binding.customStatusInput.text.toString(), - // binding.emoji.text.toString(), - // clearAt, - // accountManager.currentOwnCloudAccount?.savedAccount, - // context - // ), - // { dismiss(it) } - // ) - // } } // private fun dismiss(boolean: Boolean) { From 7cc8751a1f95a8533e536c763870e3f24471b9e4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 7 Feb 2022 12:56:00 +0100 Subject: [PATCH 07/56] disable set status button until status is received Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java | 4 +++- app/src/main/res/layout/dialog_choose_account.xml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 793bc029c..873fde07c 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -157,10 +157,11 @@ public class ChooseAccountDialogFragment extends DialogFragment { binding.setStatus.setOnClickListener(v -> { dismiss(); - // TODO: better solution if(status != null) { SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user, status); setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status"); + } else { + Log.w(TAG, "status was null"); } }); @@ -212,6 +213,7 @@ public class ChooseAccountDialogFragment extends DialogFragment { @Override public void onNext(@NonNull StatusOverall statusOverall) { status = statusOverall.ocs.data; + binding.setStatus.setEnabled(true); } @Override diff --git a/app/src/main/res/layout/dialog_choose_account.xml b/app/src/main/res/layout/dialog_choose_account.xml index 702a7392a..2b3337110 100644 --- a/app/src/main/res/layout/dialog_choose_account.xml +++ b/app/src/main/res/layout/dialog_choose_account.xml @@ -62,6 +62,7 @@ android:textAlignment="textStart" android:textAllCaps="false" android:textColor="@color/fontAppbar" + android:enabled="false" app:icon="@drawable/ic_edit" app:iconGravity="start" app:iconPadding="22dp" From 967b63b0179f89007a40118d94d5a64d0750d9b1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 7 Feb 2022 15:10:03 +0100 Subject: [PATCH 08/56] draw status in ChooseAccountDialogFragment Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/ui/StatusDrawable.java | 139 ++++++++++++++++++ .../dialog/ChooseAccountDialogFragment.java | 22 +++ 2 files changed, 161 insertions(+) create mode 100644 app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java diff --git a/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java b/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java new file mode 100644 index 000000000..fc8e69ce3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java @@ -0,0 +1,139 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.ui; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; + +import com.nextcloud.talk.R; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.core.content.res.ResourcesCompat; + +/** + * A Drawable object that draws a status + */ +public class StatusDrawable extends Drawable { + private String text; + private @DrawableRes int icon = -1; + private Paint textPaint; + private Paint backgroundPaint; + private final float radius; + private Context context; + private final static int whiteBackground = Color.argb(200, 255, 255, 255); + private final static int onlineStatus = Color.argb(255, 73, 179, 130); + + public StatusDrawable(String status, String statusIcon, float statusSize, Context context) { + backgroundPaint = new Paint(); + backgroundPaint.setStyle(Paint.Style.FILL); + backgroundPaint.setAntiAlias(true); + + radius = statusSize; + + if (TextUtils.isEmpty(statusIcon)) { + switch (status) { + case "dnd": + icon = R.drawable.ic_user_status_dnd; + backgroundPaint.setColor(whiteBackground); + this.context = context; + break; + + case "online": + backgroundPaint.setColor(onlineStatus); + break; + + case "away": + icon = R.drawable.ic_user_status_away; + backgroundPaint.setColor(whiteBackground); + this.context = context; + break; + + default: + // do not show + backgroundPaint = null; + break; + } + } else { + text = statusIcon; + + backgroundPaint.setColor(whiteBackground); + + textPaint = new Paint(); + textPaint.setColor(Color.WHITE); + textPaint.setTextSize(statusSize); + textPaint.setAntiAlias(true); + textPaint.setTextAlign(Paint.Align.CENTER); + } + } + + /** + * Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color + * filter (set via setColorFilter) a circular background with a user's first character. + * + * @param canvas The canvas to draw into + */ + @Override + public void draw(@NonNull Canvas canvas) { + if (backgroundPaint != null) { + canvas.drawCircle(radius, radius, radius, backgroundPaint); + } + + if (text != null) { + textPaint.setTextSize(1.6f * radius); + canvas.drawText(text, radius, radius - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint); + } + + if (icon != -1) { + Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), icon, null); + + if (drawable != null) { + drawable.setBounds(0, + 0, + (int) (2 * radius), + (int) (2 * radius)); + drawable.draw(canvas); + } + } + } + + @Override + public void setAlpha(int alpha) { + textPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + textPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 873fde07c..4035a3195 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -49,6 +49,7 @@ import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.models.json.status.Status; import com.nextcloud.talk.models.json.status.StatusOverall; +import com.nextcloud.talk.ui.StatusDrawable; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -74,6 +75,8 @@ import io.reactivex.schedulers.Schedulers; public class ChooseAccountDialogFragment extends DialogFragment { private static final String TAG = ChooseAccountDialogFragment.class.getSimpleName(); + private static final float STATUS_SIZE_IN_DP = 9f; + @Inject UserUtils userUtils; @@ -214,6 +217,7 @@ public class ChooseAccountDialogFragment extends DialogFragment { public void onNext(@NonNull StatusOverall statusOverall) { status = statusOverall.ocs.data; binding.setStatus.setEnabled(true); + drawStatus(); } @Override @@ -300,5 +304,23 @@ public class ChooseAccountDialogFragment extends DialogFragment { } }; + private void drawStatus() { + float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, getContext()); + binding.currentAccount.ticker.setBackground(null); + binding.currentAccount.ticker.setImageDrawable(new StatusDrawable( + status.getStatus(), + status.getIcon(), + size, + getContext())); + binding.currentAccount.ticker.setVisibility(View.VISIBLE); + + if (status.getMessage() != null && !status.getMessage().isEmpty()){ + binding.currentAccount.status.setText(status.getMessage()); + binding.currentAccount.status.setVisibility(View.VISIBLE); + } else { + binding.currentAccount.status.setText(""); + binding.currentAccount.status.setVisibility(View.GONE); + } + } } From 45028c3bd466a2bb118e0de37939f363bc8200e6 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 7 Feb 2022 17:38:08 +0100 Subject: [PATCH 09/56] fix colors for online status Signed-off-by: Marcel Hibbe --- app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 6b62f1190..6d8a27370 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -67,4 +67,5 @@ #282828 #222222 + #818181 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index a152cde85..6e29db17a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -98,10 +98,6 @@ #99121212 #eeeeee - #818181 - #D6D7D7 - #000000 - #007cc2 - #ffffff + #EEEEEE From dad53a77e665aa9281bbc1b91b33db365ffb3c12 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 7 Feb 2022 18:27:00 +0100 Subject: [PATCH 10/56] fix wrong calculation for "in 4h" (=remove linebreak) Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 33291ba52..f70419fc5 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -278,8 +278,8 @@ class SetStatusDialogFragment : POS_FOUR_HOURS -> { // four hours clearAt = - System.currentTimeMillis() / ONE_SECOND_IN_MILLIS - +FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS + System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + FOUR_HOURS * ONE_MINUTE_IN_SECONDS * + ONE_MINUTE_IN_SECONDS } POS_TODAY -> { From 622eaa1d7f1b3d54052cdda627d0149e20a642d0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 7 Feb 2022 19:10:13 +0100 Subject: [PATCH 11/56] check capabilities to set status support Signed-off-by: Marcel Hibbe --- .../dialog/ChooseAccountDialogFragment.java | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 4035a3195..b1b585866 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -139,7 +139,6 @@ public class ChooseAccountDialogFragment extends DialogFragment { binding.currentAccount.userIcon.setVisibility(View.INVISIBLE); } - loadCurrentStatus(user); } @@ -168,10 +167,6 @@ public class ChooseAccountDialogFragment extends DialogFragment { } }); - if (CapabilitiesUtil.isUserStatusAvailable(userUtils.getCurrentUser())) { - binding.statusView.setVisibility(View.VISIBLE); - } - if (adapter == null) { adapter = new FlexibleAdapter<>(userItems, getActivity(), false); @@ -205,30 +200,36 @@ public class ChooseAccountDialogFragment extends DialogFragment { private void loadCurrentStatus(User user) { String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); - ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())). - subscribeOn(Schedulers.io()). - observeOn(AndroidSchedulers.mainThread()). - subscribe(new Observer() { - @Override - public void onSubscribe(@NonNull Disposable d) {} + if (CapabilitiesUtil.isUserStatusAvailable(userUtils.getCurrentUser())) { + binding.statusView.setVisibility(View.VISIBLE); - @Override - public void onNext(@NonNull StatusOverall statusOverall) { - status = statusOverall.ocs.data; - binding.setStatus.setEnabled(true); - drawStatus(); - } + ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())). + subscribeOn(Schedulers.io()). + observeOn(AndroidSchedulers.mainThread()). + subscribe(new Observer() { - @Override - public void onError(@NonNull Throwable e) { - Log.e(TAG, "Can't receive user status from server. ", e); - } + @Override + public void onSubscribe(@NonNull Disposable d) {} - @Override - public void onComplete() { - } - }); + @Override + public void onNext(@NonNull StatusOverall statusOverall) { + status = statusOverall.ocs.data; + + binding.setStatus.setEnabled(true); + drawStatus(); + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e(TAG, "Can't receive user status from server. ", e); + } + + @Override + public void onComplete() { + } + }); + } } private void prepareViews() { From 19060bddbd57f946972a8fc2e00860c46bf7cc77 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 8 Feb 2022 11:11:39 +0100 Subject: [PATCH 12/56] delete comments Signed-off-by: Marcel Hibbe --- .../status/predefined/PredefinedStatus.kt | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt index 6cbf4597d..89c2bb08e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt @@ -21,33 +21,3 @@ data class PredefinedStatus( // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this("id", "icon", "message", null) } - - - - - -// @Parcelize -// @JsonObject -// data class Status( -// @JsonField(name = ["userId"]) -// var userId: String?, -// @JsonField(name = ["message"]) -// var message: String?, -// /* TODO: Change to enum */ -// @JsonField(name = ["messageId"]) -// var messageId: String?, -// @JsonField(name = ["messageIsPredefined"]) -// var messageIsPredefined: Boolean, -// @JsonField(name = ["icon"]) -// var icon: String?, -// @JsonField(name = ["clearAt"]) -// var clearAt: Long = 0, -// /* TODO: Change to enum */ -// @JsonField(name = ["status"]) -// var status: String = "offline", -// @JsonField(name = ["statusIsUserDefined"]) -// var statusIsUserDefined: Boolean -// ) : Parcelable { -// // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' -// constructor() : this(null, null, null, false, null, 0, "offline", false) -// } \ No newline at end of file From 45cfbc7257776bd4a543f7662ba097df1118b93d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 8 Feb 2022 12:25:30 +0100 Subject: [PATCH 13/56] add ability to set status type Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/api/NcApi.java | 6 +++++ .../talk/ui/dialog/SetStatusDialogFragment.kt | 27 ++++++++++--------- .../com/nextcloud/talk/utils/ApiUtils.java | 4 +++ 3 files changed, 24 insertions(+), 13 deletions(-) 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 c35ec464f..b7bfb7bc8 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -472,4 +472,10 @@ public interface NcApi { @Field("statusIcon") String statusIcon, @Field("message") String message, @Field("clearAt") Long clearAt); + + @FormUrlEncoded + @PUT + Observable setStatusType(@Header("Authorization") String authorization, + @Url String url, + @Field("statusType") String statusType); } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index f70419fc5..546e36e43 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -352,19 +352,20 @@ class SetStatusDialogFragment : private fun setStatus(statusType: StatusType) { visualizeStatus(statusType) - // asyncRunner.postQuickTask( - // SetStatusTask( - // statusType, - // accountManager.currentOwnCloudAccount?.savedAccount, - // context - // ), - // { - // if (!it) { - // clearTopStatus() - // } - // }, - // { clearTopStatus() } - // ) + ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl), statusType.string) + .subscribeOn(Schedulers + .io()) + .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { + override fun onSubscribe(d: Disposable) {} + override fun onNext(statusOverall: GenericOverall) { + Log.d(TAG, "statusType successfully set") + } + override fun onError(e: Throwable) { + Log.e(logTag, "Failed to set statusType", e) + clearTopStatus() + } + override fun onComplete() {} + }) } private fun visualizeStatus(statusType: String) { 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 b5cf6ff74..c8781fcae 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -425,6 +425,10 @@ public class ApiUtils { return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status"; } + public static String getUrlForSetStatusType(String baseUrl) { + return getUrlForStatus(baseUrl) + "/status"; + } + public static String getUrlForPredefinedStatuses(String baseUrl) { return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/predefined_statuses"; } From ea634ff43c62424c1fce9df2d80e9a1347e4d766 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 8 Feb 2022 15:59:32 +0100 Subject: [PATCH 14/56] delete comments Signed-off-by: Marcel Hibbe --- .../talk/ui/dialog/SetStatusDialogFragment.kt | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 546e36e43..edb58cea8 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -92,8 +92,6 @@ class SetStatusDialogFragment : private var currentUser: User? = null private var currentStatus: Status? = null - // private lateinit var accountManager: UserAccountManager - // private lateinit var predefinedStatus: ArrayList val predefinedStatusesList = ArrayList() private lateinit var adapter: PredefinedStatusListAdapter @@ -101,15 +99,6 @@ class SetStatusDialogFragment : private var clearAt: Long? = null private lateinit var popup: EmojiPopup - // @Inject - // lateinit var arbitraryDataProvider: ArbitraryDataProvider - // - // @Inject - // lateinit var asyncRunner: AsyncRunner - // - // @Inject - // lateinit var clientFactory: ClientFactory - @Inject lateinit var ncApi: NcApi @@ -166,9 +155,6 @@ class SetStatusDialogFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - // accountManager = (activity as BaseActivity).userAccountManager - // currentStatus?.let { binding.emoji.setText(it.icon) binding.customStatusInput.text?.clear() @@ -246,14 +232,8 @@ class SetStatusDialogFragment : binding.clearStatus.setTextColor(resources.getColor(R.color.colorPrimary)) binding.setStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) - // ThemeButtonUtils.colorPrimaryButton(binding.setStatus, context) binding.customStatusInput.highlightColor = resources.getColor(R.color.colorPrimary) - // ThemeTextInputUtils.colorTextInput( - // binding.customStatusInputContainer, - // binding.customStatusInput, - // ThemeColorUtils.primaryColor(activity) - // ) } @Suppress("ComplexMethod") @@ -404,7 +384,6 @@ class SetStatusDialogFragment : } } - private fun setStatusMessage() { if (selectedPredefinedMessageId != null) { @@ -461,13 +440,6 @@ class SetStatusDialogFragment : } } - // private fun dismiss(boolean: Boolean) { - // if (boolean) { - // dismiss() - // } - // } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return binding.root } @@ -504,13 +476,6 @@ class SetStatusDialogFragment : } setClearStatusAfterValue(binding.clearStatusAfterSpinner.selectedItemPosition) } - // - // @VisibleForTesting - // fun setPredefinedStatus(predefinedStatus: ArrayList) { - // adapter.list = predefinedStatus - // binding.predefinedStatusList.adapter?.notifyDataSetChanged() - // } - /** * Fragment creator From c87940e115d8c646742a77c544315d38ab07a19c Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 16 Feb 2022 15:18:41 +0100 Subject: [PATCH 15/56] fix seperator color for in account dialog Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_choose_account.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_choose_account.xml b/app/src/main/res/layout/dialog_choose_account.xml index 2b3337110..955f0fbf2 100644 --- a/app/src/main/res/layout/dialog_choose_account.xml +++ b/app/src/main/res/layout/dialog_choose_account.xml @@ -74,7 +74,7 @@ android:layout_width="0dp" android:layout_height="1dp" android:layout_marginTop="4dp" - android:background="@color/controller_chat_separator" + android:background="@color/list_divider_background" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/statusView" /> From bce0d276f3d54829dec9ab957cfeca28d0c90c50 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 16 Feb 2022 20:53:07 +0100 Subject: [PATCH 16/56] fix to be able to modify status message after predefined status was selected so api to set a predefined status is not used at all.. Signed-off-by: Marcel Hibbe --- .../talk/ui/dialog/SetStatusDialogFragment.kt | 69 ++++++------------- .../com/nextcloud/talk/utils/ApiUtils.java | 5 -- 2 files changed, 20 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index edb58cea8..95c9bdb05 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -385,59 +385,31 @@ class SetStatusDialogFragment : } private fun setStatusMessage() { - if (selectedPredefinedMessageId != null) { + ncApi.setCustomStatusMessage( + credentials, + ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), + binding.emoji.text.toString(), + binding.customStatusInput.text.toString(), + clearAt) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { - ncApi.setPredefinedStatusMessage( - credentials, - ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl), - selectedPredefinedMessageId, - clearAt) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + } - override fun onSubscribe(d: Disposable) { - } + override fun onNext(t: GenericOverall) { + Log.d(TAG, "CustomStatusMessage successfully set") + dismiss() + } - override fun onNext(t: GenericOverall) { - Log.d(TAG, "PredefinedStatusMessage successfully set") - dismiss() - } + override fun onError(e: Throwable) { + Log.d(TAG, "failed to set CustomStatusMessage", e) + } - override fun onError(e: Throwable) { - Log.d(TAG, "failed to set PredefinedStatusMessage", e) - } + override fun onComplete() {} - override fun onComplete() {} - - }) - } else { - ncApi.setCustomStatusMessage( - credentials, - ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), - binding.emoji.text.toString(), - binding.customStatusInput.text.toString(), - clearAt) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - - override fun onSubscribe(d: Disposable) { - } - - override fun onNext(t: GenericOverall) { - Log.d(TAG, "CustomStatusMessage successfully set") - dismiss() - } - - override fun onError(e: Throwable) { - Log.d(TAG, "failed to set CustomStatusMessage", e) - } - - override fun onComplete() {} - - }) - } + }) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -445,7 +417,6 @@ class SetStatusDialogFragment : } override fun onClick(predefinedStatus: PredefinedStatus) { - selectedPredefinedMessageId = predefinedStatus.id clearAt = clearAtToUnixTime(predefinedStatus.clearAt) binding.emoji.setText(predefinedStatus.icon) binding.customStatusInput.text?.clear() 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 c8781fcae..713a52e5d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -437,11 +437,6 @@ public class ApiUtils { return getUrlForStatus(baseUrl) + "/message"; } - - public static String getUrlForSetPredefinedStatus(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/predefined"; - } - public static String getUrlForSetCustomStatus(String baseUrl) { return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/custom"; } From 546f3fe1f2f577318353ffb5f29f4281d88e27ea Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 17 Feb 2022 10:29:46 +0100 Subject: [PATCH 17/56] fix to be able to set only "icon" / only "clear at" for status api seems to set single modification for icon or "clear at" only if status message is not empty (must be at least whitespace) Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 95c9bdb05..2a9b66bf5 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -385,11 +385,16 @@ class SetStatusDialogFragment : } private fun setStatusMessage() { + var inputText = binding.customStatusInput.text.toString() + if (inputText.isEmpty()){ + inputText = " " + } + ncApi.setCustomStatusMessage( credentials, ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), binding.emoji.text.toString(), - binding.customStatusInput.text.toString(), + inputText, clearAt) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) From e27ede75ff3c8e2d96c61e8f431cce93361e6eab Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 17 Feb 2022 12:12:48 +0100 Subject: [PATCH 18/56] tidy up Signed-off-by: Marcel Hibbe --- .../ConversationsListController.java | 6 --- .../dialog/ChooseAccountDialogFragment.java | 7 +-- .../talk/ui/dialog/SetStatusDialogFragment.kt | 45 +++++++++---------- 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 0c3b5f707..19a1808cf 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -64,7 +64,6 @@ import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.request.ImageRequest; import com.google.android.material.button.MaterialButton; import com.google.android.material.floatingactionbutton.FloatingActionButton; - import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MainActivity; import com.nextcloud.talk.adapters.items.ConversationItem; @@ -72,8 +71,6 @@ import com.nextcloud.talk.adapters.items.GenericTextHeaderItem; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum; -import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController; import com.nextcloud.talk.events.ConversationsListFetchDataEvent; import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.interfaces.ConversationMenuInterface; @@ -84,7 +81,6 @@ import com.nextcloud.talk.jobs.UploadAndShareFilesWorker; import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.ui.dialog.ChooseAccountDialogFragment; import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog; import com.nextcloud.talk.utils.ApiUtils; @@ -191,8 +187,6 @@ public class ConversationsListController extends BaseController implements Searc private SearchView searchView; private String searchQuery; - private View view; - private String credentials; private boolean adapterWasNull = true; diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index b1b585866..e40e55ee9 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -159,7 +159,7 @@ public class ChooseAccountDialogFragment extends DialogFragment { binding.setStatus.setOnClickListener(v -> { dismiss(); - if(status != null) { + if (status != null) { SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user, status); setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status"); } else { @@ -210,7 +210,8 @@ public class ChooseAccountDialogFragment extends DialogFragment { subscribe(new Observer() { @Override - public void onSubscribe(@NonNull Disposable d) {} + public void onSubscribe(@NonNull Disposable d) { + } @Override public void onNext(@NonNull StatusOverall statusOverall) { @@ -316,7 +317,7 @@ public class ChooseAccountDialogFragment extends DialogFragment { binding.currentAccount.ticker.setVisibility(View.VISIBLE); - if (status.getMessage() != null && !status.getMessage().isEmpty()){ + if (status.getMessage() != null && !status.getMessage().isEmpty()) { binding.currentAccount.status.setText(status.getMessage()); binding.currentAccount.status.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 2a9b66bf5..63f8264d0 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -86,7 +86,6 @@ class SetStatusDialogFragment : private val logTag = ChooseAccountDialogFragment::class.java.simpleName - private lateinit var binding: DialogSetStatusBinding private var currentUser: User? = null @@ -95,7 +94,6 @@ class SetStatusDialogFragment : val predefinedStatusesList = ArrayList() private lateinit var adapter: PredefinedStatusListAdapter - private var selectedPredefinedMessageId: String? = null private var clearAt: Long? = null private lateinit var popup: EmojiPopup @@ -123,9 +121,11 @@ class SetStatusDialogFragment : } override fun onNext(responseBody: ResponseBody) { - val predefinedStatusOverall : PredefinedStatusOverall = LoganSquare.parse(responseBody + val predefinedStatusOverall: PredefinedStatusOverall = LoganSquare.parse( + responseBody .string(), - PredefinedStatusOverall::class.java) + PredefinedStatusOverall::class.java + ) predefinedStatusesList.addAll(predefinedStatusOverall.getOcs().data) adapter.notifyDataSetChanged() @@ -135,11 +135,8 @@ class SetStatusDialogFragment : } override fun onComplete() {} - }) } - - // EmojiManager.install(GoogleEmojiProvider()) } @SuppressLint("InflateParams") @@ -179,10 +176,8 @@ class SetStatusDialogFragment : } adapter = PredefinedStatusListAdapter(this, requireContext()) - adapter.list = predefinedStatusesList - binding.predefinedStatusList.adapter = adapter binding.predefinedStatusList.layoutManager = LinearLayoutManager(context) @@ -310,19 +305,20 @@ class SetStatusDialogFragment : } private fun openEmojiPopup() { - popup.show() + popup.show() } - private fun clearStatus() { val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) - ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl)).subscribeOn(Schedulers.io()) + ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl)) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { override fun onSubscribe(d: Disposable) {} override fun onNext(statusOverall: GenericOverall) {} override fun onError(e: Throwable) { Log.e(logTag, "Failed to clear status", e) } + override fun onComplete() { dismiss() } @@ -333,17 +329,21 @@ class SetStatusDialogFragment : visualizeStatus(statusType) ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl), statusType.string) - .subscribeOn(Schedulers - .io()) + .subscribeOn( + Schedulers + .io() + ) .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { override fun onSubscribe(d: Disposable) {} override fun onNext(statusOverall: GenericOverall) { - Log.d(TAG, "statusType successfully set") + Log.d(logTag, "statusType successfully set") } + override fun onError(e: Throwable) { Log.e(logTag, "Failed to set statusType", e) clearTopStatus() } + override fun onComplete() {} }) } @@ -386,7 +386,7 @@ class SetStatusDialogFragment : private fun setStatusMessage() { var inputText = binding.customStatusInput.text.toString() - if (inputText.isEmpty()){ + if (inputText.isEmpty()) { inputText = " " } @@ -395,7 +395,8 @@ class SetStatusDialogFragment : ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), binding.emoji.text.toString(), inputText, - clearAt) + clearAt + ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) ?.subscribe(object : Observer { @@ -404,16 +405,15 @@ class SetStatusDialogFragment : } override fun onNext(t: GenericOverall) { - Log.d(TAG, "CustomStatusMessage successfully set") + Log.d(logTag, "CustomStatusMessage successfully set") dismiss() } override fun onError(e: Throwable) { - Log.d(TAG, "failed to set CustomStatusMessage", e) + Log.e(logTag, "failed to set CustomStatusMessage", e) } override fun onComplete() {} - }) } @@ -457,20 +457,15 @@ class SetStatusDialogFragment : * Fragment creator */ companion object { - private const val TAG = "SetStatusDialogFragment" - @JvmStatic fun newInstance(user: User, status: Status): SetStatusDialogFragment { val args = Bundle() args.putParcelable(ARG_CURRENT_USER_PARAM, user) args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) - val dialogFragment = SetStatusDialogFragment() dialogFragment.arguments = args - // dialogFragment.setStyle(STYLE_NORMAL, R.style.Theme_ownCloud_Dialog) return dialogFragment } } - } From 6057306ab3fa852897dd832ea2c2e8b58e8cb153 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 17 Feb 2022 20:43:59 +0100 Subject: [PATCH 19/56] show user statuses in conversation list Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/ConversationItem.java | 79 +++++++++++++------ .../java/com/nextcloud/talk/api/NcApi.java | 5 ++ .../ConversationsListController.java | 45 ++++++++++- .../models/json/statuses/StatusesOCS.java | 71 +++++++++++++++++ .../models/json/statuses/StatusesOverall.java | 64 +++++++++++++++ .../com/nextcloud/talk/utils/ApiUtils.java | 4 + .../ic_user_status_away_with_border.xml | 43 ++++++++++ .../ic_user_status_dnd_with_border.xml | 41 ++++++++++ .../drawable/online_status_with_border.xml | 26 ++++++ ...rv_item_conversation_with_last_message.xml | 18 +++++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 370 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOverall.java create mode 100644 app/src/main/res/drawable/ic_user_status_away_with_border.xml create mode 100644 app/src/main/res/drawable/ic_user_status_dnd_with_border.xml create mode 100644 app/src/main/res/drawable/online_status_with_border.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index f63b1f3cf..bf171f4d2 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -46,6 +46,8 @@ import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.status.Status; +import com.nextcloud.talk.models.json.status.StatusType; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; @@ -66,26 +68,29 @@ import eu.davidea.flexibleadapter.utils.FlexibleUtils; import eu.davidea.viewholders.FlexibleViewHolder; public class ConversationItem extends AbstractFlexibleItem implements ISectionable, - IFilterable { + IFilterable { private Conversation conversation; private UserEntity userEntity; private Context context; private GenericTextHeaderItem header; + private Status status; - public ConversationItem(Conversation conversation, UserEntity userEntity, Context activityContext) { + public ConversationItem(Conversation conversation, UserEntity userEntity, Context activityContext, Status status) { this.conversation = conversation; this.userEntity = userEntity; this.context = activityContext; + this.status = status; } public ConversationItem(Conversation conversation, UserEntity userEntity, - Context activityContext, GenericTextHeaderItem genericTextHeaderItem) { + Context activityContext, GenericTextHeaderItem genericTextHeaderItem, Status status) { this.conversation = conversation; this.userEntity = userEntity; this.context = activityContext; this.header = genericTextHeaderItem; + this.status = status; } @Override @@ -120,7 +125,7 @@ public class ConversationItem extends AbstractFlexibleItem adapter, ConversationItemViewHolder holder, int position, List payloads) { Context appContext = - NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); + NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); holder.dialogAvatar.setController(null); holder.dialogName.setTextColor(ResourcesCompat.getColor(context.getResources(), @@ -129,8 +134,8 @@ public class ConversationItem extends AbstractFlexibleItem setStatusType(@Header("Authorization") String authorization, @Url String url, @Field("statusType") String statusType); + + @GET + Observable getUserStatuses(@Header("Authorization") String authorization, @Url String url); + } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 19a1808cf..c45430bed 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -81,6 +81,8 @@ import com.nextcloud.talk.jobs.UploadAndShareFilesWorker; import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.status.Status; +import com.nextcloud.talk.models.json.statuses.StatusesOverall; import com.nextcloud.talk.ui.dialog.ChooseAccountDialogFragment; import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog; import com.nextcloud.talk.utils.ApiUtils; @@ -128,6 +130,7 @@ import butterknife.BindView; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; +import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; @@ -214,6 +217,8 @@ public class ConversationsListController extends BaseController implements Searc private ConversationsListBottomDialog conversationsListBottomDialog; + private HashMap userStatuses = new HashMap<>(); + public ConversationsListController(Bundle bundle) { super(); setHasOptionsMenu(true); @@ -467,6 +472,37 @@ public class ConversationsListController extends BaseController implements Searc @SuppressLint("LongLogTag") public void fetchData() { + fetchUserStatuses(); + } + + private void fetchUserStatuses() { + ncApi.getUserStatuses(credentials, ApiUtils.getUrlForUserStatuses(currentUser.getBaseUrl())) + .subscribe(new Observer() { + @Override + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { + } + + @Override + public void onNext(@NonNull StatusesOverall statusesOverall) { + for (Status status : statusesOverall.getOcs().getData()) { + userStatuses.put(status.getUserId(), status); + } + fetchRooms(); + } + + @Override + public void onError(@io.reactivex.annotations.NonNull Throwable e) { + Log.e(TAG, "failed to fetch user statuses", e); + } + + @Override + public void onComplete() { + } + }); + + } + + private void fetchRooms() { dispose(null); isRefreshing = true; @@ -525,14 +561,16 @@ public class ConversationsListController extends BaseController implements Searc ConversationItem conversationItem = new ConversationItem( conversation, currentUser, - getActivity()); + getActivity(), + userStatuses.get(conversation.name)); conversationItems.add(conversationItem); ConversationItem conversationItemWithHeader = new ConversationItem( conversation, currentUser, getActivity(), - callHeaderItems.get(headerTitle)); + callHeaderItems.get(headerTitle), + userStatuses.get(conversation.name)); conversationItemsWithHeader.add(conversationItemWithHeader); } } @@ -604,7 +642,8 @@ public class ConversationsListController extends BaseController implements Searc conversation, currentUser, getActivity(), - callHeaderItems.get(headerTitle)); + callHeaderItems.get(headerTitle), + userStatuses.get(conversation.name)); openConversationItems.add(conversationItem); } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOCS.java new file mode 100644 index 000000000..31ad65e56 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOCS.java @@ -0,0 +1,71 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.statuses; + +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.status.Status; + +import java.util.List; +import java.util.Objects; + +@JsonObject +public class StatusesOCS extends GenericOCS { + @JsonField(name = "data") + public List data; + + public List getData() { + return this.data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + StatusesOCS that = (StatusesOCS) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), data); + } + + @Override + public String toString() { + return "StatusesOCS{" + + "data=" + data + + '}'; + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOverall.java new file mode 100644 index 000000000..b3a547ecb --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/statuses/StatusesOverall.java @@ -0,0 +1,64 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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.statuses; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import java.util.Objects; + +@JsonObject +public class StatusesOverall { + @JsonField(name = "ocs") + public StatusesOCS ocs; + + public StatusesOCS getOcs() { + return this.ocs; + } + + public void setOcs(StatusesOCS ocs) { + this.ocs = ocs; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StatusesOverall that = (StatusesOverall) o; + return Objects.equals(ocs, that.ocs); + } + + @Override + public int hashCode() { + return Objects.hash(ocs); + } + + @Override + public String toString() { + return "StatusesOverall{" + + "ocs=" + ocs + + '}'; + } +} 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 713a52e5d..89ed1f667 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -440,4 +440,8 @@ public class ApiUtils { public static String getUrlForSetCustomStatus(String baseUrl) { return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/custom"; } + + public static String getUrlForUserStatuses(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/statuses"; + } } diff --git a/app/src/main/res/drawable/ic_user_status_away_with_border.xml b/app/src/main/res/drawable/ic_user_status_away_with_border.xml new file mode 100644 index 000000000..6de7c7bf7 --- /dev/null +++ b/app/src/main/res/drawable/ic_user_status_away_with_border.xml @@ -0,0 +1,43 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_user_status_dnd_with_border.xml b/app/src/main/res/drawable/ic_user_status_dnd_with_border.xml new file mode 100644 index 000000000..69f952f05 --- /dev/null +++ b/app/src/main/res/drawable/ic_user_status_dnd_with_border.xml @@ -0,0 +1,41 @@ + + + + + + diff --git a/app/src/main/res/drawable/online_status_with_border.xml b/app/src/main/res/drawable/online_status_with_border.xml new file mode 100644 index 000000000..2b6fc8dcb --- /dev/null +++ b/app/src/main/res/drawable/online_status_with_border.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml index 21d47f39b..7a3c05001 100644 --- a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml +++ b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml @@ -56,6 +56,24 @@ app:tint="@color/favorite_icon_tint" app:tintMode="src_in" /> + + + Account not found Favorite + Status Encrypted Password protected From d453773a1df31065ec38d5b7ae378e2776ab045e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 18 Feb 2022 15:49:21 +0100 Subject: [PATCH 20/56] show user statuses in conversation info (wip) Signed-off-by: Marcel Hibbe --- .../firebase/MagicFirebaseMessagingService.kt | 2 +- .../talk/activities/CallActivity.java | 2 +- .../activities/CallNotificationActivity.java | 2 +- .../items/MentionAutocompleteItem.java | 8 +- .../talk/adapters/items/UserItem.java | 134 ++++++++++------- .../java/com/nextcloud/talk/api/NcApi.java | 3 +- .../talk/controllers/ContactsController.java | 1 + .../controllers/ConversationInfoController.kt | 24 ++-- .../models/json/participants/Participant.java | 9 ++ .../rv_item_conversation_info_participant.xml | 136 +++++++++++------- ...rv_item_conversation_with_last_message.xml | 3 +- 11 files changed, 206 insertions(+), 118 deletions(-) diff --git a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt index ea55052db..87facd860 100644 --- a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt +++ b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt @@ -272,7 +272,7 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() { apiVersion, signatureVerification.userEntity.baseUrl, decryptedPushMessage.id - ) + ), null ) .repeatWhen { completed -> completed.zipWith(Observable.range(1, 12), { _, i -> i }) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 36f6b9c69..f9ec0ff3b 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -1755,7 +1755,7 @@ public class CallActivity extends CallBaseActivity { Log.d(TAG, "getPeersForCall"); int apiVersion = ApiUtils.getCallApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1}); - ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken)) + ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken), null) .subscribeOn(Schedulers.io()) .subscribe(new Observer() { @Override diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.java index dc3dc0889..97b29f915 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.java @@ -215,7 +215,7 @@ public class CallNotificationActivity extends CallBaseActivity { int apiVersion = ApiUtils.getCallApiVersion(userBeingCalled, new int[]{ApiUtils.APIv4, 1}); ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(apiVersion, userBeingCalled.getBaseUrl(), - currentConversation.getToken())) + currentConversation.getToken()), null) .subscribeOn(Schedulers.io()) .repeatWhen(completed -> completed.zipWith(Observable.range(1, 12), (n, i) -> i) .flatMap(retryCount -> Observable.timer(5, TimeUnit.SECONDS)) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index 2d1fec291..5cf654130 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -135,7 +135,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem implements - ISectionable, IFilterable { + ISectionable, IFilterable { + private Context context; private Participant participant; private UserEntity userEntity; private GenericTextHeaderItem header; public boolean isOnline = true; - public UserItem(Participant participant, UserEntity userEntity, GenericTextHeaderItem genericTextHeaderItem) { + public UserItem(Context activityContext, + Participant participant, + UserEntity userEntity, + GenericTextHeaderItem genericTextHeaderItem) { + this.context = activityContext; this.participant = participant; this.userEntity = userEntity; this.header = genericTextHeaderItem; @@ -72,7 +79,7 @@ public class UserItem extends AbstractFlexibleItem if (o instanceof UserItem) { UserItem inItem = (UserItem) o; return participant.getActorType() == inItem.getModel().getActorType() && - participant.getActorId().equals(inItem.getModel().getActorId()); + participant.getActorId().equals(inItem.getModel().getActorId()); } return false; } @@ -112,7 +119,7 @@ public class UserItem extends AbstractFlexibleItem @Override public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) { - holder.simpleDraweeView.setController(null); + holder.participantAvatar.setController(null); if (holder.checkedImageView != null) { if (participant.isSelected()) { @@ -122,69 +129,88 @@ public class UserItem extends AbstractFlexibleItem } } + if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { + setOnlineStateIcon(holder, R.drawable.ic_user_status_dnd_with_border); + } else if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { + holder.participantOnlineStateImage.setVisibility(View.GONE); + holder.participantEmoji.setVisibility(View.VISIBLE); + holder.participantEmoji.setText(participant.statusIcon); + } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { + setOnlineStateIcon(holder, R.drawable.ic_user_status_away_with_border); + } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { + setOnlineStateIcon(holder, R.drawable.online_status_with_border); + } else { + holder.participantEmoji.setVisibility(View.GONE); + holder.participantOnlineStateImage.setVisibility(View.GONE); + } + + if (participant.statusMessage != null) { + holder.statusMessage.setText(participant.statusMessage); + } + if (!isOnline) { holder.contactDisplayName.setTextColor(ResourcesCompat.getColor( - holder.contactDisplayName.getContext().getResources(), - R.color.medium_emphasis_text, - null) - ); - holder.simpleDraweeView.setAlpha(0.38f); + holder.contactDisplayName.getContext().getResources(), + R.color.medium_emphasis_text, + null) + ); + holder.participantAvatar.setAlpha(0.38f); } else { holder.contactDisplayName.setTextColor(ResourcesCompat.getColor( - holder.contactDisplayName.getContext().getResources(), - R.color.high_emphasis_text, - null) - ); - holder.simpleDraweeView.setAlpha(1.0f); + holder.contactDisplayName.getContext().getResources(), + R.color.high_emphasis_text, + null) + ); + holder.participantAvatar.setAlpha(1.0f); } if (adapter.hasFilter()) { FlexibleUtils.highlightText(holder.contactDisplayName, participant.getDisplayName(), - String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.colorPrimary)); + String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.colorPrimary)); } holder.contactDisplayName.setText(participant.getDisplayName()); if (TextUtils.isEmpty(participant.getDisplayName()) && - (participant.getType().equals(Participant.ParticipantType.GUEST) || participant.getType().equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { + (participant.getType().equals(Participant.ParticipantType.GUEST) || participant.getType().equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { holder.contactDisplayName.setText(NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest)); } if (participant.getActorType() == Participant.ActorType.GROUPS || - "groups".equals(participant.getSource()) || - participant.getActorType() == Participant.ActorType.CIRCLES || - "circles".equals(participant.getSource())) { - holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_group); + "groups".equals(participant.getSource()) || + participant.getActorType() == Participant.ActorType.CIRCLES || + "circles".equals(participant.getSource())) { + holder.participantAvatar.setImageResource(R.drawable.ic_circular_group); } else if (participant.getActorType() == Participant.ActorType.EMAILS) { - holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_mail); + holder.participantAvatar.setImageResource(R.drawable.ic_circular_mail); } else if (participant.getActorType() == Participant.ActorType.GUESTS || - Participant.ParticipantType.GUEST.equals(participant.getType()) || - Participant.ParticipantType.GUEST_MODERATOR.equals(participant.getType())) { + Participant.ParticipantType.GUEST.equals(participant.getType()) || + Participant.ParticipantType.GUEST_MODERATOR.equals(participant.getType())) { String displayName = NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getString(R.string.nc_guest); + .getResources().getString(R.string.nc_guest); if (!TextUtils.isEmpty(participant.getDisplayName())) { displayName = participant.getDisplayName(); } DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.simpleDraweeView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(), - displayName, R.dimen.avatar_size), null)) - .build(); - holder.simpleDraweeView.setController(draweeController); + .setOldController(holder.participantAvatar.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(), + displayName, R.dimen.avatar_size), null)) + .build(); + holder.participantAvatar.setController(draweeController); } else if (participant.getActorType() == Participant.ActorType.USERS || participant.getSource().equals("users")) { DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.simpleDraweeView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - participant.getActorId(), R.dimen.avatar_size), null)) - .build(); - holder.simpleDraweeView.setController(draweeController); + .setOldController(holder.participantAvatar.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + participant.getActorId(), R.dimen.avatar_size), null)) + .build(); + holder.participantAvatar.setController(draweeController); } Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); @@ -195,17 +221,17 @@ public class UserItem extends AbstractFlexibleItem holder.videoCallIconView.setImageResource(R.drawable.ic_call_grey_600_24dp); holder.videoCallIconView.setVisibility(View.VISIBLE); holder.videoCallIconView.setContentDescription( - resources.getString(R.string.nc_call_state_with_phone, participant.displayName)); + resources.getString(R.string.nc_call_state_with_phone, participant.displayName)); } else if ((inCallFlag & InCallFlags.WITH_VIDEO) > 0) { holder.videoCallIconView.setImageResource(R.drawable.ic_videocam_grey_600_24dp); holder.videoCallIconView.setVisibility(View.VISIBLE); holder.videoCallIconView.setContentDescription( - resources.getString(R.string.nc_call_state_with_video, participant.displayName)); + resources.getString(R.string.nc_call_state_with_video, participant.displayName)); } else if (inCallFlag > InCallFlags.DISCONNECTED) { holder.videoCallIconView.setImageResource(R.drawable.ic_mic_grey_600_24dp); holder.videoCallIconView.setVisibility(View.VISIBLE); holder.videoCallIconView.setContentDescription( - resources.getString(R.string.nc_call_state_in_call, participant.displayName)); + resources.getString(R.string.nc_call_state_in_call, participant.displayName)); } else { holder.videoCallIconView.setVisibility(View.GONE); } @@ -250,11 +276,17 @@ public class UserItem extends AbstractFlexibleItem } } + private void setOnlineStateIcon(UserItem.UserItemViewHolder holder, int icon) { + holder.participantEmoji.setVisibility(View.GONE); + holder.participantOnlineStateImage.setVisibility(View.VISIBLE); + holder.participantOnlineStateImage.setImageDrawable(ContextCompat.getDrawable(context, icon)); + } + @Override public boolean filter(String constraint) { return participant.getDisplayName() != null && - (Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getDisplayName().trim()).find() || - Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getActorId().trim()).find()); + (Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getDisplayName().trim()).find() || + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getActorId().trim()).find()); } @Override @@ -271,8 +303,8 @@ public class UserItem extends AbstractFlexibleItem @BindView(R.id.name_text) public EmojiTextView contactDisplayName; - @BindView(R.id.simple_drawee_view) - public SimpleDraweeView simpleDraweeView; + @BindView(R.id.conversation_info_participant_avatar) + public SimpleDraweeView participantAvatar; @Nullable @BindView(R.id.secondary_text) public EmojiTextView contactMentionId; @@ -282,6 +314,12 @@ public class UserItem extends AbstractFlexibleItem @Nullable @BindView(R.id.checkedImageView) ImageView checkedImageView; + @BindView(R.id.conversation_info_participant_emoji) + com.vanniktech.emoji.EmojiEditText participantEmoji; + @BindView(R.id.conversation_info_participant_online_state) + ImageView participantOnlineStateImage; + @BindView(R.id.conversation_info_status_message) + EmojiTextView statusMessage; /** * Default constructor. 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 5a2384d70..688416959 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -187,7 +187,8 @@ public interface NcApi { Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken */ @GET - Observable getPeersForCall(@Header("Authorization") String authorization, @Url String url); + Observable getPeersForCall(@Header("Authorization") String authorization, @Url String url, + @QueryMap Map fields); @FormUrlEncoded @POST 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 3adbb0903..1ca545dfc 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -551,6 +551,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ } UserItem newContactItem = new UserItem( + getApplicationContext(), participant, currentUser, userHeaderItems.get(headerTitle) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 37ab07ee8..b1de2d208 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -88,6 +88,7 @@ import org.greenrobot.eventbus.ThreadMode import java.util.Calendar import java.util.Collections import java.util.Comparator +import java.util.HashMap import java.util.Locale import javax.inject.Inject @@ -120,7 +121,7 @@ class ConversationInfoController(args: Bundle) : private var conversation: Conversation? = null private var adapter: FlexibleAdapter? = null - private var recyclerViewItems: MutableList = ArrayList() + private var userItems: MutableList = ArrayList() private var saveStateHandler: LovelySaveStateHandler? = null @@ -362,7 +363,7 @@ class ConversationInfoController(args: Bundle) : private fun setupAdapter() { if (activity != null) { if (adapter == null) { - adapter = FlexibleAdapter(recyclerViewItems, activity, true) + adapter = FlexibleAdapter(userItems, activity, true) } val layoutManager = SmoothScrollLinearLayoutManager(activity) @@ -378,12 +379,12 @@ class ConversationInfoController(args: Bundle) : var userItem: UserItem var participant: Participant - recyclerViewItems = ArrayList() + userItems = ArrayList() var ownUserItem: UserItem? = null for (i in participants.indices) { participant = participants[i] - userItem = UserItem(participant, conversationUser, null) + userItem = UserItem(context, participant, conversationUser, null) if (participant.sessionId != null) { userItem.isOnline = !participant.sessionId.equals("0") } else { @@ -395,20 +396,20 @@ class ConversationInfoController(args: Bundle) : ownUserItem.model.sessionId = "-1" ownUserItem.isOnline = true } else { - recyclerViewItems.add(userItem) + userItems.add(userItem) } } - Collections.sort(recyclerViewItems, UserItemComparator()) + Collections.sort(userItems, UserItemComparator()) if (ownUserItem != null) { - recyclerViewItems.add(0, ownUserItem) + userItems.add(0, ownUserItem) } setupAdapter() binding.participantsListCategory.visibility = View.VISIBLE - adapter!!.updateDataSet(recyclerViewItems) + adapter!!.updateDataSet(userItems) } override val title: String @@ -426,9 +427,12 @@ class ConversationInfoController(args: Bundle) : apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) } + val fieldMap = HashMap() + fieldMap["includeStatus"] = true + ncApi?.getPeersForCall( credentials, - ApiUtils.getUrlForParticipants(apiVersion, conversationUser!!.baseUrl, conversationToken) + ApiUtils.getUrlForParticipants(apiVersion, conversationUser!!.baseUrl, conversationToken), fieldMap ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) @@ -462,7 +466,7 @@ class ConversationInfoController(args: Bundle) : val bundle = Bundle() val existingParticipantsId = arrayListOf() - for (userItem in recyclerViewItems) { + for (userItem in userItems) { if (userItem.model.getActorType() == USERS) { existingParticipantsId.add(userItem.model.getActorId()) } 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 36dbaa482..9465e0936 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 @@ -78,6 +78,15 @@ public class Participant { @JsonField(name = "inCall") public Object inCall; + @JsonField(name = "status") + public String status; + + @JsonField(name = "statusIcon") + public String statusIcon; + + @JsonField(name = "statusMessage") + public String statusMessage; + public String source; public boolean selected; diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index d179479c5..82e0db12d 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -18,67 +18,103 @@ ~ along with this program. If not, see . --> - + android:layout_height="@dimen/item_height"> + + + + + + + + + + + + - - - - - - - - - + diff --git a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml index 7a3c05001..4f16aef4d 100644 --- a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml +++ b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml @@ -43,8 +43,7 @@ android:layout_width="@dimen/small_item_height" android:layout_height="@dimen/small_item_height" android:contentDescription="@null" - app:roundAsCircle="true" - tools:src="@drawable/ic_call_black_24dp" /> + app:roundAsCircle="true" /> Date: Mon, 21 Feb 2022 11:05:51 +0100 Subject: [PATCH 21/56] show online status AND emoji in conversations info Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/adapters/items/UserItem.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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 68943a16b..9d77fbaf4 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 @@ -129,18 +129,19 @@ public class UserItem extends AbstractFlexibleItem } } + if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { + holder.participantEmoji.setText(participant.statusIcon); + } else { + holder.participantEmoji.setVisibility(View.GONE); + } + if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { setOnlineStateIcon(holder, R.drawable.ic_user_status_dnd_with_border); - } else if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { - holder.participantOnlineStateImage.setVisibility(View.GONE); - holder.participantEmoji.setVisibility(View.VISIBLE); - holder.participantEmoji.setText(participant.statusIcon); } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { setOnlineStateIcon(holder, R.drawable.ic_user_status_away_with_border); } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { setOnlineStateIcon(holder, R.drawable.online_status_with_border); } else { - holder.participantEmoji.setVisibility(View.GONE); holder.participantOnlineStateImage.setVisibility(View.GONE); } @@ -277,8 +278,6 @@ public class UserItem extends AbstractFlexibleItem } private void setOnlineStateIcon(UserItem.UserItemViewHolder holder, int icon) { - holder.participantEmoji.setVisibility(View.GONE); - holder.participantOnlineStateImage.setVisibility(View.VISIBLE); holder.participantOnlineStateImage.setImageDrawable(ContextCompat.getDrawable(context, icon)); } From 22c770fa8a082f6c6f43b9b36763676671d84db6 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 21 Feb 2022 11:20:59 +0100 Subject: [PATCH 22/56] set "away" / "dnd" as status message if it's empty Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/adapters/items/UserItem.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 9d77fbaf4..2dcd8c7af 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 @@ -129,6 +129,10 @@ public class UserItem extends AbstractFlexibleItem } } + if (participant.statusMessage != null) { + holder.statusMessage.setText(participant.statusMessage); + } + if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { holder.participantEmoji.setText(participant.statusIcon); } else { @@ -137,17 +141,20 @@ public class UserItem extends AbstractFlexibleItem if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { setOnlineStateIcon(holder, R.drawable.ic_user_status_dnd_with_border); + if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { + holder.statusMessage.setText(R.string.dnd); + } } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { setOnlineStateIcon(holder, R.drawable.ic_user_status_away_with_border); + if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { + holder.statusMessage.setText(R.string.away); + } } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { setOnlineStateIcon(holder, R.drawable.online_status_with_border); } else { holder.participantOnlineStateImage.setVisibility(View.GONE); } - if (participant.statusMessage != null) { - holder.statusMessage.setText(participant.statusMessage); - } if (!isOnline) { holder.contactDisplayName.setTextColor(ResourcesCompat.getColor( From c64a71b6c1c1ff0c9a6f076039396e2c6691a099 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 21 Feb 2022 11:53:17 +0100 Subject: [PATCH 23/56] improve layout Signed-off-by: Marcel Hibbe --- .../rv_item_conversation_info_participant.xml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index 82e0db12d..0319cde88 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -31,7 +31,7 @@ android:layout_width="@dimen/small_item_height" android:layout_height="@dimen/small_item_height" android:layout_marginStart="16dp" - android:layout_marginTop="8dp" + android:layout_marginTop="16dp" android:contentDescription="@null" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -39,15 +39,14 @@ Date: Mon, 21 Feb 2022 12:09:17 +0100 Subject: [PATCH 24/56] hide secondary text for normal users Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/adapters/items/UserItem.java | 6 ++++-- .../res/layout/rv_item_conversation_info_participant.xml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) 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 2dcd8c7af..3525fc6bb 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.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.text.TextUtils; @@ -116,6 +117,7 @@ public class UserItem extends AbstractFlexibleItem return new UserItemViewHolder(view, adapter); } + @SuppressLint("SetTextI18n") @Override public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) { @@ -277,8 +279,8 @@ public class UserItem extends AbstractFlexibleItem break; } - if (!holder.contactMentionId.getText().equals(userType)) { - holder.contactMentionId.setText(userType); + if (!userType.equals(NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_user))) { + holder.contactMentionId.setText("(" + userType + ")"); } } } diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index 0319cde88..15e4ee6f1 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -39,7 +39,7 @@ From ffbe39bd018622ef4b2fd624d92d67b51cf24812 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 21 Feb 2022 14:43:48 +0100 Subject: [PATCH 25/56] change UserStatusCapability to kotlin data class Signed-off-by: Marcel Hibbe --- .../models/database/CapabilitiesUtil.java | 4 +- .../json/capabilities/Capabilities.java | 160 ------------------ .../models/json/capabilities/Capabilities.kt | 6 +- .../capabilities/UserStatusCapability.java | 32 ---- .../json/capabilities/UserStatusCapability.kt | 39 +++++ 5 files changed, 45 insertions(+), 196 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.kt diff --git a/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java b/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java index 3d8375bcc..1babff115 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java @@ -180,8 +180,8 @@ public abstract class CapabilitiesUtil { try { Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities.getUserStatusCapability() != null && - capabilities.getUserStatusCapability().isEnabled() && - capabilities.getUserStatusCapability().isSupportsEmoji()) { + capabilities.getUserStatusCapability().getEnabled() && + capabilities.getUserStatusCapability().getSupportsEmoji()) { return true; } } catch (IOException e) { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java deleted file mode 100644 index ca0fa9492..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.capabilities; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; - -import org.parceler.Parcel; - -import java.util.HashMap; -import java.util.List; - -@Parcel -@JsonObject -public class Capabilities { - @JsonField(name = "spreed") - SpreedCapability spreedCapability; - - @JsonField(name = "notifications") - NotificationsCapability notificationsCapability; - - @JsonField(name = "theming") - ThemingCapability themingCapability; - - @JsonField(name = "external") - HashMap> externalCapability; - - @JsonField(name = "provisioning_api") - ProvisioningCapability provisioningCapability; - - @JsonField(name = "user_status") - UserStatusCapability userStatusCapability; - - public SpreedCapability getSpreedCapability() { - return this.spreedCapability; - } - - public NotificationsCapability getNotificationsCapability() { - return this.notificationsCapability; - } - - public ThemingCapability getThemingCapability() { - return this.themingCapability; - } - - public HashMap> getExternalCapability() { - return this.externalCapability; - } - - public ProvisioningCapability getProvisioningCapability() { - return this.provisioningCapability; - } - - public UserStatusCapability getUserStatusCapability() { - return userStatusCapability; - } - - public void setSpreedCapability(SpreedCapability spreedCapability) { - this.spreedCapability = spreedCapability; - } - - public void setNotificationsCapability(NotificationsCapability notificationsCapability) { - this.notificationsCapability = notificationsCapability; - } - - public void setThemingCapability(ThemingCapability themingCapability) { - this.themingCapability = themingCapability; - } - - public void setExternalCapability(HashMap> externalCapability) { - this.externalCapability = externalCapability; - } - - public void setProvisioningCapability(ProvisioningCapability provisioningCapability) { - this.provisioningCapability = provisioningCapability; - } - - public void setUserStatusCapability(UserStatusCapability userStatusCapability) { - this.userStatusCapability = userStatusCapability; - } - - public boolean equals(final Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Capabilities)) { - return false; - } - final Capabilities other = (Capabilities) o; - if (!other.canEqual((Object) this)) { - return false; - } - final Object this$spreedCapability = this.getSpreedCapability(); - final Object other$spreedCapability = other.getSpreedCapability(); - if (this$spreedCapability == null ? other$spreedCapability != null : !this$spreedCapability.equals(other$spreedCapability)) { - return false; - } - final Object this$notificationsCapability = this.getNotificationsCapability(); - final Object other$notificationsCapability = other.getNotificationsCapability(); - if (this$notificationsCapability == null ? other$notificationsCapability != null : !this$notificationsCapability.equals(other$notificationsCapability)) { - return false; - } - final Object this$themingCapability = this.getThemingCapability(); - final Object other$themingCapability = other.getThemingCapability(); - if (this$themingCapability == null ? other$themingCapability != null : !this$themingCapability.equals(other$themingCapability)) { - return false; - } - final Object this$externalCapability = this.getExternalCapability(); - final Object other$externalCapability = other.getExternalCapability(); - if (this$externalCapability == null ? other$externalCapability != null : !this$externalCapability.equals(other$externalCapability)) { - return false; - } - final Object this$provisioningCapability = this.getProvisioningCapability(); - final Object other$provisioningCapability = other.getProvisioningCapability(); - - return this$provisioningCapability == null ? other$provisioningCapability == null : this$provisioningCapability.equals(other$provisioningCapability); - } - - protected boolean canEqual(final Object other) { - return other instanceof Capabilities; - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $spreedCapability = this.getSpreedCapability(); - result = result * PRIME + ($spreedCapability == null ? 43 : $spreedCapability.hashCode()); - final Object $notificationsCapability = this.getNotificationsCapability(); - result = result * PRIME + ($notificationsCapability == null ? 43 : $notificationsCapability.hashCode()); - final Object $themingCapability = this.getThemingCapability(); - result = result * PRIME + ($themingCapability == null ? 43 : $themingCapability.hashCode()); - final Object $externalCapability = this.getExternalCapability(); - result = result * PRIME + ($externalCapability == null ? 43 : $externalCapability.hashCode()); - final Object $provisioningCapability = this.getProvisioningCapability(); - result = result * PRIME + ($provisioningCapability == null ? 43 : $provisioningCapability.hashCode()); - return result; - } - - public String toString() { - return "Capabilities(spreedCapability=" + this.getSpreedCapability() + ", notificationsCapability=" + this.getNotificationsCapability() + ", themingCapability=" + this.getThemingCapability() + ", externalCapability=" + this.getExternalCapability() + ", provisioningCapability=" + this.getProvisioningCapability() + ")"; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt index 0bda48018..6682469b5 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt @@ -38,8 +38,10 @@ data class Capabilities( @JsonField(name = ["external"]) var externalCapability: HashMap>?, @JsonField(name = ["provisioning_api"]) - var provisioningCapability: ProvisioningCapability? + var provisioningCapability: ProvisioningCapability?, + @JsonField(name = ["user_status"]) + var userStatusCapability: UserStatusCapability? ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null, null, null, null) + constructor() : this(null, null, null, null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java deleted file mode 100644 index 9c344e852..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.nextcloud.talk.models.json.capabilities; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; - -import org.parceler.Parcel; - -@Parcel -@JsonObject -public class UserStatusCapability { - @JsonField(name = "enabled") - boolean enabled; - - @JsonField(name = "supports_emoji") - boolean supportsEmoji; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isSupportsEmoji() { - return supportsEmoji; - } - - public void setSupportsEmoji(boolean supportsEmoji) { - this.supportsEmoji = supportsEmoji; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.kt b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.kt new file mode 100644 index 000000000..eda9cb399 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.kt @@ -0,0 +1,39 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Tim Krüger + * Copyright (C) 2022 Tim Krüger + * Copyright (C) 2017-2019 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.capabilities + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class UserStatusCapability( + @JsonField(name = ["enabled"]) + var enabled: Boolean, + @JsonField(name = ["supports_emoji"]) + var supportsEmoji: Boolean +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(false, false) +} From e3fd9a7592907d938ef359af4dd442746c746712 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 21 Feb 2022 15:31:14 +0100 Subject: [PATCH 26/56] fix onlinestatus color for night mode Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/controllers/ConversationInfoController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index b1de2d208..89e1d336e 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -384,7 +384,7 @@ class ConversationInfoController(args: Bundle) : for (i in participants.indices) { participant = participants[i] - userItem = UserItem(context, participant, conversationUser, null) + userItem = UserItem(router.activity, participant, conversationUser, null) if (participant.sessionId != null) { userItem.isOnline = !participant.sessionId.equals("0") } else { From e94cb3b9ac3c70881f59f2374a9bcc9cbb803625 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 22 Feb 2022 11:05:13 +0100 Subject: [PATCH 27/56] allow 2 lines for status message in conversation info Signed-off-by: Marcel Hibbe --- .../layout/rv_item_conversation_info_participant.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index 15e4ee6f1..6576ca36e 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -23,7 +23,7 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/relativeLayout" android:layout_width="match_parent" - android:layout_height="@dimen/item_height"> + android:layout_height="wrap_content"> + app:layout_constraintEnd_toEndOf="parent" + tools:text="this is a very long status message. server allows only 81 chars here. 0123456789" /> Date: Tue, 22 Feb 2022 14:25:08 +0100 Subject: [PATCH 28/56] fix autocomplete mentions Signed-off-by: Marcel Hibbe --- .../main/java/com/nextcloud/talk/adapters/items/UserItem.java | 4 ++++ app/src/main/res/layout/rv_item_mention.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) 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 3525fc6bb..4b5ccdbb3 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 @@ -311,6 +311,7 @@ public class UserItem extends AbstractFlexibleItem @BindView(R.id.name_text) public EmojiTextView contactDisplayName; + @Nullable @BindView(R.id.conversation_info_participant_avatar) public SimpleDraweeView participantAvatar; @Nullable @@ -322,10 +323,13 @@ public class UserItem extends AbstractFlexibleItem @Nullable @BindView(R.id.checkedImageView) ImageView checkedImageView; + @Nullable @BindView(R.id.conversation_info_participant_emoji) com.vanniktech.emoji.EmojiEditText participantEmoji; + @Nullable @BindView(R.id.conversation_info_participant_online_state) ImageView participantOnlineStateImage; + @Nullable @BindView(R.id.conversation_info_status_message) EmojiTextView statusMessage; diff --git a/app/src/main/res/layout/rv_item_mention.xml b/app/src/main/res/layout/rv_item_mention.xml index d86d338a0..54e5d47c1 100644 --- a/app/src/main/res/layout/rv_item_mention.xml +++ b/app/src/main/res/layout/rv_item_mention.xml @@ -35,7 +35,7 @@ android:layout_marginStart="@dimen/activity_horizontal_margin"> From 0078778c9967b329319422b30c5d54002de94b8e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 22 Feb 2022 18:01:25 +0100 Subject: [PATCH 29/56] show status for autocomplete mentions Signed-off-by: Marcel Hibbe --- .../items/MentionAutocompleteItem.java | 37 +++++- .../talk/adapters/items/UserItem.java | 4 +- .../java/com/nextcloud/talk/api/NcApi.java | 3 +- .../talk/models/json/mention/Mention.java | 110 ------------------ .../talk/models/json/mention/Mention.kt | 52 +++++++++ .../MentionAutocompletePresenter.java | 17 ++- .../rv_item_conversation_info_participant.xml | 6 +- app/src/main/res/layout/rv_item_mention.xml | 19 +++ 8 files changed, 122 insertions(+), 126 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index 5cf654130..1b9491c7b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -29,12 +29,15 @@ import com.facebook.drawee.interfaces.DraweeController; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.mention.Mention; +import com.nextcloud.talk.models.json.status.StatusType; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import java.util.List; import java.util.regex.Pattern; +import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; @@ -50,18 +53,20 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem @BindView(R.id.checkedImageView) ImageView checkedImageView; @Nullable - @BindView(R.id.conversation_info_participant_emoji) + @BindView(R.id.participant_status_emoji) com.vanniktech.emoji.EmojiEditText participantEmoji; @Nullable - @BindView(R.id.conversation_info_participant_online_state) + @BindView(R.id.participant_online_state) ImageView participantOnlineStateImage; @Nullable @BindView(R.id.conversation_info_status_message) 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 688416959..f05910afa 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -336,7 +336,8 @@ public interface NcApi { @GET Observable getMentionAutocompleteSuggestions(@Header("Authorization") String authorization, @Url String url, @Query("search") String query, - @Nullable @Query("limit") Integer limit); + @Nullable @Query("limit") Integer limit, + @QueryMap Map fields); // Url is: /api/{apiVersion}/room/{token}/pin @POST diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java deleted file mode 100644 index 7770eb13f..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.mention; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; - -import org.parceler.Parcel; - -@Parcel -@JsonObject -public class Mention { - @JsonField(name = "id") - String id; - - @JsonField(name = "label") - String label; - - // type of user (guests or users or calls) - @JsonField(name = "source") - String source; - - public String getId() { - return this.id; - } - - public String getLabel() { - return this.label; - } - - public String getSource() { - return this.source; - } - - public void setId(String id) { - this.id = id; - } - - public void setLabel(String label) { - this.label = label; - } - - public void setSource(String source) { - this.source = source; - } - - public boolean equals(final Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Mention)) { - return false; - } - final Mention other = (Mention) o; - if (!other.canEqual((Object) this)) { - return false; - } - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (this$id == null ? other$id != null : !this$id.equals(other$id)) { - return false; - } - final Object this$label = this.getLabel(); - final Object other$label = other.getLabel(); - if (this$label == null ? other$label != null : !this$label.equals(other$label)) { - return false; - } - final Object this$source = this.getSource(); - final Object other$source = other.getSource(); - - return this$source == null ? other$source == null : this$source.equals(other$source); - } - - protected boolean canEqual(final Object other) { - return other instanceof Mention; - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final Object $label = this.getLabel(); - result = result * PRIME + ($label == null ? 43 : $label.hashCode()); - final Object $source = this.getSource(); - result = result * PRIME + ($source == null ? 43 : $source.hashCode()); - return result; - } - - public String toString() { - return "Mention(id=" + this.getId() + ", label=" + this.getLabel() + ", source=" + this.getSource() + ")"; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt new file mode 100644 index 000000000..0ab861725 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt @@ -0,0 +1,52 @@ +/* + * 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.mention + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class Mention( + @JsonField(name = ["id"]) + var id : String, + + @JsonField(name = ["label"]) + var label : String, + + // type of user (guests or users or calls) + @JsonField(name = ["source"]) + var source : String, + + @JsonField(name = ["status"]) + var status : String?, + + @JsonField(name = ["statusIcon"]) + var statusIcon : String?, + + @JsonField(name = ["statusMessage"]) + var statusMessage : String? + +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this("", "", "", "", "", "") +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java index a70f81f84..e87c9c1a0 100644 --- a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java +++ b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java @@ -22,7 +22,9 @@ package com.nextcloud.talk.presenters; +import android.annotation.SuppressLint; import android.content.Context; +import android.util.Log; import android.view.View; import com.nextcloud.talk.adapters.items.MentionAutocompleteItem; @@ -38,7 +40,9 @@ import com.otaliastudios.autocomplete.RecyclerViewPresenter; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -54,6 +58,7 @@ import io.reactivex.schedulers.Schedulers; @AutoInjector(NextcloudTalkApplication.class) public class MentionAutocompletePresenter extends RecyclerViewPresenter implements FlexibleAdapter.OnItemClickListener { + private static final String TAG = "MentionAutocompletePresenter"; @Inject NcApi ncApi; @Inject @@ -101,10 +106,14 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter int apiVersion = ApiUtils.getChatApiVersion(currentUser, new int[] {1}); adapter.setFilter(queryString); + + Map queryMap = new HashMap<>(); + queryMap.put("includeStatus", "true"); + ncApi.getMentionAutocompleteSuggestions( ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), ApiUtils.getUrlForMentionSuggestions(apiVersion, currentUser.getBaseUrl(), roomToken), - queryString, 5) + queryString, 5, queryMap) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(3) @@ -125,9 +134,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter for (Mention mention : mentionsList) { internalAbstractFlexibleItemList.add( new MentionAutocompleteItem( - mention.getId(), - mention.getLabel(), - mention.getSource(), + mention, currentUser, context)); } @@ -140,9 +147,11 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter } } + @SuppressLint("LongLogTag") @Override public void onError(@NotNull Throwable e) { adapter.clear(); + Log.e(TAG, "failed to get MentionAutocompleteSuggestions", e); } @Override diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index 6576ca36e..bde617f15 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -38,7 +38,7 @@ app:roundAsCircle="true" /> diff --git a/app/src/main/res/layout/rv_item_mention.xml b/app/src/main/res/layout/rv_item_mention.xml index 54e5d47c1..09f2d14ab 100644 --- a/app/src/main/res/layout/rv_item_mention.xml +++ b/app/src/main/res/layout/rv_item_mention.xml @@ -40,6 +40,25 @@ android:layout_height="@dimen/avatar_size" app:roundAsCircle="true" /> + + + + Date: Wed, 23 Feb 2022 09:27:57 +0100 Subject: [PATCH 30/56] improve autocomplete mention items design Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/rv_item_mention.xml | 119 +++++++++----------- 1 file changed, 56 insertions(+), 63 deletions(-) diff --git a/app/src/main/res/layout/rv_item_mention.xml b/app/src/main/res/layout/rv_item_mention.xml index 09f2d14ab..8e24ee150 100644 --- a/app/src/main/res/layout/rv_item_mention.xml +++ b/app/src/main/res/layout/rv_item_mention.xml @@ -20,77 +20,70 @@ ~ along with this program. If not, see . --> - + android:layout_height="@dimen/item_height"> - + + + android:background="@color/transparent" + android:cursorVisible="false" + android:gravity="center" + android:text="@string/default_emoji" + android:textSize="16sp" + app:layout_constraintBottom_toBottomOf="@+id/participant_online_state" + app:layout_constraintEnd_toEndOf="@+id/participant_online_state" /> - + - - - - - - - + android:layout_marginStart="16dp" + android:layout_marginTop="4dp" + android:ellipsize="middle" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/ListItem" + app:layout_constraintStart_toEndOf="@+id/conversation_info_participant_avatar" + app:layout_constraintTop_toTopOf="@+id/conversation_info_participant_avatar" + tools:text="username" /> - + - - - - - + From 452baf4550c361d3338860d2c47f0ec50f88515f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 23 Feb 2022 11:41:21 +0100 Subject: [PATCH 31/56] improve autocomplete mentions UI add status message and emoji match parent improve design use rv_item_mention_conversation_info_participant instead of rv_item_mention.xml Signed-off-by: Marcel Hibbe --- .../items/MentionAutocompleteItem.java | 50 +++++++---- .../talk/adapters/items/UserItem.java | 2 + .../MentionAutocompletePresenter.java | 9 ++ .../rv_item_conversation_info_participant.xml | 28 +++--- app/src/main/res/layout/rv_item_mention.xml | 89 ------------------- 5 files changed, 61 insertions(+), 117 deletions(-) delete mode 100644 app/src/main/res/layout/rv_item_mention.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index 1b9491c7b..dbe3f3a8e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -55,6 +55,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem if (participant.statusMessage != null) { holder.statusMessage.setText(participant.statusMessage); + } else { + holder.statusMessage.setText(""); } if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { diff --git a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java index e87c9c1a0..2b9a43259 100644 --- a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java +++ b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java @@ -26,6 +26,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.util.Log; import android.view.View; +import android.view.ViewGroup; import com.nextcloud.talk.adapters.items.MentionAutocompleteItem; import com.nextcloud.talk.api.NcApi; @@ -93,6 +94,14 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter return adapter; } + @Override + protected PopupDimensions getPopupDimensions() { + PopupDimensions popupDimensions = new PopupDimensions(); + popupDimensions.width = ViewGroup.LayoutParams.MATCH_PARENT; + popupDimensions.height = ViewGroup.LayoutParams.WRAP_CONTENT; + return popupDimensions; + } + @Override protected void onQuery(@Nullable CharSequence query) { diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index bde617f15..0852cdf5e 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -23,15 +23,16 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/relativeLayout" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/standard_half_margin" + android:layout_marginTop="@dimen/standard_margin"> + tools:text="Moderator (or userid for autocomplete mention)" /> diff --git a/app/src/main/res/layout/rv_item_mention.xml b/app/src/main/res/layout/rv_item_mention.xml deleted file mode 100644 index 8e24ee150..000000000 --- a/app/src/main/res/layout/rv_item_mention.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - From 7820bc7c51d95e5c72113666ba966d7800e25a6b Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 23 Feb 2022 13:21:24 +0100 Subject: [PATCH 32/56] hide status of "Talk updates" conversation Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/ConversationItem.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index bf171f4d2..1a9be11b0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -197,16 +197,21 @@ public class ConversationItem extends AbstractFlexibleItem Date: Wed, 23 Feb 2022 14:21:22 +0100 Subject: [PATCH 33/56] fix NPE when opening Contacts view. because UserItem is used for ContactsController and ConversationInfoController it differs between two different xml designs in method getLayoutRes() because of this there need to be checks which xml element exists or not. These are added with this commit. In the long term UserItem should not use two different xml designs. This might also be a problem for view binding?! Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/UserItem.java | 55 ++++++++++--------- app/src/main/res/layout/rv_item_contact.xml | 4 +- .../rv_item_conversation_info_participant.xml | 10 ++-- 3 files changed, 35 insertions(+), 34 deletions(-) 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 84c6219c7..ef22694c2 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 @@ -131,34 +131,35 @@ public class UserItem extends AbstractFlexibleItem } } - if (participant.statusMessage != null) { - holder.statusMessage.setText(participant.statusMessage); - } else { - holder.statusMessage.setText(""); - } - - if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { - holder.participantEmoji.setText(participant.statusIcon); - } else { - holder.participantEmoji.setVisibility(View.GONE); - } - - if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { - setOnlineStateIcon(holder, R.drawable.ic_user_status_dnd_with_border); - if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { - holder.statusMessage.setText(R.string.dnd); + if (holder.statusMessage != null && holder.participantEmoji != null && holder.participantOnlineStateImage != null) { + if (participant.statusMessage != null) { + holder.statusMessage.setText(participant.statusMessage); + } else { + holder.statusMessage.setText(""); } - } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { - setOnlineStateIcon(holder, R.drawable.ic_user_status_away_with_border); - if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { - holder.statusMessage.setText(R.string.away); - } - } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { - setOnlineStateIcon(holder, R.drawable.online_status_with_border); - } else { - holder.participantOnlineStateImage.setVisibility(View.GONE); - } + if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { + holder.participantEmoji.setText(participant.statusIcon); + } else { + holder.participantEmoji.setVisibility(View.GONE); + } + + if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { + setOnlineStateIcon(holder, R.drawable.ic_user_status_dnd_with_border); + if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { + holder.statusMessage.setText(R.string.dnd); + } + } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { + setOnlineStateIcon(holder, R.drawable.ic_user_status_away_with_border); + if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { + holder.statusMessage.setText(R.string.away); + } + } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { + setOnlineStateIcon(holder, R.drawable.online_status_with_border); + } else { + holder.participantOnlineStateImage.setVisibility(View.GONE); + } + } if (!isOnline) { holder.contactDisplayName.setTextColor(ResourcesCompat.getColor( @@ -314,7 +315,7 @@ public class UserItem extends AbstractFlexibleItem @BindView(R.id.name_text) public EmojiTextView contactDisplayName; @Nullable - @BindView(R.id.conversation_info_participant_avatar) + @BindView(R.id.avatar_drawee_view) public SimpleDraweeView participantAvatar; @Nullable @BindView(R.id.secondary_text) diff --git a/app/src/main/res/layout/rv_item_contact.xml b/app/src/main/res/layout/rv_item_contact.xml index 57d6e6894..7bcefd7e7 100644 --- a/app/src/main/res/layout/rv_item_contact.xml +++ b/app/src/main/res/layout/rv_item_contact.xml @@ -48,7 +48,7 @@ android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toStartOf="@id/checkedImageView" - android:layout_toEndOf="@id/simple_drawee_view" + android:layout_toEndOf="@id/avatar_drawee_view" android:ellipsize="end" android:lines="1" android:textAlignment="viewStart" @@ -56,7 +56,7 @@ tools:text="Jane Doe" /> + app:layout_constraintBottom_toBottomOf="@+id/avatar_drawee_view" + app:layout_constraintEnd_toEndOf="@+id/avatar_drawee_view" /> Date: Wed, 23 Feb 2022 14:49:14 +0100 Subject: [PATCH 34/56] fix lint/spotbug warnings Signed-off-by: Marcel Hibbe --- .../items/MentionAutocompleteItem.java | 92 ++++++++++--------- .../talk/adapters/items/UserItem.java | 13 ++- .../predefined/PredefinedStatusOCS.java | 74 --------------- .../status/predefined/PredefinedStatusOCS.kt | 17 ++++ .../predefined/PredefinedStatusOverall.java | 65 ------------- .../predefined/PredefinedStatusOverall.kt | 37 ++++++++ .../talk/ui/dialog/SetStatusDialogFragment.kt | 2 +- 7 files changed, 111 insertions(+), 189 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.kt diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index dbe3f3a8e..fd107682e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -35,6 +35,7 @@ import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import java.util.List; +import java.util.Objects; import java.util.regex.Pattern; import androidx.core.content.ContextCompat; @@ -50,14 +51,14 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem } if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { - setOnlineStateIcon(holder, R.drawable.ic_user_status_dnd_with_border); + holder.participantOnlineStateImage.setImageDrawable( + ContextCompat.getDrawable(context, R.drawable.ic_user_status_dnd_with_border)); if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { holder.statusMessage.setText(R.string.dnd); } } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { - setOnlineStateIcon(holder, R.drawable.ic_user_status_away_with_border); + holder.participantOnlineStateImage.setImageDrawable( + ContextCompat.getDrawable(context, R.drawable.ic_user_status_away_with_border)); if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { holder.statusMessage.setText(R.string.away); } } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { - setOnlineStateIcon(holder, R.drawable.online_status_with_border); + holder.participantOnlineStateImage.setImageDrawable( + ContextCompat.getDrawable(context, R.drawable.online_status_with_border)); } else { holder.participantOnlineStateImage.setVisibility(View.GONE); } @@ -289,10 +292,6 @@ public class UserItem extends AbstractFlexibleItem } } - private void setOnlineStateIcon(UserItem.UserItemViewHolder holder, int icon) { - holder.participantOnlineStateImage.setImageDrawable(ContextCompat.getDrawable(context, icon)); - } - @Override public boolean filter(String constraint) { return participant.getDisplayName() != null && diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java deleted file mode 100644 index e3447b013..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Nextcloud Talk application - * - * @author Tim Krüger - * Copyright (C) 2021 Tim Krüger - * - * 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.status.predefined; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; -import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser; -import com.nextcloud.talk.models.json.generic.GenericOCS; -import com.nextcloud.talk.models.json.status.Status; - -import java.util.List; -import java.util.Objects; - -import kotlin.jvm.JvmSuppressWildcards; - -@JsonObject -public class PredefinedStatusOCS extends GenericOCS { - @JsonField(name = "data") - List data; - - public List getData() { - return this.data; - } - - public void setData(List data) { - this.data = data; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - PredefinedStatusOCS that = (PredefinedStatusOCS) o; - return Objects.equals(data, that.data); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), data); - } - - @Override - public String toString() { - return "PredefinedStatusOCS{" + - "data=" + data + - '}'; - } - -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt new file mode 100644 index 000000000..53164cd65 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt @@ -0,0 +1,17 @@ +package com.nextcloud.talk.models.json.status.predefined + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.generic.GenericOCS +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class PredefinedStatusOCS( + @JsonField(name = ["data"]) + var data: List? +) : GenericOCS(), Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java deleted file mode 100644 index a9ccf8089..000000000 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Nextcloud Talk application - * - * @author Tim Krüger - * Copyright (C) 2021 Tim Krüger - * - * 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.status.predefined; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; -import com.nextcloud.talk.models.json.status.StatusOCS; - -import java.util.Objects; - -@JsonObject -public class PredefinedStatusOverall { - @JsonField(name = "ocs") - public PredefinedStatusOCS ocs; - - public PredefinedStatusOCS getOcs() { - return this.ocs; - } - - public void setOcs(PredefinedStatusOCS ocs) { - this.ocs = ocs; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PredefinedStatusOverall that = (PredefinedStatusOverall) o; - return Objects.equals(ocs, that.ocs); - } - - @Override - public int hashCode() { - return Objects.hash(ocs); - } - - @Override - public String toString() { - return "PredefinedStatusOverall{" + - "ocs=" + ocs + - '}'; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.kt new file mode 100644 index 000000000..030bd4926 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOverall.kt @@ -0,0 +1,37 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Tim Krüger + * Copyright (C) 2022 Tim Krüger + * 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.status.predefined + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class PredefinedStatusOverall( + @JsonField(name = ["ocs"]) + var ocs: PredefinedStatusOCS? = null +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 63f8264d0..837b2b993 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -126,7 +126,7 @@ class SetStatusDialogFragment : .string(), PredefinedStatusOverall::class.java ) - predefinedStatusesList.addAll(predefinedStatusOverall.getOcs().data) + predefinedStatusOverall.ocs?.data?.let { it1 -> predefinedStatusesList.addAll(it1) } adapter.notifyDataSetChanged() } From 73ce2716de6ef18ecab65840c9a4b976ff49d624 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 23 Feb 2022 15:07:09 +0100 Subject: [PATCH 35/56] fix klint warnings Signed-off-by: Marcel Hibbe --- .../firebase/MagicFirebaseMessagingService.kt | 3 ++- .../nextcloud/talk/models/json/mention/Mention.kt | 14 +++++++------- .../nextcloud/talk/models/json/status/ClearAt.kt | 2 +- .../talk/models/json/status/StatusType.kt | 4 +--- .../json/status/predefined/PredefinedStatus.kt | 2 +- .../json/status/predefined/PredefinedStatusOCS.kt | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt index 87facd860..0989bffaa 100644 --- a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt +++ b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt @@ -272,7 +272,8 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() { apiVersion, signatureVerification.userEntity.baseUrl, decryptedPushMessage.id - ), null + ), + null ) .repeatWhen { completed -> completed.zipWith(Observable.range(1, 12), { _, i -> i }) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt index 0ab861725..470ba3c8f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt @@ -28,25 +28,25 @@ import kotlinx.android.parcel.Parcelize @JsonObject data class Mention( @JsonField(name = ["id"]) - var id : String, + var id: String, @JsonField(name = ["label"]) - var label : String, + var label: String, // type of user (guests or users or calls) @JsonField(name = ["source"]) - var source : String, + var source: String, @JsonField(name = ["status"]) - var status : String?, + var status: String?, @JsonField(name = ["statusIcon"]) - var statusIcon : String?, + var statusIcon: String?, @JsonField(name = ["statusMessage"]) - var statusMessage : String? + var statusMessage: String? ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this("", "", "", "", "", "") -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt index b5a4fbe13..34e3ea19c 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/ClearAt.kt @@ -12,7 +12,7 @@ data class ClearAt( var type: String, @JsonField(name = ["time"]) var time: String - ) : Parcelable{ +) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this("type", "time") } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt index 3f11936de..4e8eda9b4 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt @@ -1,11 +1,9 @@ package com.nextcloud.talk.models.json.status - - enum class StatusType(val string: String) { ONLINE("online"), OFFLINE("offline"), DND("dnd"), AWAY("away"), INVISIBLE("invisible"); -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt index 89c2bb08e..170d0cb9a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatus.kt @@ -17,7 +17,7 @@ data class PredefinedStatus( var message: String, @JsonField(name = ["clearAt"]) var clearAt: ClearAt? - ) : Parcelable { +) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this("id", "icon", "message", null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt index 53164cd65..0a6e6ecbd 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt @@ -14,4 +14,4 @@ data class PredefinedStatusOCS( ) : GenericOCS(), Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null) -} \ No newline at end of file +} From 1da8c3b206be3e5cd1e3596c070fa672f869d1b1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 12:31:58 +0100 Subject: [PATCH 36/56] modify StatusDrawable and use it for conversationList Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/ConversationItem.java | 39 +++++------------ .../com/nextcloud/talk/ui/StatusDrawable.java | 43 ++++++++----------- .../dialog/ChooseAccountDialogFragment.java | 1 + .../talk/ui/dialog/SetStatusDialogFragment.kt | 2 +- app/src/main/res/drawable/online_status.xml | 17 ++++++-- .../main/res/layout/current_account_item.xml | 1 + ...rv_item_conversation_with_last_message.xml | 20 ++------- app/src/main/res/values-night/colors.xml | 2 + app/src/main/res/values/colors.xml | 4 ++ 9 files changed, 55 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index 1a9be11b0..0de0a2413 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -48,6 +48,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.models.json.status.Status; import com.nextcloud.talk.models.json.status.StatusType; +import com.nextcloud.talk.ui.StatusDrawable; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; @@ -70,6 +71,7 @@ import eu.davidea.viewholders.FlexibleViewHolder; public class ConversationItem extends AbstractFlexibleItem implements ISectionable, IFilterable { + private static final float STATUS_SIZE_IN_DP = 9f; private Conversation conversation; private UserEntity userEntity; @@ -198,23 +200,13 @@ public class ConversationItem extends AbstractFlexibleItem. --> - - - + + + + diff --git a/app/src/main/res/layout/current_account_item.xml b/app/src/main/res/layout/current_account_item.xml index 5e6ab4d57..680fb3a36 100644 --- a/app/src/main/res/layout/current_account_item.xml +++ b/app/src/main/res/layout/current_account_item.xml @@ -107,6 +107,7 @@ android:maxLines="1" android:textColor="?android:attr/textColorSecondary" android:visibility="gone" + tools:visibility="visible" tools:text="☁️ My custom status" /> - - + android:contentDescription="@string/nc_account_chooser_active_user" /> #222222 #818181 + + #353535 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 6e29db17a..0289c8679 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -100,4 +100,8 @@ #eeeeee #EEEEEE + + #FFFFFF + From 5437466b75c14361fa0b66a108d13971ca0d9341 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 13:32:37 +0100 Subject: [PATCH 37/56] use same online state color as web and iOS Signed-off-by: Marcel Hibbe --- app/src/main/res/drawable/online_status.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/drawable/online_status.xml b/app/src/main/res/drawable/online_status.xml index 694071933..6d627e81d 100644 --- a/app/src/main/res/drawable/online_status.xml +++ b/app/src/main/res/drawable/online_status.xml @@ -26,7 +26,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" tools:ignore="VectorRaster"> From fc8462e0a5ad0f537ebd44afbbc5bf0affa8a996 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 13:41:43 +0100 Subject: [PATCH 38/56] use StatusDrawable for conversationInfo and mention autocomplete Signed-off-by: Marcel Hibbe --- .../items/MentionAutocompleteItem.java | 30 ++++++------- .../talk/adapters/items/UserItem.java | 29 +++++++------ .../ic_user_status_away_with_border.xml | 43 ------------------- .../ic_user_status_dnd_with_border.xml | 41 ------------------ .../rv_item_conversation_info_participant.xml | 14 +++--- ...rv_item_conversation_with_last_message.xml | 3 +- 6 files changed, 38 insertions(+), 122 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_user_status_away_with_border.xml delete mode 100644 app/src/main/res/drawable/ic_user_status_dnd_with_border.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index fd107682e..77fb848e4 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -31,6 +31,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.mention.Mention; import com.nextcloud.talk.models.json.status.StatusType; +import com.nextcloud.talk.ui.StatusDrawable; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; @@ -38,7 +39,6 @@ import java.util.List; import java.util.Objects; import java.util.regex.Pattern; -import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; @@ -49,8 +49,11 @@ import eu.davidea.flexibleadapter.utils.FlexibleUtils; public class MentionAutocompleteItem extends AbstractFlexibleItem implements IFilterable { + private static final float STATUS_SIZE_IN_DP = 9f; + private static final String NO_ICON = ""; public static final String SOURCE_CALLS = "calls"; public static final String SOURCE_GUESTS = "guests"; + private String source; private final String objectId; private final String displayName; @@ -171,7 +174,16 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem implements ISectionable, IFilterable { + private static final float STATUS_SIZE_IN_DP = 9f; + private static final String NO_ICON = ""; + private Context context; private Participant participant; private UserEntity userEntity; @@ -131,7 +134,15 @@ public class UserItem extends AbstractFlexibleItem } } - if (holder.statusMessage != null && holder.participantEmoji != null && holder.participantOnlineStateImage != null) { + if (holder.statusMessage != null && holder.participantEmoji != null && holder.userStatusImage != null) { + float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context); + holder.userStatusImage.setImageDrawable(new StatusDrawable( + participant.status, + NO_ICON, + size, + context.getResources().getColor(R.color.bg_default), + context)); + if (participant.statusMessage != null) { holder.statusMessage.setText(participant.statusMessage); } else { @@ -145,22 +156,13 @@ public class UserItem extends AbstractFlexibleItem } if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { - holder.participantOnlineStateImage.setImageDrawable( - ContextCompat.getDrawable(context, R.drawable.ic_user_status_dnd_with_border)); if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { holder.statusMessage.setText(R.string.dnd); } } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { - holder.participantOnlineStateImage.setImageDrawable( - ContextCompat.getDrawable(context, R.drawable.ic_user_status_away_with_border)); if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { holder.statusMessage.setText(R.string.away); } - } else if (participant.status != null && participant.status.equals(StatusType.ONLINE.getString())) { - holder.participantOnlineStateImage.setImageDrawable( - ContextCompat.getDrawable(context, R.drawable.online_status_with_border)); - } else { - holder.participantOnlineStateImage.setVisibility(View.GONE); } } @@ -328,9 +330,8 @@ public class UserItem extends AbstractFlexibleItem @Nullable @BindView(R.id.participant_status_emoji) com.vanniktech.emoji.EmojiEditText participantEmoji; - @Nullable - @BindView(R.id.participant_online_state) - ImageView participantOnlineStateImage; + @BindView(R.id.user_status_image) + ImageView userStatusImage; @Nullable @BindView(R.id.conversation_info_status_message) EmojiTextView statusMessage; diff --git a/app/src/main/res/drawable/ic_user_status_away_with_border.xml b/app/src/main/res/drawable/ic_user_status_away_with_border.xml deleted file mode 100644 index 6de7c7bf7..000000000 --- a/app/src/main/res/drawable/ic_user_status_away_with_border.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_user_status_dnd_with_border.xml b/app/src/main/res/drawable/ic_user_status_dnd_with_border.xml deleted file mode 100644 index 69f952f05..000000000 --- a/app/src/main/res/drawable/ic_user_status_dnd_with_border.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index ccf263663..6afc309de 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -51,14 +51,14 @@ app:layout_constraintTop_toBottomOf="@+id/name_text" /> - + app:layout_constraintEnd_toEndOf="@+id/avatar_drawee_view" + tools:src="@drawable/emoji_one_category_smileysandpeople"/> + android:contentDescription="@string/nc_account_chooser_active_user" + tools:src="@drawable/emoji_one_category_smileysandpeople"/> Date: Thu, 24 Feb 2022 14:26:47 +0100 Subject: [PATCH 39/56] fix NPE for contacts view Signed-off-by: Marcel Hibbe --- .../main/java/com/nextcloud/talk/adapters/items/UserItem.java | 1 + 1 file changed, 1 insertion(+) 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 a85f71b5f..34180bef9 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 @@ -330,6 +330,7 @@ public class UserItem extends AbstractFlexibleItem @Nullable @BindView(R.id.participant_status_emoji) com.vanniktech.emoji.EmojiEditText participantEmoji; + @Nullable @BindView(R.id.user_status_image) ImageView userStatusImage; @Nullable From a7792cb66e4e6c27eac659f1797b50009563963f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 14:45:21 +0100 Subject: [PATCH 40/56] fix lint and spotbugs warnings Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/ConversationItem.java | 6 +- .../items/MentionAutocompleteItem.java | 3 + .../talk/adapters/items/UserItem.java | 70 ++++++++++--------- app/src/main/res/values/strings.xml | 1 - 4 files changed, 44 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index 0de0a2413..f87a7bcc7 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -199,7 +199,7 @@ public class ConversationItem extends AbstractFlexibleItem @Override public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) { - holder.participantAvatar.setController(null); + if (holder.participantAvatar != null) { + holder.participantAvatar.setController(null); + } if (holder.checkedImageView != null) { if (participant.isSelected()) { @@ -134,37 +136,7 @@ public class UserItem extends AbstractFlexibleItem } } - if (holder.statusMessage != null && holder.participantEmoji != null && holder.userStatusImage != null) { - float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context); - holder.userStatusImage.setImageDrawable(new StatusDrawable( - participant.status, - NO_ICON, - size, - context.getResources().getColor(R.color.bg_default), - context)); - - if (participant.statusMessage != null) { - holder.statusMessage.setText(participant.statusMessage); - } else { - holder.statusMessage.setText(""); - } - - if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { - holder.participantEmoji.setText(participant.statusIcon); - } else { - holder.participantEmoji.setVisibility(View.GONE); - } - - if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { - if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { - holder.statusMessage.setText(R.string.dnd); - } - } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { - if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { - holder.statusMessage.setText(R.string.away); - } - } - } + drawStatus(holder); if (!isOnline) { holder.contactDisplayName.setTextColor(ResourcesCompat.getColor( @@ -294,6 +266,40 @@ public class UserItem extends AbstractFlexibleItem } } + private void drawStatus(UserItemViewHolder holder) { + if (holder.statusMessage != null && holder.participantEmoji != null && holder.userStatusImage != null) { + float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context); + holder.userStatusImage.setImageDrawable(new StatusDrawable( + participant.status, + NO_ICON, + size, + context.getResources().getColor(R.color.bg_default), + context)); + + if (participant.statusMessage != null) { + holder.statusMessage.setText(participant.statusMessage); + } else { + holder.statusMessage.setText(""); + } + + if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { + holder.participantEmoji.setText(participant.statusIcon); + } else { + holder.participantEmoji.setVisibility(View.GONE); + } + + if (participant.status != null && participant.status.equals(StatusType.DND.getString())) { + if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { + holder.statusMessage.setText(R.string.dnd); + } + } else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) { + if (participant.statusMessage == null || participant.statusMessage.isEmpty()) { + holder.statusMessage.setText(R.string.away); + } + } + } + } + @Override public boolean filter(String constraint) { return participant.getDisplayName() != null && diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 128e1c338..0cc93d084 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -459,7 +459,6 @@ Favorite Status Encrypted - Password protected Avatar Account icon From c746b95ac2766ded2fa506656299498bdde34acd Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 15:19:16 +0100 Subject: [PATCH 41/56] add author to license headers Signed-off-by: Marcel Hibbe --- app/build.gradle | 2 -- .../talk/adapters/items/ConversationItem.java | 2 ++ .../items/MentionAutocompleteItem.java | 2 ++ .../talk/adapters/items/UserItem.java | 2 ++ .../controllers/ConversationInfoController.kt | 2 ++ .../status/predefined/PredefinedStatusOCS.kt | 20 +++++++++++++++++++ .../MentionAutocompletePresenter.java | 2 ++ .../com/nextcloud/talk/ui/StatusDrawable.java | 3 ++- .../dialog/ChooseAccountDialogFragment.java | 2 ++ .../talk/ui/dialog/SetStatusDialogFragment.kt | 4 +++- 10 files changed, 37 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 20d0d23c3..1f6568db9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -194,9 +194,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - // implementation 'com.github.vanniktech:Emoji:0.6.0' // 0.7.0 has display issue - don't update to 0.7.0 implementation "com.vanniktech:emoji-google:0.8.0" - // implementation "com.vanniktech:emoji-google-compat:0.8.0" implementation group: 'androidx.emoji', name: 'emoji-bundled', version: '1.1.0' implementation 'org.michaelevans.colorart:library:0.0.3' implementation "androidx.work:work-runtime:${workVersion}" diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index f87a7bcc7..2c88334a8 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -3,6 +3,8 @@ * * @author Mario Danic * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017-2018 Mario Danic * diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index d6e911bc2..98f27f9d1 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -1,7 +1,9 @@ /* * Nextcloud Talk application * + * @author Marcel Hibbe * @author Mario Danic + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * Copyright (C) 2017-2018 Mario Danic * * This program is free software: you can redistribute it and/or modify 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 ba00580a2..362ba90eb 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 @@ -2,7 +2,9 @@ * Nextcloud Talk application * * @author Mario Danic + * @author Marcel Hibbe * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * * 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 diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 89e1d336e..5c0f77a60 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -4,6 +4,8 @@ * @author Mario Danic * @author Andy Scherzinger * @author Tim Krüger + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) * Copyright (C) 2017-2018 Mario Danic diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt index 0a6e6ecbd..2f2b6682b 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/predefined/PredefinedStatusOCS.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) + * + * 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.status.predefined import android.os.Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java index 2b9a43259..56730f207 100644 --- a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java +++ b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java @@ -3,6 +3,8 @@ * * @author Mario Danic * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017-2018 Mario Danic * diff --git a/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java b/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java index fc2c77747..1fca690ca 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java +++ b/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java @@ -2,7 +2,9 @@ * Nextcloud Android client application * * @author Tobias Kaminsky + * @author Marcel Hibbe * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * Copyright (C) 2020 Nextcloud GmbH * * This program is free software: you can redistribute it and/or modify @@ -23,7 +25,6 @@ package com.nextcloud.talk.ui; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 3a5138637..52b9b869e 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -3,8 +3,10 @@ * * @author Andy Scherzinger * @author Mario Danic + * @author Marcel Hibbe * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017 Mario Danic + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * * 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 diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 9b07a7877..ff2ce74f2 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -1,8 +1,11 @@ + /* * Nextcloud Android client application * * @author Tobias Kaminsky + * @author Marcel Hibbe * Copyright (C) 2020 Nextcloud GmbH + * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -17,7 +20,6 @@ * You should have received a copy of the GNU Affero General Public * License along with this program. If not, see . */ - package com.nextcloud.talk.ui.dialog import android.annotation.SuppressLint From 305ceedc75432710df5414b8ce81fd1621bcff55 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 22:03:56 +0100 Subject: [PATCH 42/56] align username etc vertical if no status message is set Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/MentionAutocompleteItem.java | 9 +++++++++ .../java/com/nextcloud/talk/adapters/items/UserItem.java | 9 +++++++++ .../res/layout/rv_item_conversation_info_participant.xml | 1 - 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index 98f27f9d1..71a508b7a 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Objects; import java.util.regex.Pattern; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.res.ResourcesCompat; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; @@ -193,6 +194,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem holder.statusMessage.setText(participant.statusMessage); } else { holder.statusMessage.setText(""); + alignUsernameVertical(holder); } if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { @@ -302,6 +304,13 @@ public class UserItem extends AbstractFlexibleItem } } + private void alignUsernameVertical(UserItemViewHolder holder) { + ConstraintLayout.LayoutParams layoutParams = + (ConstraintLayout.LayoutParams) holder.contactDisplayName.getLayoutParams(); + layoutParams.topMargin = (int) DisplayUtils.convertDpToPixel(10, context); + holder.contactDisplayName.setLayoutParams(layoutParams); + } + @Override public boolean filter(String constraint) { return participant.getDisplayName() != null && diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index 6afc309de..974cc00aa 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -21,7 +21,6 @@ Date: Thu, 24 Feb 2022 22:26:04 +0100 Subject: [PATCH 43/56] fix textcolor for highlighted online status button Signed-off-by: Marcel Hibbe --- .../talk/ui/dialog/SetStatusDialogFragment.kt | 16 +++++++++++----- app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index ff2ce74f2..1fd747a32 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -355,24 +355,25 @@ class SetStatusDialogFragment : } private fun visualizeStatus(statusType: StatusType) { + clearTopStatus() when (statusType) { StatusType.ONLINE -> { - clearTopStatus() binding.onlineStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } StatusType.AWAY -> { - clearTopStatus() binding.awayStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } StatusType.DND -> { - clearTopStatus() binding.dndStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } StatusType.INVISIBLE -> { - clearTopStatus() binding.invisibleStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } - else -> clearTopStatus() + else -> Log.d(logTag, "unknown status") } } @@ -383,6 +384,11 @@ class SetStatusDialogFragment : binding.awayStatus.setBackgroundColor(grey) binding.dndStatus.setBackgroundColor(grey) binding.invisibleStatus.setBackgroundColor(grey) + + binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) + binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) + binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) + binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) } } diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 2e630736f..ee62ad4ff 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -38,6 +38,7 @@ #deffffff #99ffffff #61ffffff + #de000000 #121212 #99121212 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 0289c8679..e67e5c6fe 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -39,6 +39,7 @@ #de000000 #99000000 #61000000 + #deffffff #deffffff From f475065a7f03f2f5b80db0504a1bd0c9a4228a93 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 22:47:20 +0100 Subject: [PATCH 44/56] set same textSize for all headings in Status dialog Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_set_status.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/dialog_set_status.xml b/app/src/main/res/layout/dialog_set_status.xml index 521f6db42..5f3b184cb 100644 --- a/app/src/main/res/layout/dialog_set_status.xml +++ b/app/src/main/res/layout/dialog_set_status.xml @@ -32,6 +32,7 @@ android:layout_marginBottom="@dimen/standard_half_margin" android:text="@string/online_status" android:textColor="@color/high_emphasis_text" + android:textSize="@dimen/activity_list_item_title_header_text_size" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> From 4984ba4ddeedac9b313fc0fd4d0a2a1884fdab65 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 23:08:40 +0100 Subject: [PATCH 45/56] avoid linebreaks for "clear at" calculation because: - can lead to wrong calculations! - avoid klint complaining about indentation Signed-off-by: Marcel Hibbe --- .../talk/ui/dialog/SetStatusDialogFragment.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 1fd747a32..0985f3545 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -235,6 +235,9 @@ class SetStatusDialogFragment : @Suppress("ComplexMethod") private fun setClearStatusAfterValue(item: Int) { + + val currentTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + when (item) { POS_DONT_CLEAR -> { // don't clear @@ -243,20 +246,17 @@ class SetStatusDialogFragment : POS_HALF_AN_HOUR -> { // 30 minutes - clearAt = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS + clearAt = currentTime + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS } POS_AN_HOUR -> { // one hour - clearAt = - System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS + clearAt = currentTime + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS } POS_FOUR_HOURS -> { // four hours - clearAt = - System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + FOUR_HOURS * ONE_MINUTE_IN_SECONDS * - ONE_MINUTE_IN_SECONDS + clearAt = currentTime + FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS } POS_TODAY -> { From e1c706fe682a08f04dd44f157df5e475f9c412cb Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 23:28:48 +0100 Subject: [PATCH 46/56] align icons + text in choose account dialog Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_choose_account.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/layout/dialog_choose_account.xml b/app/src/main/res/layout/dialog_choose_account.xml index 955f0fbf2..88c86895b 100644 --- a/app/src/main/res/layout/dialog_choose_account.xml +++ b/app/src/main/res/layout/dialog_choose_account.xml @@ -56,7 +56,7 @@ android:layout_height="50dp" android:layout_marginStart="12dp" android:layout_marginEnd="12dp" - android:paddingStart="10dp" + android:paddingStart="18dp" android:paddingEnd="0dp" android:text="@string/set_status" android:textAlignment="textStart" @@ -98,7 +98,7 @@ android:layout_height="50dp" android:layout_marginStart="12dp" android:layout_marginEnd="12dp" - android:paddingStart="14dp" + android:paddingStart="18dp" android:paddingEnd="4dp" android:text="@string/nc_account_chooser_add_account" android:textAlignment="textStart" @@ -120,7 +120,7 @@ android:layout_marginStart="12dp" android:layout_marginEnd="12dp" android:layout_marginBottom="12dp" - android:paddingStart="16dp" + android:paddingStart="18dp" android:paddingEnd="4dp" android:text="@string/nc_settings" android:textAlignment="textStart" @@ -128,7 +128,7 @@ android:textColor="@color/fontAppbar" app:icon="@drawable/ic_settings" app:iconGravity="start" - app:iconPadding="20dp" + app:iconPadding="22dp" app:iconTint="@color/fontAppbar" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" From ec438b0a9dc77f85395f40f8d0335966fd6647dc Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Feb 2022 23:51:53 +0100 Subject: [PATCH 47/56] add rounded colors to online status buttons Signed-off-by: Marcel Hibbe --- .../talk/ui/dialog/SetStatusDialogFragment.kt | 16 ++++++++-------- app/src/main/res/layout/dialog_set_status.xml | 12 ++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 0985f3545..9f5922887 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -358,19 +358,19 @@ class SetStatusDialogFragment : clearTopStatus() when (statusType) { StatusType.ONLINE -> { - binding.onlineStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.onlineStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary)) binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } StatusType.AWAY -> { - binding.awayStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.awayStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary)) binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } StatusType.DND -> { - binding.dndStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.dndStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary)) binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } StatusType.INVISIBLE -> { - binding.invisibleStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + binding.invisibleStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary)) binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) } else -> Log.d(logTag, "unknown status") @@ -380,10 +380,10 @@ class SetStatusDialogFragment : private fun clearTopStatus() { context?.let { val grey = it.resources.getColor(R.color.grey_200) - binding.onlineStatus.setBackgroundColor(grey) - binding.awayStatus.setBackgroundColor(grey) - binding.dndStatus.setBackgroundColor(grey) - binding.invisibleStatus.setBackgroundColor(grey) + binding.onlineStatus.setCardBackgroundColor(grey) + binding.awayStatus.setCardBackgroundColor(grey) + binding.dndStatus.setCardBackgroundColor(grey) + binding.invisibleStatus.setCardBackgroundColor(grey) binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) diff --git a/app/src/main/res/layout/dialog_set_status.xml b/app/src/main/res/layout/dialog_set_status.xml index 5f3b184cb..4af46709e 100644 --- a/app/src/main/res/layout/dialog_set_status.xml +++ b/app/src/main/res/layout/dialog_set_status.xml @@ -62,7 +62,8 @@ android:layout_weight="1" android:orientation="horizontal" app:cardBackgroundColor="@color/grey_200" - app:cardElevation="0dp"> + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/button_corner_radius"> + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/button_corner_radius"> + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/button_corner_radius"> + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/button_corner_radius"> Date: Fri, 25 Feb 2022 00:10:24 +0100 Subject: [PATCH 48/56] fix vertical alignment for MentionAutocompleteItem's Signed-off-by: Marcel Hibbe --- .../talk/adapters/items/MentionAutocompleteItem.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index 71a508b7a..4795851e7 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -192,9 +192,10 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem Date: Fri, 25 Feb 2022 11:19:17 +0100 Subject: [PATCH 49/56] migrate FlexibleItems to native view bindings Signed-off-by: Andy Scherzinger --- .../talk/adapters/items/AdvancedUserItem.java | 60 ++++++------- .../adapters/items/BrowserFileItem.java | 90 +++++++++---------- 2 files changed, 68 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java index d1eb5beb3..19a0b9d4b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java @@ -2,6 +2,8 @@ * Nextcloud Talk application * * @author Mario Danic + * @author Andy Scherzinger + * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) * * This program is free software: you can redistribute it and/or modify @@ -24,17 +26,12 @@ import android.accounts.Account; import android.net.Uri; import android.text.TextUtils; import android.view.View; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; -import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.databinding.AccountItemBinding; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.utils.ApiUtils; @@ -44,9 +41,6 @@ import java.util.List; import java.util.regex.Pattern; import androidx.annotation.Nullable; -import androidx.emoji.widget.EmojiTextView; -import butterknife.BindView; -import butterknife.ButterKnife; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; import eu.davidea.flexibleadapter.items.IFilterable; @@ -56,10 +50,10 @@ import eu.davidea.viewholders.FlexibleViewHolder; public class AdvancedUserItem extends AbstractFlexibleItem implements IFilterable { - private Participant participant; - private UserEntity userEntity; + private final Participant participant; + private final UserEntity userEntity; @Nullable - private Account account; + private final Account account; public AdvancedUserItem(Participant participant, UserEntity userEntity, @Nullable Account account) { this.participant = participant; @@ -110,68 +104,70 @@ public class AdvancedUserItem extends AbstractFlexibleItem * Copyright (C) 2017-2018 Mario Danic * * This program is free software: you can redistribute it and/or modify @@ -24,20 +26,14 @@ import android.content.Context; import android.text.format.Formatter; import android.view.View; import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; -import androidx.appcompat.content.res.AppCompatResources; -import autodagger.AutoInjector; -import butterknife.BindView; -import butterknife.ButterKnife; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; -import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.components.filebrowser.models.BrowserFile; +import com.nextcloud.talk.databinding.RvItemBrowserFileBinding; import com.nextcloud.talk.interfaces.SelectionInterface; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.utils.ApiUtils; @@ -49,9 +45,8 @@ import java.util.List; import javax.inject.Inject; +import androidx.appcompat.content.res.AppCompatResources; import autodagger.AutoInjector; -import butterknife.BindView; -import butterknife.ButterKnife; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; import eu.davidea.flexibleadapter.items.IFilterable; @@ -59,12 +54,12 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.FlexibleViewHolder; @AutoInjector(NextcloudTalkApplication.class) -public class BrowserFileItem extends AbstractFlexibleItem implements IFilterable { +public class BrowserFileItem extends AbstractFlexibleItem implements IFilterable { @Inject Context context; - private BrowserFile browserFile; - private UserEntity activeUser; - private SelectionInterface selectionInterface; + private final BrowserFile browserFile; + private final UserEntity activeUser; + private final SelectionInterface selectionInterface; private boolean selected; public BrowserFileItem(BrowserFile browserFile, UserEntity activeUser, SelectionInterface selectionInterface) { @@ -94,9 +89,8 @@ public class BrowserFileItem extends AbstractFlexibleItem adapter) { - return new ViewHolder(view, adapter); - + public BrowserFileItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new BrowserFileItemViewHolder(view, adapter); } private boolean isSelected() { @@ -108,8 +102,11 @@ public class BrowserFileItem extends AbstractFlexibleItem adapter, ViewHolder holder, int position, List payloads) { - holder.fileIconImageView.setController(null); + public void bindViewHolder(FlexibleAdapter adapter, + BrowserFileItemViewHolder holder, + int position, + List payloads) { + holder.binding.fileIcon.setController(null); if (!browserFile.isAllowedToReShare() || browserFile.isEncrypted()) { holder.itemView.setEnabled(false); holder.itemView.setAlpha(0.38f); @@ -119,31 +116,32 @@ public class BrowserFileItem extends AbstractFlexibleItem Date: Fri, 25 Feb 2022 11:30:20 +0100 Subject: [PATCH 50/56] adapt license header to app Signed-off-by: Andy Scherzinger --- .../nextcloud/talk/adapters/PredefinedStatusClickListener.kt | 3 +-- .../nextcloud/talk/adapters/PredefinedStatusListAdapter.kt | 3 +-- .../com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt | 3 +-- .../com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt | 4 ++-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt index cb07e5943..ff25288a4 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusClickListener.kt @@ -1,6 +1,5 @@ /* - * - * Nextcloud Android client application + * Nextcloud Talk application * * @author Tobias Kaminsky * Copyright (C) 2020 Tobias Kaminsky diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt index 59a8c50c0..48617748e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusListAdapter.kt @@ -1,6 +1,5 @@ /* - * - * Nextcloud Android client application + * Nextcloud Talk application * * @author Tobias Kaminsky * Copyright (C) 2020 Tobias Kaminsky diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt index c8e094bf4..1dae3149f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt @@ -1,6 +1,5 @@ /* - * - * Nextcloud Android client application + * Nextcloud Talk application * * @author Tobias Kaminsky * Copyright (C) 2020 Tobias Kaminsky diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index 9f5922887..898564b8a 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -1,6 +1,5 @@ - /* - * Nextcloud Android client application + * Nextcloud Talk application * * @author Tobias Kaminsky * @author Marcel Hibbe @@ -20,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public * License along with this program. If not, see . */ + package com.nextcloud.talk.ui.dialog import android.annotation.SuppressLint From d3c46e72f7b557d5a73625f4a4e44c9fbf76d3a4 Mon Sep 17 00:00:00 2001 From: drone Date: Fri, 25 Feb 2022 10:33:52 +0000 Subject: [PATCH 51/56] Drone: update Lint results to reflect reduced error/warning count [skip ci] Signed-off-by: drone --- scripts/analysis/lint-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 909332510..06528635e 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 1 error and 208 warnings + Lint Report: 1 error and 205 warnings From 0dc42a1b1ed87d0a41d1f80278c2a839acb6878e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 25 Feb 2022 11:20:34 +0100 Subject: [PATCH 52/56] fix NPE if status is null Signed-off-by: Marcel Hibbe --- app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java b/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java index 1fca690ca..258263327 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java +++ b/app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java @@ -56,7 +56,7 @@ public class StatusDrawable extends Drawable { if ("dnd".equals(status)) { icon = R.drawable.ic_user_status_dnd; this.context = context; - } else if (TextUtils.isEmpty(statusIcon)) { + } else if (TextUtils.isEmpty(statusIcon) && status != null) { switch (status) { case "online": icon = R.drawable.online_status; From a3e86d841f064305573a1817f5520f7b106f2a24 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Feb 2022 12:23:34 +0100 Subject: [PATCH 53/56] correct enum comparisons and add constants for fixed values Signed-off-by: Andy Scherzinger --- .../talk/activities/CallActivity.java | 132 ++++++++++++------ 1 file changed, 88 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index f9ec0ff3b..f6d685005 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -160,6 +160,9 @@ import pub.devrel.easypermissions.AfterPermissionGranted; @AutoInjector(NextcloudTalkApplication.class) public class CallActivity extends CallBaseActivity { + public static final String VIDEO_STREAM_TYPE_SCREEN = "screen"; + public static final String VIDEO_STREAM_TYPE_VIDEO = "video"; + @Inject NcApi ncApi; @Inject @@ -396,7 +399,8 @@ public class CallActivity extends CallBaseActivity { PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory( rootEglBase.getEglBaseContext(), true, true); - DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext()); + DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory( + rootEglBase.getEglBaseContext()); peerConnectionFactory = PeerConnectionFactory.builder() .setOptions(options) @@ -436,7 +440,8 @@ public class CallActivity extends CallBaseActivity { offerToReceiveVideoString = "false"; } - sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", offerToReceiveVideoString)); + sdpConstraints.mandatory.add( + new MediaConstraints.KeyValuePair("OfferToReceiveVideo", offerToReceiveVideoString)); sdpConstraintsForMCU.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false")); sdpConstraintsForMCU.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false")); @@ -600,7 +605,8 @@ public class CallActivity extends CallBaseActivity { Log.d(TAG, "initGridAdapter"); int columns; int participantsInGrid = participantDisplayItems.size(); - if (getResources() != null && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + if (getResources() != null + && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { if (participantsInGrid > 2) { columns = 2; } else { @@ -618,7 +624,9 @@ public class CallActivity extends CallBaseActivity { binding.gridview.setNumColumns(columns); - binding.conversationRelativeLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + binding.conversationRelativeLayout + .getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { binding.conversationRelativeLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); @@ -627,7 +635,10 @@ public class CallActivity extends CallBaseActivity { } }); - binding.callInfosLinearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + binding + .callInfosLinearLayout + .getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { binding.callInfosLinearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); @@ -776,7 +787,8 @@ public class CallActivity extends CallBaseActivity { //Create a VideoSource instance if (videoCapturer != null) { - SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase.getEglBaseContext()); + SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", + rootEglBase.getEglBaseContext()); videoSource = peerConnectionFactory.createVideoSource(false); videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver()); } @@ -1141,14 +1153,19 @@ public class CallActivity extends CallBaseActivity { @Override public void onNext(@io.reactivex.annotations.NonNull SignalingSettingsOverall signalingSettingsOverall) { - if (signalingSettingsOverall.getOcs() != null && signalingSettingsOverall.getOcs().getSettings() != null) { + if (signalingSettingsOverall.getOcs() != null + && signalingSettingsOverall.getOcs().getSettings() != null) { externalSignalingServer = new ExternalSignalingServer(); - if (!TextUtils.isEmpty(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()) && - !TextUtils.isEmpty(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket())) { + if (!TextUtils.isEmpty( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()) && + !TextUtils.isEmpty( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket())) { externalSignalingServer = new ExternalSignalingServer(); - externalSignalingServer.setExternalSignalingServer(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()); - externalSignalingServer.setExternalSignalingTicket(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket()); + externalSignalingServer.setExternalSignalingServer( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()); + externalSignalingServer.setExternalSignalingTicket( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket()); hasExternalSignalingServer = true; } else { hasExternalSignalingServer = false; @@ -1157,8 +1174,17 @@ public class CallActivity extends CallBaseActivity { if (!conversationUser.getUserId().equals("?")) { try { - userUtils.createOrUpdateUser(null, null, null, null, null, null, null, - conversationUser.getId(), null, null, LoganSquare.serialize(externalSignalingServer)) + userUtils.createOrUpdateUser(null, + null, + null, + null, + null, + null, + null, + conversationUser.getId(), + null, + null, + LoganSquare.serialize(externalSignalingServer)) .subscribeOn(Schedulers.io()) .subscribe(); } catch (IOException exception) { @@ -1547,14 +1573,16 @@ public class CallActivity extends CallBaseActivity { sessionDescriptionStringWithPreferredCodec); if (peerConnectionWrapper.getPeerConnection() != null) { - peerConnectionWrapper.getPeerConnection().setRemoteDescription(peerConnectionWrapper - .getMagicSdpObserver(), sessionDescriptionWithPreferredCodec); + peerConnectionWrapper.getPeerConnection().setRemoteDescription( + peerConnectionWrapper.getMagicSdpObserver(), + sessionDescriptionWithPreferredCodec); } break; case "candidate": NCIceCandidate ncIceCandidate = ncSignalingMessage.getPayload().getIceCandidate(); IceCandidate iceCandidate = new IceCandidate(ncIceCandidate.getSdpMid(), - ncIceCandidate.getSdpMLineIndex(), ncIceCandidate.getCandidate()); + ncIceCandidate.getSdpMLineIndex(), + ncIceCandidate.getCandidate()); peerConnectionWrapper.addCandidate(iceCandidate); break; case "endOfCandidates": @@ -1651,7 +1679,8 @@ public class CallActivity extends CallBaseActivity { public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) { if (shutDownView) { finish(); - } else if (currentCallStatus == CallStatus.RECONNECTING || currentCallStatus == CallStatus.PUBLISHER_FAILED) { + } else if (currentCallStatus == CallStatus.RECONNECTING + || currentCallStatus == CallStatus.PUBLISHER_FAILED) { initiateCall(); } } @@ -1694,7 +1723,10 @@ public class CallActivity extends CallBaseActivity { long inCallFlag = (long) participant.get("inCall"); if (!participant.get("sessionId").equals(currentSessionId)) { boolean isNewSession; - Log.d(TAG, " inCallFlag of participant " + participant.get("sessionId").toString().substring(0, 4) + " : " + inCallFlag); + Log.d(TAG, " inCallFlag of participant " + + participant.get("sessionId").toString().substring(0, 4) + + " : " + + inCallFlag); isNewSession = inCallFlag != 0; if (isNewSession) { @@ -1733,12 +1765,12 @@ public class CallActivity extends CallBaseActivity { if (hasMCU) { // Ensure that own publishing peer is set up. - getPeerConnectionWrapperForSessionIdAndType(webSocketClient.getSessionId(), "video", true); + getPeerConnectionWrapperForSessionIdAndType(webSocketClient.getSessionId(), VIDEO_STREAM_TYPE_VIDEO, true); } for (String sessionId : newSessions) { Log.d(TAG, " newSession joined: " + sessionId); - getPeerConnectionWrapperForSessionIdAndType(sessionId, "video", false); + getPeerConnectionWrapperForSessionIdAndType(sessionId, VIDEO_STREAM_TYPE_VIDEO, false); } if (newSessions.size() > 0 && !currentCallStatus.equals(CallStatus.IN_CONVERSATION)) { @@ -1790,7 +1822,8 @@ public class CallActivity extends CallBaseActivity { private PeerConnectionWrapper getPeerConnectionWrapperForSessionId(String sessionId, String type) { for (int i = 0; i < peerConnectionWrapperList.size(); i++) { - if (peerConnectionWrapperList.get(i).getSessionId().equals(sessionId) && peerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) { + if (peerConnectionWrapperList.get(i).getSessionId().equals(sessionId) + && peerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) { return peerConnectionWrapperList.get(i); } } @@ -1798,7 +1831,9 @@ public class CallActivity extends CallBaseActivity { return null; } - private PeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId, String type, boolean publisher) { + private PeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId, + String type, + boolean publisher) { PeerConnectionWrapper peerConnectionWrapper; if ((peerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId, type)) != null) { return peerConnectionWrapper; @@ -1876,7 +1911,7 @@ public class CallActivity extends CallBaseActivity { for (int i = 0; i < peerConnectionWrappers.size(); i++) { peerConnectionWrapper = peerConnectionWrappers.get(i); if (peerConnectionWrapper.getSessionId().equals(sessionId)) { - if (peerConnectionWrapper.getVideoStreamType().equals("screen") || !justScreen) { + if (VIDEO_STREAM_TYPE_SCREEN.equals(peerConnectionWrapper.getVideoStreamType()) || !justScreen) { runOnUiThread(() -> removeMediaStream(sessionId)); deletePeerConnection(peerConnectionWrapper); } @@ -1904,7 +1939,8 @@ public class CallActivity extends CallBaseActivity { private void updateSelfVideoViewPosition() { Log.d(TAG, "updateSelfVideoViewPosition"); if (!isInPipMode) { - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) binding.selfVideoRenderer.getLayoutParams(); + FrameLayout.LayoutParams layoutParams = + (FrameLayout.LayoutParams) binding.selfVideoRenderer.getLayoutParams(); DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics(); int screenWidthPx = displayMetrics.widthPixels; @@ -1941,42 +1977,46 @@ public class CallActivity extends CallBaseActivity { public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) { String sessionId = peerConnectionEvent.getSessionId(); - if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType - .PEER_CLOSED)) { - endPeerConnection(sessionId, peerConnectionEvent.getVideoStreamType().equals("screen")); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_FAR) || - peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_NEAR)) { + if (peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.PEER_CLOSED) { + endPeerConnection(sessionId, VIDEO_STREAM_TYPE_SCREEN.equals(peerConnectionEvent.getVideoStreamType())); + } else if (peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.SENSOR_FAR || + peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.SENSOR_NEAR) { if (!isVoiceOnlyCall) { - boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_FAR) && videoOn; + boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.SENSOR_FAR && videoOn; if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA) && (currentCallStatus.equals(CallStatus.CONNECTING) || isConnectionEstablished()) && videoOn && enableVideo != localVideoTrack.enabled()) { toggleMedia(enableVideo, true); } } - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE)) { + } else if (peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE) { if (participantDisplayItems.get(sessionId) != null) { participantDisplayItems.get(sessionId).setNick(peerConnectionEvent.getNick()); } participantsAdapter.notifyDataSetChanged(); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { + } else if (peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE && !isVoiceOnlyCall) { if (participantDisplayItems.get(sessionId) != null) { participantDisplayItems.get(sessionId).setStreamEnabled(peerConnectionEvent.getChangeValue()); } participantsAdapter.notifyDataSetChanged(); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE)) { + } else if (peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE) { if (participantDisplayItems.get(sessionId) != null) { participantDisplayItems.get(sessionId).setAudioEnabled(peerConnectionEvent.getChangeValue()); } participantsAdapter.notifyDataSetChanged(); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) { + } else if (peerConnectionEvent.getPeerConnectionEventType() == + PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED) { currentCallStatus = CallStatus.PUBLISHER_FAILED; webSocketClient.clearResumeId(); hangup(false); @@ -2074,7 +2114,8 @@ public class CallActivity extends CallBaseActivity { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("{") .append("\"fn\":\"") - .append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))).append("\"") + .append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))) + .append("\"") .append(",") .append("\"sessionId\":") .append("\"").append(StringEscapeUtils.escapeJson(callSession)).append("\"") @@ -2127,7 +2168,10 @@ public class CallActivity extends CallBaseActivity { this); } - private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, boolean videoStreamEnabled, String videoStreamType) { + private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, + String session, + boolean videoStreamEnabled, + String videoStreamType) { String nick; if (hasExternalSignalingServer) { nick = webSocketClient.getDisplayNameForSession(session); @@ -2416,13 +2460,12 @@ public class CallActivity extends CallBaseActivity { @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(NetworkEvent networkEvent) { - if (networkEvent.getNetworkConnectionEvent() - .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) { + if (networkEvent.getNetworkConnectionEvent() == NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED) { if (handler != null) { handler.removeCallbacksAndMessages(null); } - } else if (networkEvent.getNetworkConnectionEvent() - .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) { + } else if (networkEvent.getNetworkConnectionEvent() == + NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED) { if (handler != null) { handler.removeCallbacksAndMessages(null); } @@ -2516,7 +2559,8 @@ public class CallActivity extends CallBaseActivity { if (isVoiceOnlyCall) { binding.callControls.setVisibility(View.VISIBLE); } else { - binding.callControls.setVisibility(View.INVISIBLE); // animateCallControls needs this to be invisible for a check. + // animateCallControls needs this to be invisible for a check. + binding.callControls.setVisibility(View.INVISIBLE); } initViews(); From 5c86629b533fd2429e61ae5ab872237404b2727f Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Feb 2022 12:25:52 +0100 Subject: [PATCH 54/56] fix license header mail format Signed-off-by: Andy Scherzinger --- .../com/nextcloud/talk/adapters/items/ConversationItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index 2c88334a8..f81664e07 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -4,7 +4,7 @@ * @author Mario Danic * @author Andy Scherzinger * @author Marcel Hibbe - * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de) + * Copyright (C) 2022 Marcel Hibbe * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017-2018 Mario Danic * From 9d29cbf9cddcb425ed6d6d8ba085a775c91b18bb Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 25 Feb 2022 12:28:25 +0100 Subject: [PATCH 55/56] fix vertical alignments for UserItem Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/adapters/items/UserItem.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 606028377..78ce884c5 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 @@ -281,9 +281,10 @@ public class UserItem extends AbstractFlexibleItem if (participant.statusMessage != null) { holder.statusMessage.setText(participant.statusMessage); + alignUsernameVertical(holder, 0); } else { holder.statusMessage.setText(""); - alignUsernameVertical(holder); + alignUsernameVertical(holder, 10); } if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) { @@ -304,10 +305,10 @@ public class UserItem extends AbstractFlexibleItem } } - private void alignUsernameVertical(UserItemViewHolder holder) { + private void alignUsernameVertical(UserItem.UserItemViewHolder holder, float densityPixelsFromTop) { ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) holder.contactDisplayName.getLayoutParams(); - layoutParams.topMargin = (int) DisplayUtils.convertDpToPixel(10, context); + layoutParams.topMargin = (int) DisplayUtils.convertDpToPixel(densityPixelsFromTop, context); holder.contactDisplayName.setLayoutParams(layoutParams); } From dacaf5b76ba2837a18cd3f995b4ee37230cbe26f Mon Sep 17 00:00:00 2001 From: drone Date: Fri, 25 Feb 2022 11:49:33 +0000 Subject: [PATCH 56/56] Drone: update FindBugs results to reflect reduced error/warning count [skip ci] Signed-off-by: drone --- scripts/analysis/findbugs-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index df2d2b6ce..4f09af713 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -497 \ No newline at end of file +492 \ No newline at end of file