diff --git a/libpurple/purplepresence.h b/libpurple/purplepresence.h
--- a/libpurple/purplepresence.h
+++ b/libpurple/purplepresence.h
@@ -298,13 +298,24 @@
  * purple_presence_get_primitive:
  * @presence: The instance.
  *
- * Gets the [enum@Purple.StatusPrimitive] for @presence.
+ * Gets the [enum@Purple.PresencePrimitive] for @presence.
  *
  * Returns: The current primitive.
  *
  * Since: 3.0.0
  */
-PurpleStatusPrimitive purple_presence_get_primitive(PurplePresence *presence);
+PurplePresencePrimitive purple_presence_get_primitive(PurplePresence *presence);
+
+/**
+ * purple_presence_set_primitive:
+ * @presence: The instance.
+ * @primitive: The new primitive.
+ *
+ * Sets the [enum@Purple.StatusPrimitive] for @presence to @primitive.
+ *
+ * Since: 3.0.0
+ */
+void purple_presence_set_primitive(PurplePresence *presence, PurplePresencePrimitive primitive);
 
 /**
  * purple_presence_get_message:
@@ -318,6 +329,64 @@
  */
 const char *purple_presence_get_message(PurplePresence *presence);
 
+/**
+ * purple_presence_set_message:
+ * @presence: The instance.
+ * @message: (nullable): The new message.
+ *
+ * Sets the status message of @presence to @message.
+ *
+ * Since: 3.0.0
+ */
+void purple_presence_set_message(PurplePresence *presence, const char *message);
+
+/**
+ * purple_presence_get_emoji:
+ * @presence: The instance.
+ *
+ * Gets the current emoji, sometimes referred to as a mood, of @presence.
+ *
+ * Returns: The current emoji or %NULL if none is set.
+ *
+ * Since: 3.0.0
+ */
+const char *purple_presence_get_emoji(PurplePresence *presence);
+
+/**
+ * purple_presence_set_emoji:
+ * @presence: The instance.
+ * @emoji: (nullable): The new emoji to set.
+ *
+ * Sets the current emoji, sometimes referred to as a mood, of @presence to
+ * @emoji.
+ *
+ * Since: 3.0.0
+ */
+void purple_presence_set_emoji(PurplePresence *presence, const char *emoji);
+
+/**
+ * purple_presence_get_mobile:
+ * @presence: The instance.
+ *
+ * Gets whether or not @presence is on a mobile device.
+ *
+ * Returns: %TRUE if @presence is on a mobile device, otherwise %FALSE.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_presence_get_mobile(PurplePresence *presence);
+
+/**
+ * purple_presence_set_mobile:
+ * @presence: The instance.
+ * @mobile: The new mobile status.
+ *
+ * Sets whether or not @presence is on a mobile device.
+ *
+ * Since: 3.0.0
+ */
+void purple_presence_set_mobile(PurplePresence *presence, gboolean mobile);
+
 G_END_DECLS
 
 #endif /* PURPLE_PRESENCE_H */
# HG changeset patch
# Node ID e302e465b122
# Parent  5240fe0d7bdc
diff --git a/libpurple/purplepresence.c b/libpurple/purplepresence.c
--- a/libpurple/purplepresence.c
+++ b/libpurple/purplepresence.c
@@ -36,6 +36,11 @@
 	GHashTable *status_table;
 
 	PurpleStatus *active_status;
+
+	PurplePresencePrimitive primitive;
+	char *message;
+	char *emoji;
+	gboolean mobile;
 } PurplePresencePrivate;
 
 enum {
@@ -46,6 +51,8 @@
 	PROP_ACTIVE_STATUS,
 	PROP_PRIMITIVE,
 	PROP_MESSAGE,
+	PROP_EMOJI,
+	PROP_MOBILE,
 	N_PROPERTIES
 };
 static GParamSpec *properties[N_PROPERTIES];
@@ -98,6 +105,18 @@
 			purple_presence_set_active_status(presence,
 			                                  g_value_get_object(value));
 			break;
+		case PROP_PRIMITIVE:
+			purple_presence_set_primitive(presence, g_value_get_enum(value));
+			break;
+		case PROP_MESSAGE:
+			purple_presence_set_message(presence, g_value_get_string(value));
+			break;
+		case PROP_EMOJI:
+			purple_presence_set_emoji(presence, g_value_get_string(value));
+			break;
+		case PROP_MOBILE:
+			purple_presence_set_mobile(presence, g_value_get_boolean(value));
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
 			break;
@@ -129,6 +148,12 @@
 		case PROP_MESSAGE:
 			g_value_set_string(value, purple_presence_get_message(presence));
 			break;
+		case PROP_EMOJI:
+			g_value_set_string(value, purple_presence_get_emoji(presence));
+			break;
+		case PROP_MOBILE:
+			g_value_set_boolean(value, purple_presence_get_mobile(presence));
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
 			break;
@@ -157,6 +182,9 @@
 	g_clear_pointer(&priv->idle_time, g_date_time_unref);
 	g_clear_pointer(&priv->login_time, g_date_time_unref);
 
+	g_clear_pointer(&priv->message, g_free);
+	g_clear_pointer(&priv->emoji, g_free);
+
 	G_OBJECT_CLASS(purple_presence_parent_class)->finalize(obj);
 }
 
@@ -219,9 +247,9 @@
 	properties[PROP_PRIMITIVE] = g_param_spec_enum(
 		"primitive", "primitive",
 		"The primitive for the presence",
-		PURPLE_TYPE_STATUS_PRIMITIVE,
-		PURPLE_STATUS_UNSET,
-		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+		PURPLE_TYPE_PRESENCE_PRIMITIVE,
+		PURPLE_PRESENCE_PRIMITIVE_OFFLINE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurplePresence:message:
@@ -234,7 +262,33 @@
 		"message", "message",
 		"The status message",
 		NULL,
-		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurplePresence:emoji:
+	 *
+	 * The emoji or mood of the presence.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_EMOJI] = g_param_spec_string(
+		"emoji", "emoji",
+		"The emoji for the presence.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurplePresence:mobile:
+	 *
+	 * Whether or not the presence is on a mobile device.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_MOBILE] = g_param_spec_boolean(
+		"mobile", "mobile",
+		"Whether or not the presence is on a mobile device.",
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
 }
@@ -557,21 +611,34 @@
 	return 0;
 }
 
-PurpleStatusPrimitive
+PurplePresencePrimitive
 purple_presence_get_primitive(PurplePresence *presence) {
 	PurplePresencePrivate *priv = NULL;
-	PurpleStatusType *type = NULL;
 
-	g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), PURPLE_STATUS_UNSET);
+	g_return_val_if_fail(PURPLE_IS_PRESENCE(presence),
+	                     PURPLE_PRESENCE_PRIMITIVE_OFFLINE);
 
 	priv = purple_presence_get_instance_private(presence);
 
-	type = purple_status_get_status_type(priv->active_status);
-	if(type != NULL) {
-		return purple_status_type_get_primitive(type);
+	return priv->primitive;
+}
+
+void
+purple_presence_set_primitive(PurplePresence *presence,
+                              PurplePresencePrimitive primitive)
+{
+	PurplePresencePrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_PRESENCE(presence));
+
+	priv = purple_presence_get_instance_private(presence);
+
+	if(priv->primitive != primitive) {
+		priv->primitive = primitive;
+
+		g_object_notify_by_pspec(G_OBJECT(presence),
+		                         properties[PROP_PRIMITIVE]);
 	}
-
-	return PURPLE_STATUS_UNSET;
 }
 
 const char *
@@ -582,5 +649,78 @@
 
 	priv = purple_presence_get_instance_private(presence);
 
+	if(priv->message != NULL) {
+		return priv->message;
+	}
+
 	return purple_status_get_attr_string(priv->active_status, "message");
 }
+
+void
+purple_presence_set_message(PurplePresence *presence, const char *message) {
+	PurplePresencePrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_PRESENCE(presence));
+
+	priv = purple_presence_get_instance_private(presence);
+
+	if(!purple_strequal(priv->message, message)) {
+		g_free(priv->message);
+		priv->message = g_strdup(message);
+
+		g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_MESSAGE]);
+	}
+}
+
+const char *
+purple_presence_get_emoji(PurplePresence *presence) {
+	PurplePresencePrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
+
+	priv = purple_presence_get_instance_private(presence);
+
+	return priv->emoji;
+}
+
+void
+purple_presence_set_emoji(PurplePresence *presence, const char *emoji) {
+	PurplePresencePrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_PRESENCE(presence));
+
+	priv = purple_presence_get_instance_private(presence);
+
+	if(!purple_strequal(priv->emoji, emoji)) {
+		g_free(priv->emoji);
+		priv->emoji = g_strdup(emoji);
+
+		g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_EMOJI]);
+	}
+}
+
+gboolean
+purple_presence_get_mobile(PurplePresence *presence) {
+	PurplePresencePrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
+
+	priv = purple_presence_get_instance_private(presence);
+
+	return priv->mobile;
+}
+
+void
+purple_presence_set_mobile(PurplePresence *presence, gboolean mobile) {
+	PurplePresencePrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_PRESENCE(presence));
+
+	priv = purple_presence_get_instance_private(presence);
+
+	if(priv->mobile != mobile) {
+		priv->mobile = mobile;
+
+		g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_MOBILE]);
+	}
+}
diff --git a/libpurple/tests/test_presence.c b/libpurple/tests/test_presence.c
--- a/libpurple/tests/test_presence.c
+++ b/libpurple/tests/test_presence.c
@@ -24,65 +24,79 @@
  * Main
  *****************************************************************************/
 static void
-test_purple_presence_migrate_online_without_message(void) {
+test_purple_presence_new(void) {
 	PurplePresence *presence = NULL;
-	PurpleStatus *status = NULL;
-	PurpleStatusType *type = NULL;
 
-	type = purple_status_type_new(PURPLE_STATUS_AVAILABLE,
-	                              "online", "online", TRUE);
 	presence = purple_presence_new();
-	status = purple_status_new(type, presence);
-
-	purple_status_set_active(status, TRUE);
-
-	/* Now verify that the presence returns the correct values for the
-	 * properties.
-	 */
-	g_assert_cmpint(purple_presence_get_primitive(presence), ==,
-	                PURPLE_STATUS_AVAILABLE);
-	g_assert_null(purple_presence_get_message(presence));
+	g_assert_true(PURPLE_IS_PRESENCE(presence));
 
 	/* Cleanup. */
 	g_clear_object(&presence);
-	g_clear_object(&status);
-	g_clear_pointer(&type, purple_status_type_destroy);
 }
 
 static void
-test_purple_presence_migrate_online_with_message(void) {
+test_purple_presence_properties(void) {
 	PurplePresence *presence = NULL;
-	PurpleStatus *status = NULL;
-	PurpleStatusType *type = NULL;
-	GHashTable *attrs =  NULL;
+	PurplePresencePrimitive primitive = PURPLE_PRESENCE_PRIMITIVE_OFFLINE;
+	GDateTime *now = NULL;
+	GDateTime *login = NULL;
+	GDateTime *login1 = NULL;
+	GDateTime *idle = NULL;
+	GDateTime *idle1 = NULL;
+	char *message = NULL;
+	char *emoji = NULL;
+	gboolean mobile = FALSE;
 
-	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
-	                                         "online", "online",
-	                                         FALSE, TRUE, TRUE,
-	                                         "message", "message",
-	                                         purple_value_new(G_TYPE_STRING),
-	                                         NULL);
-	presence = purple_presence_new();
+	/* Create the login and idle times. */
+	now = g_date_time_new_now_utc();
+	login = g_date_time_add_hours(now, -1);
+	idle = g_date_time_add_minutes(now, -10);
+	g_clear_pointer(&now, g_date_time_unref);
 
-	status = purple_status_new(type, presence);
-
-	attrs = g_hash_table_new(g_str_hash, g_str_equal);
-	g_hash_table_insert(attrs, "message", "Greetings Programs!");
-	purple_status_set_active_with_attributes(status, TRUE, attrs);
-	g_hash_table_destroy(attrs);
+	/* Create the presence using g_object_new to make sure set_property is
+	 * wired up correctly.
+	 */
+	presence = g_object_new(
+		PURPLE_TYPE_PRESENCE,
+		"primitive", PURPLE_PRESENCE_PRIMITIVE_AVAILABLE,
+		"message", "I'll be back!",
+		"emoji", "🤖",
+		"login-time", login,
+		"idle-time", idle,
+		"mobile", TRUE,
+		NULL);
 
-	/* Now verify that the presence returns the correct values for the
-	 * properties.
+	/* Grab the values via g_object_get to make sure get_property is wired up
+	 * correctly.
 	 */
-	g_assert_cmpint(purple_presence_get_primitive(presence), ==,
-	                PURPLE_STATUS_AVAILABLE);
-	g_assert_cmpstr(purple_presence_get_message(presence), ==,
-	                "Greetings Programs!");
+	g_object_get(
+		presence,
+		"primitive", &primitive,
+		"message", &message,
+		"emoji", &emoji,
+		"login-time", &login1,
+		"idle-time", &idle1,
+		"mobile", &mobile,
+		NULL);
+
+	/* Validate! */
+	g_assert_cmpint(primitive, ==, PURPLE_PRESENCE_PRIMITIVE_AVAILABLE);
+	g_assert_cmpstr(message, ==, "I'll be back!");
+	g_assert_cmpstr(emoji, ==, "🤖");
+	g_assert_nonnull(login1);
+	g_assert_true(g_date_time_equal(login, login1));
+	g_assert_nonnull(idle1);
+	g_assert_true(g_date_time_equal(idle, idle1));
+	g_assert_true(mobile);
 
 	/* Cleanup. */
+	g_clear_pointer(&message, g_free);
+	g_clear_pointer(&emoji, g_free);
+	g_clear_pointer(&login, g_date_time_unref);
+	g_clear_pointer(&login1, g_date_time_unref);
+	g_clear_pointer(&idle, g_date_time_unref);
+	g_clear_pointer(&idle1, g_date_time_unref);
 	g_clear_object(&presence);
-	g_clear_object(&status);
-	g_clear_pointer(&type, purple_status_type_destroy);
 }
 
 /******************************************************************************
@@ -92,14 +106,8 @@
 main(gint argc, gchar *argv[]) {
 	g_test_init(&argc, &argv, NULL);
 
-	/* This tests verify that PurplePresence is properly notifying of property
-	 * changes with the 2.x.y and earlier status back end. When PurplePresence
-	 * has replaced that back end, these tests should be removed as well.
-	 */
-	g_test_add_func("/presence/migrate/online-without-message",
-	                test_purple_presence_migrate_online_without_message);
-	g_test_add_func("/presence/migrate/online-with-message",
-	                test_purple_presence_migrate_online_with_message);
+	g_test_add_func("/presence/new", test_purple_presence_new);
+	g_test_add_func("/presence/properties", test_purple_presence_properties);
 
 	return g_test_run();
 }
