Эх сурвалжийг харах

add user status option to account dialog (WIP)

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 3 жил өмнө
parent
commit
05586ccf47
23 өөрчлөгдсөн 1780 нэмэгдсэн , 31 устгасан
  1. 7 0
      app/src/main/java/com/nextcloud/talk/api/NcApi.java
  2. 17 1
      app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java
  3. 160 0
      app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java
  4. 32 0
      app/src/main/java/com/nextcloud/talk/models/json/capabilities/UserStatusCapability.java
  5. 159 0
      app/src/main/java/com/nextcloud/talk/models/json/status/Status.java
  6. 69 0
      app/src/main/java/com/nextcloud/talk/models/json/status/StatusOCS.java
  7. 64 0
      app/src/main/java/com/nextcloud/talk/models/json/status/StatusOverall.java
  8. 86 29
      app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java
  9. 420 0
      app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt
  10. 8 0
      app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java
  11. 34 0
      app/src/main/res/drawable/ic_edit.xml
  12. 32 0
      app/src/main/res/drawable/ic_user_status_away.xml
  13. 38 0
      app/src/main/res/drawable/ic_user_status_dnd.xml
  14. 34 0
      app/src/main/res/drawable/ic_user_status_invisible.xml
  15. 23 0
      app/src/main/res/drawable/online_status.xml
  16. 38 1
      app/src/main/res/layout/dialog_choose_account.xml
  17. 453 0
      app/src/main/res/layout/dialog_set_status.xml
  18. 62 0
      app/src/main/res/layout/predefined_status.xml
  19. 2 0
      app/src/main/res/values-night/colors.xml
  20. 7 0
      app/src/main/res/values/colors.xml
  21. 6 0
      app/src/main/res/values/dimens.xml
  22. 21 0
      app/src/main/res/values/strings.xml
  23. 8 0
      app/src/main/res/values/styles.xml

+ 7 - 0
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);
 }

+ 17 - 1
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 {

+ 160 - 0
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() + ")";
+    }
+}

+ 32 - 0
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;
+    }
+}

+ 159 - 0
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 +
+            '}';
+    }
+}

+ 69 - 0
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 +
+            '}';
+    }
+
+}

+ 64 - 0
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 +
+            '}';
+    }
+}

+ 86 - 29
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;
-        }
-    };
 }

+ 420 - 0
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()
+    // }
+}

+ 8 - 0
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";
+    }
 }

+ 34 - 0
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>

+ 32 - 0
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>

+ 38 - 0
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>

+ 34 - 0
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>

+ 23 - 0
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>

+ 38 - 1
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"

+ 453 - 0
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>

+ 62 - 0
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>

+ 2 - 0
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>

+ 7 - 0
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>

+ 6 - 0
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>

+ 21 - 0
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>

+ 8 - 0
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>