Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/talk-android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
authorMarcel Hibbe <dev@mhibbe.de>2021-12-28 17:10:29 +0300
committerMarcel Hibbe <dev@mhibbe.de>2022-02-25 14:11:36 +0300
commit05586ccf47c8bfc24499fd6c06fe87543200a3f9 (patch)
tree5a359248b20b9534615d953628a25525ee8f2b0c /app/src
parent3f6ef9aed226d8c2a5085fe3a93afcd549e91947 (diff)
add user status option to account dialog (WIP)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/com/nextcloud/talk/api/NcApi.java7
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java18
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java160
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java32
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/status/Status.java159
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/status/StatusOCS.java69
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/status/StatusOverall.java64
-rw-r--r--app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java115
-rw-r--r--app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt420
-rw-r--r--app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java8
-rw-r--r--app/src/main/res/drawable/ic_edit.xml34
-rw-r--r--app/src/main/res/drawable/ic_user_status_away.xml32
-rw-r--r--app/src/main/res/drawable/ic_user_status_dnd.xml38
-rw-r--r--app/src/main/res/drawable/ic_user_status_invisible.xml34
-rw-r--r--app/src/main/res/drawable/online_status.xml23
-rw-r--r--app/src/main/res/layout/dialog_choose_account.xml39
-rw-r--r--app/src/main/res/layout/dialog_set_status.xml453
-rw-r--r--app/src/main/res/layout/predefined_status.xml62
-rw-r--r--app/src/main/res/values-night/colors.xml2
-rw-r--r--app/src/main/res/values/colors.xml7
-rw-r--r--app/src/main/res/values/dimens.xml6
-rw-r--r--app/src/main/res/values/strings.xml21
-rw-r--r--app/src/main/res/values/styles.xml8
23 files changed, 1780 insertions, 31 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 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<RoomsOverall> getOpenConversations(@Header("Authorization") String authorization, @Url String url);
+
+ /*
+ * OCS Status API
+ */
+ @GET
+ Observable<StatusOverall> 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 <mario@lovelyhq.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.models.json.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<String, List<String>> 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<String, List<String>> 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<String, List<String>> 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 <t@timkrueger.me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.talk.models.json.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 <t@timkrueger.me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.talk.models.json.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 <t@timkrueger.me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.talk.models.json.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<StatusOverall>() {
+
+ 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<UserEntity>() {
@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 <http://www.gnu.org/licenses/>.
+ */
+
+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<PredefinedStatus>
+ // 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<ArrayList<PredefinedStatus>>() {}.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<String>(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<PredefinedStatus>) {
+ // 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 @@
+<!--
+ ~
+ ~ Nextcloud Android client application
+ ~
+ ~ @author Tobias Kaminsky
+ ~ Copyright (C) 2019 Tobias Kaminsky
+ ~ Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector xmlns:tools="http://schemas.android.com/tools"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ tools:ignore="VectorRaster">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
+</vector>
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 @@
+<!--
+ 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
+ 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 <http://www.gnu.org/licenses/>.
+-->
+<vector xmlns:tools="http://schemas.android.com/tools"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ tools:ignore="VectorRaster">
+ <path
+ android:fillColor="#f4a331"
+ android:pathData="m10.615,2.1094c-4.8491,0.6811 -8.6152,4.8615 -8.6152,9.8906 0,5.5 4.5,10 10,10 5.0292,0 9.2096,-3.7661 9.8906,-8.6152 -1.4654,1.601 -3.5625,2.6152 -5.8906,2.6152 -4.4,0 -8,-3.6 -8,-8 0,-2.3281 1.0143,-4.4252 2.6152,-5.8906z" />
+</vector>
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 @@
+<!--
+ 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
+ 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 <http://www.gnu.org/licenses/>.
+-->
+<vector xmlns:tools="http://schemas.android.com/tools"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ tools:ignore="VectorRaster">
+ <path
+ android:fillColor="#ed484c"
+ android:pathData="m12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10 10,-4.48 10,-10 -4.48,-10 -10,-10z" />
+ <path
+ android:fillColor="#fdffff"
+ android:pathData="m8,10h8c1.108,0 2,0.892 2,2s-0.892,2 -2,2h-8c-1.108,0 -2,-0.892 -2,-2s0.892,-2 2,-2z"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+</vector>
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 @@
+<!--
+ ~
+ ~ 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector xmlns:tools="http://schemas.android.com/tools"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ tools:ignore="VectorRaster">
+ <path
+ android:fillColor="#000000"
+ android:pathData="m12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10 10,-4.48 10,-10 -4.48,-10 -10,-10zM12,6a6,6 0,0 1,6 6,6 6,0 0,1 -6,6 6,6 0,0 1,-6 -6,6 6,0 0,1 6,-6z" />
+</vector>
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 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Nextcloud Android client application
+
+ @author Andy Scherzinger
+ Copyright (C) 2019 Andy Scherzinger
+
+ 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 <http://www.gnu.org/licenses/>.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="#00ff00" />
+</shape>
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 @@
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -31,6 +32,42 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
+ <LinearLayout
+ android:id="@+id/statusView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/current_account"
+ tools:visibility="visible">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="4dp"
+ android:background="@color/list_divider_background" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/set_status"
+ style="@style/Nextcloud.Material.TextButton"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:paddingStart="10dp"
+ android:paddingEnd="0dp"
+ android:text="@string/set_status"
+ android:textAlignment="textStart"
+ android:textAllCaps="false"
+ android:textColor="@color/fontAppbar"
+ app:icon="@drawable/ic_edit"
+ app:iconGravity="start"
+ app:iconPadding="22dp"
+ app:iconTint="@color/fontAppbar" />
+ </LinearLayout>
+
<View
android:id="@+id/separator_line"
android:layout_width="0dp"
@@ -39,7 +76,7 @@
android:background="@color/controller_chat_separator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/current_account" />
+ app:layout_constraintTop_toBottomOf="@id/statusView" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/accounts_list"
diff --git a/app/src/main/res/layout/dialog_set_status.xml b/app/src/main/res/layout/dialog_set_status.xml
new file mode 100644
index 000000000..521f6db42
--- /dev/null
+++ b/app/src/main/res/layout/dialog_set_status.xml
@@ -0,0 +1,453 @@
+<!--
+ Nextcloud Android client application
+
+ Copyright (C) 2020 Andy Scherzinger
+ 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 General Public License version 2,
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="@dimen/standard_padding">
+
+ <TextView
+ android:id="@+id/onlineStatusView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/standard_half_margin"
+ android:text="@string/online_status"
+ android:textColor="@color/high_emphasis_text"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:id="@+id/statusView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="false"
+ android:orientation="vertical"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/onlineStatusView">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/standard_margin"
+ android:orientation="horizontal">
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/onlineStatus"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/online_status_item_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ app:cardBackgroundColor="@color/grey_200"
+ app:cardElevation="0dp">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/standard_margin"
+ android:layout_marginEnd="@dimen/standard_margin"
+ android:orientation="horizontal"
+ tools:ignore="UnusedAttribute">
+
+ <ImageView
+ android:id="@+id/online_icon"
+ android:layout_width="@dimen/iconized_single_line_item_icon_size"
+ android:layout_height="@dimen/iconized_single_line_item_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_gravity="top|start"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:contentDescription="@null"
+ android:src="@drawable/online_status"
+ app:tint="@color/hwSecurityGreen" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:layout_toEndOf="@id/online_icon"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/online_headline"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="bottom"
+ android:maxLines="1"
+ android:text="@string/online"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+ <TextView
+ android:id="@+id/online_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/standard_half_margin"
+ android:layout_marginBottom="@dimen/standard_quarter_margin"
+ android:ellipsize="end"
+ android:gravity="top"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </com.google.android.material.card.MaterialCardView>
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/awayStatus"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/online_status_item_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/standard_half_margin"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ app:cardBackgroundColor="@color/grey_200"
+ app:cardElevation="0dp">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/standard_margin"
+ android:layout_marginEnd="@dimen/standard_margin"
+ android:orientation="horizontal"
+ tools:ignore="UnusedAttribute">
+
+ <ImageView
+ android:id="@+id/away_icon"
+ android:layout_width="@dimen/iconized_single_line_item_icon_size"
+ android:layout_height="@dimen/iconized_single_line_item_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_gravity="top|start"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_user_status_away"
+ app:tint="#f4a331" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:layout_toEndOf="@id/away_icon"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/away_headline"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="bottom"
+ android:maxLines="1"
+ android:text="@string/away"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+ <TextView
+ android:id="@+id/away_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/standard_half_margin"
+ android:layout_marginBottom="@dimen/standard_quarter_margin"
+ android:ellipsize="end"
+ android:gravity="top"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </com.google.android.material.card.MaterialCardView>
+
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/standard_margin"
+ android:orientation="horizontal">
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/dndStatus"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/online_status_item_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ app:cardBackgroundColor="@color/grey_200"
+ app:cardElevation="0dp">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/standard_margin"
+ android:layout_marginEnd="@dimen/standard_margin"
+ android:orientation="horizontal"
+ tools:ignore="UnusedAttribute">
+
+ <ImageView
+ android:id="@+id/dnd_icon"
+ android:layout_width="@dimen/iconized_single_line_item_icon_size"
+ android:layout_height="@dimen/iconized_single_line_item_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_gravity="top|start"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_user_status_dnd" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:layout_toEndOf="@id/dnd_icon"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/dnd_headline"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="bottom"
+ android:maxLines="1"
+ android:text="@string/dnd"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </com.google.android.material.card.MaterialCardView>
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/invisibleStatus"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/online_status_item_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/standard_half_margin"
+
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ app:cardBackgroundColor="@color/grey_200"
+ app:cardElevation="0dp">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/standard_margin"
+ android:layout_marginEnd="@dimen/standard_margin"
+ android:orientation="horizontal"
+ tools:ignore="UnusedAttribute">
+
+ <ImageView
+ android:id="@+id/invisible_icon"
+ android:layout_width="@dimen/iconized_single_line_item_icon_size"
+ android:layout_height="@dimen/iconized_single_line_item_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_gravity="top|start"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_user_status_invisible" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:layout_toEndOf="@id/invisible_icon"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/invisible_headline"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="bottom"
+ android:maxLines="1"
+ android:text="@string/invisible"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </com.google.android.material.card.MaterialCardView>
+
+ </LinearLayout>
+ </LinearLayout>
+
+
+ <View
+ android:id="@+id/separator_line"
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_marginTop="@dimen/standard_quarter_margin"
+ android:background="@color/list_divider_background"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/statusView" />
+
+ <LinearLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/statusMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/standard_half_margin"
+ android:text="@string/status_message"
+ android:textColor="@color/high_emphasis_text"
+ android:textSize="@dimen/activity_list_item_title_header_text_size"
+ app:layout_constraintTop_toBottomOf="@+id/statusView" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/emojiCard"
+ android:layout_width="@dimen/activity_row_layout_height"
+ android:layout_height="@dimen/activity_row_layout_height"
+ android:layout_gravity="center"
+ android:layout_marginTop="@dimen/standard_eighth_margin"
+ android:layout_marginEnd="@dimen/standard_margin"
+ android:orientation="horizontal"
+ app:cardBackgroundColor="@color/grey_200"
+ app:cardCornerRadius="24dp"
+ app:cardElevation="0dp">
+
+ <com.vanniktech.emoji.EmojiEditText
+ android:id="@+id/emoji"
+ android:layout_width="@dimen/activity_row_layout_height"
+ android:layout_height="@dimen/activity_row_layout_height"
+ android:background="@color/grey_200"
+ android:cursorVisible="false"
+ android:gravity="center"
+ android:text="@string/default_emoji"
+ android:textSize="24sp" />
+
+ </com.google.android.material.card.MaterialCardView>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/customStatusInput_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="@string/whats_your_status">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/customStatusInput"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:importantForAutofill="no"
+ android:inputType="textAutoCorrect"
+ android:scrollbars="vertical">
+
+ </com.google.android.material.textfield.TextInputEditText>
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ </LinearLayout>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/predefinedStatusList"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:itemCount="5"
+ tools:listitem="@layout/predefined_status" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/standard_half_margin"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/clearStatusMessageTextView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/clear_status_message_after"
+ android:textColor="@color/high_emphasis_text" />
+
+ <Spinner
+ android:id="@+id/clearStatusAfterSpinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/remainingClearTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/high_emphasis_text"
+ android:layout_marginStart="4dp"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/standard_half_margin"
+ android:orientation="horizontal">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/clearStatus"
+ style="@style/OutlinedButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/standard_half_margin"
+ android:layout_weight="1"
+ android:text="@string/clear_status_message"
+ app:cornerRadius="@dimen/button_corner_radius" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/setStatus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/set_status_message"
+ android:theme="@style/Button.Primary"
+ app:cornerRadius="@dimen/button_corner_radius" />
+
+ </LinearLayout>
+
+</LinearLayout>
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 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+
+ Nextcloud Android client application
+
+ Copyright (C) 2020 Andy Scherzinger
+ 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 <https://www.gnu.org/licenses/>.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="48dp">
+
+ <TextView
+ android:id="@+id/icon"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textSize="25sp"
+ tools:text="📆" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ tools:text="In a meeting" />
+
+ <TextView
+ android:id="@+id/divider"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/standard_half_margin"
+ android:gravity="center_vertical"
+ android:text="@string/divider"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <TextView
+ android:id="@+id/clearAt"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textColor="?android:attr/textColorSecondary"
+ tools:text="an hour" />
+</LinearLayout>
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 @@
<!-- shimmer element colors -->
<color name="nc_shimmer_default_color">#4B4B4B</color>
<color name="nc_shimmer_darker_color">#282828</color>
+
+ <color name="list_divider_background">#222222</color>
</resources>
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 @@
<color name="camera_bg_tint">#99121212</color>
+ <color name="list_divider_background">#eeeeee</color>
+ <color name="grey_200">#818181</color>
+ <color name="secondary_button_background_color">#D6D7D7</color>
+ <color name="secondary_button_text_color">#000000</color>
+ <color name="primary_button_background_color">#007cc2</color>
+ <color name="primary_button_text_color">#ffffff</color>
+
</resources>
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 @@
<dimen name="call_grid_item_min_height">180dp</dimen>
<dimen name="call_controls_height">110dp</dimen>
<dimen name="zero">0dp</dimen>
+
+ <dimen name="online_status_item_height">52dp</dimen>
+ <dimen name="standard_quarter_margin">4dp</dimen>
+ <dimen name="activity_list_item_title_header_text_size">16sp</dimen>
+ <dimen name="activity_row_layout_height">48dp</dimen>
+ <dimen name="standard_eighth_margin">2dp</dimen>
</resources>
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 @@
<string name="nc_remove_group_and_members">Remove group and members</string>
<string name="nc_attendee_pin">Pin: %1$s</string>
+ <!-- User Status -->
+ <string name="set_status">Set status</string>
+ <string name="online_status">Online status</string>
+ <string name="status_message">Status message</string>
+ <string name="whats_your_status">What is your status?</string>
+ <string name="clear_status_message_after">Clear status message after</string>
+ <string name="clear_status_message">Clear status message</string>
+ <string name="set_status_message">Set status message</string>
+ <string name="online">Online</string>
+ <string name="dnd">Do not disturb</string>
+ <string name="away">Away</string>
+ <string name="invisible">Invisible</string>
+ <string translatable="false" name="divider">—</string>
+ <string translatable="false" name="default_emoji">😃</string>
+ <string name="dontClear">Don\'t clear</string>
+ <string name="today">Today</string>
+ <string name="thirtyMinutes">30 minutes</string>
+ <string name="oneHour">1 hour</string>
+ <string name="fourHours">4 hours</string>
+ <string name="thisWeek">This week</string>
+
<!-- Conversations List-->
<string name="nc_new_mention">Unread mentions</string>
<string name="conversations">Conversations</string>
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 @@
<item name="android:windowSoftInputMode">adjustResize</item>
</style>
+ <style name="OutlinedButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
+ <item name="colorAccent">@color/transparent</item>
+ <item name="android:textColor">@color/colorPrimaryDark</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:typeface">sans</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
</resources>