show contact status in a separate widget

This commit is contained in:
kosyak 2024-08-05 22:34:10 +02:00
parent f709e32805
commit cf9d0c4f09
13 changed files with 159 additions and 4 deletions

View file

@ -197,10 +197,6 @@ public class Contact implements ListItem, Blockable {
final HashSet<Tag> tags = new HashSet<>(); final HashSet<Tag> tags = new HashSet<>();
tags.addAll(getGroupTags()); tags.addAll(getGroupTags());
Presence.Status status = getShownStatus();
if (status != Presence.Status.OFFLINE) {
tags.add(UIHelper.getTagForStatus(context, status));
}
if (isBlocked()) { if (isBlocked()) {
tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b)); tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b));
} }

View file

@ -523,6 +523,8 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size); AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size);
binding.detailsContactBadge.setOnClickListener(this::onBadgeClick); binding.detailsContactBadge.setOnClickListener(this::onBadgeClick);
binding.presenceIndicator.setStatus(contact.getShownStatus());
binding.detailsContactKeys.removeAllViews(); binding.detailsContactKeys.removeAllViews();
boolean hasKeys = false; boolean hasKeys = false;
final LayoutInflater inflater = getLayoutInflater(); final LayoutInflater inflater = getLayoutInflater();

View file

@ -32,6 +32,7 @@ import java.util.Set;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ConversationListRowBinding; import eu.siacs.conversations.databinding.ConversationListRowBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
@ -388,6 +389,14 @@ public class ConversationAdapter
viewHolder.binding.conversationName.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null); viewHolder.binding.conversationName.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null);
} }
Contact contact = conversation.getContact();
if (contact != null) {
viewHolder.binding.presenceIndicator.setStatus(contact.getShownStatus());
} else {
viewHolder.binding.presenceIndicator.setStatus(null);
}
if (draft != null) { if (draft != null) {
viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE); viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
viewHolder.binding.conversationLastmsg.setText(draft.getMessage()); viewHolder.binding.conversationLastmsg.setText(draft.getMessage());

View file

@ -17,11 +17,13 @@ import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ContactBinding; import eu.siacs.conversations.databinding.ContactBinding;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.SettingsActivity; import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes; import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.ui.widget.PresenceIndicator;
import eu.siacs.conversations.utils.IrregularUnicodeDetector; import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
@ -86,6 +88,13 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
} }
viewHolder.name.setText(item.getDisplayName()); viewHolder.name.setText(item.getDisplayName());
AvatarWorkerTask.loadAvatar(item, viewHolder.avatar, R.dimen.avatar); AvatarWorkerTask.loadAvatar(item, viewHolder.avatar, R.dimen.avatar);
if (item instanceof Contact) {
viewHolder.presenceIndicator.setStatus(((Contact) item).getShownStatus());
} else {
viewHolder.presenceIndicator.setStatus(null);
}
return view; return view;
} }
@ -104,6 +113,8 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
private ImageView avatar; private ImageView avatar;
private FlowLayout tags; private FlowLayout tags;
private PresenceIndicator presenceIndicator;
private ViewHolder() { private ViewHolder() {
} }
@ -114,6 +125,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
viewHolder.jid = binding.contactJid; viewHolder.jid = binding.contactJid;
viewHolder.avatar = binding.contactPhoto; viewHolder.avatar = binding.contactPhoto;
viewHolder.tags = binding.tags; viewHolder.tags = binding.tags;
viewHolder.presenceIndicator = binding.presenceIndicator;
binding.getRoot().setTag(viewHolder); binding.getRoot().setTag(viewHolder);
return viewHolder; return viewHolder;
} }

View file

@ -91,9 +91,11 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
} else { } else {
viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode)); viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
} }
viewHolder.binding.presenceIndicator.setStatus(contact.getShownStatus());
} else { } else {
viewHolder.binding.contactDisplayName.setText(name == null ? "" : name); viewHolder.binding.contactDisplayName.setText(name == null ? "" : name);
viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode)); viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
viewHolder.binding.presenceIndicator.setStatus(null);
} }
if (advancedMode && user.getPgpKeyId() != 0) { if (advancedMode && user.getPgpKeyId() != 0) {
viewHolder.binding.key.setVisibility(View.VISIBLE); viewHolder.binding.key.setVisibility(View.VISIBLE);

View file

@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.UserPreviewBinding; import eu.siacs.conversations.databinding.UserPreviewBinding;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
@ -35,6 +36,12 @@ public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreview
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
final MucOptions.User user = getItem(position); final MucOptions.User user = getItem(position);
AvatarWorkerTask.loadAvatar(user, viewHolder.binding.avatar, R.dimen.media_size); AvatarWorkerTask.loadAvatar(user, viewHolder.binding.avatar, R.dimen.media_size);
Contact contact = user.getContact();
if (contact != null) {
viewHolder.binding.presenceIndicator.setStatus(user.getContact().getShownStatus());
} else {
viewHolder.binding.presenceIndicator.setStatus(null);
}
viewHolder.binding.getRoot().setOnClickListener(v -> { viewHolder.binding.getRoot().setOnClickListener(v -> {
final XmppActivity activity = XmppActivity.find(v); final XmppActivity activity = XmppActivity.find(v);
if (activity != null) { if (activity != null) {

View file

@ -0,0 +1,64 @@
package eu.siacs.conversations.ui.widget
import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Outline
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import eu.siacs.conversations.entities.Presence
import eu.siacs.conversations.ui.util.StyledAttributes
import eu.siacs.conversations.utils.UIHelper
class PresenceIndicator : View {
private var paint: Paint = Paint().also {
it.setColor(StyledAttributes.getColor(this.context, androidx.appcompat.R.attr.colorPrimaryDark))
it.style = Paint.Style.STROKE
it.strokeWidth = 1 * Resources.getSystem().displayMetrics.density
}
var status: Presence.Status? = null
set(value) {
if (field != value) {
field = value
invalidate()
}
}
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
init {
clipToOutline = true
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setOval(0, 0, view.width, view.height)
}
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val color: Int? = UIHelper.getColorForStatus(status);
if (color != null) {
canvas.drawColor(color)
canvas.drawCircle(width / 2f, height / 2f, width / 2f, paint)
}
}
}

View file

@ -7,6 +7,7 @@ import android.text.format.DateUtils;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ -608,6 +609,28 @@ public class UIHelper {
} }
} }
@Nullable
public static Integer getColorForStatus(Presence.Status status) {
if (status == null) {
return null;
}
switch (status) {
case CHAT:
return 0xff259b24;
case AWAY:
return 0xffff9800;
case XA:
return 0xfff44336;
case DND:
return 0xfff44336;
case ONLINE:
return 0xff259b24;
default:
return null;
}
}
public static boolean isStatusTag(Context context, ListItem.Tag tag) { public static boolean isStatusTag(Context context, ListItem.Tag tag) {
String name = tag.getName(); String name = tag.getName();
return name.equals(context.getString(R.string.presence_chat)) || return name.equals(context.getString(R.string.presence_chat)) ||

View file

@ -42,6 +42,16 @@
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:scaleType="centerCrop" /> android:scaleType="centerCrop" />
<eu.siacs.conversations.ui.widget.PresenceIndicator
android:id="@+id/presence_indicator"
android:layout_gravity="bottom|end"
android:layout_width="@dimen/presence_indicator_size"
android:layout_height="@dimen/presence_indicator_size"
android:layout_marginEnd="@dimen/presence_indicator_offset"
android:layout_marginBottom="@dimen/presence_indicator_offset"
android:layout_alignBottom="@id/details_contact_badge"
android:layout_alignEnd="@id/details_contact_badge" />
<LinearLayout <LinearLayout
android:id="@+id/details_jidbox" android:id="@+id/details_jidbox"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -15,6 +15,16 @@
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:scaleType="centerCrop"/> android:scaleType="centerCrop"/>
<eu.siacs.conversations.ui.widget.PresenceIndicator
android:id="@+id/presence_indicator"
android:layout_gravity="bottom|end"
android:layout_width="@dimen/presence_indicator_size"
android:layout_height="@dimen/presence_indicator_size"
android:layout_marginEnd="@dimen/presence_indicator_offset"
android:layout_marginBottom="@dimen/presence_indicator_offset"
android:layout_alignBottom="@id/contact_photo"
android:layout_alignEnd="@id/contact_photo" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -26,6 +26,16 @@
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:scaleType="centerCrop" /> android:scaleType="centerCrop" />
<eu.siacs.conversations.ui.widget.PresenceIndicator
android:id="@+id/presence_indicator"
android:layout_gravity="bottom|end"
android:layout_width="@dimen/presence_indicator_size"
android:layout_height="@dimen/presence_indicator_size"
android:layout_marginEnd="@dimen/presence_indicator_offset"
android:layout_marginBottom="@dimen/presence_indicator_offset"
android:layout_alignBottom="@id/conversation_image"
android:layout_alignEnd="@id/conversation_image" />
<RelativeLayout <RelativeLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -12,5 +12,13 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black54" android:background="@color/black54"
android:scaleType="centerInside"/> android:scaleType="centerInside"/>
<eu.siacs.conversations.ui.widget.PresenceIndicator
android:id="@+id/presence_indicator"
android:layout_gravity="bottom|end"
android:layout_width="@dimen/presence_indicator_size"
android:layout_height="@dimen/presence_indicator_size"
android:layout_marginEnd="@dimen/presence_indicator_offset"
android:layout_marginBottom="@dimen/presence_indicator_offset" />
</eu.siacs.conversations.ui.widget.SquareFrameLayout> </eu.siacs.conversations.ui.widget.SquareFrameLayout>
</layout> </layout>

View file

@ -80,4 +80,6 @@
<dimen name="colorpicker_hue_width">30dp</dimen> <dimen name="colorpicker_hue_width">30dp</dimen>
<dimen name="avatar_corners_radius">8dp</dimen> <dimen name="avatar_corners_radius">8dp</dimen>
<dimen name="presence_indicator_size">12dp</dimen>
<dimen name="presence_indicator_offset">2dp</dimen>
</resources> </resources>