Enhance Sailfish.Share API to include metadata hints for the receiving application

Description

I propose an addition to the Sailfish.Share protocol (not necessarily implementation) to allow the sending application to include hint data for the receiving application.

Something like this is already implemented in the context of the Webview Share context, in the form of the linkText and sister properties, but is specialized for sharing URLs.

My proposed feature generalizes the approach, and does not need changes to Sailfish.Share (but does require changes to applications receiving ShareAction data).

In fact, this proposes more of a convention than a feature.

Background

While working on Bugger! i came across the problem of wanting to send an email, and include both a Subject and Recipient, and attachments,

It turns out while Sailfish.Share will allow for text to be placed in the email body (in addition to adding files as attachments), I have found no way to add other basic email data such as recipient.

(There is also the possibility to use Qt.openURLExternally(mailto:...), which allows to specify recipients and subjects, but no attachments can be added).

Such came the need to have Sailfish.Share transport “hint” or “metadata” in addition to the pure file data. Not only for the email case, but in general.

RFC: Specification

ShareAction senders

As one of the elements of the ShareAction::resources array, include objects of the format:

{ "type": "text/x-sailfish-share-hints", "appHint": dataString }

with dataString being a JSON string of the format:

 { "type" : "", "data": {} }

where type is a string of the intended target application type, such as “email”, “browser”, “messenger”, or the special string “generic”. This is intended to be used by applications handling the hints to ignore those that don’t fit their purpose.

If the type is “email”, data must be of the format:

{
    "to":  "",
    "cc":  "",
    "bcc": "",
    "subject": ""
}

(perhaps body should also be part of this set, but the current implementation of jolla-email already has other ways of adding body data, i.e. using a resource of type “text/plain”.).
However, IF body was added to this format, and the additional properties of Content-Type and Content-Encoding this could allow for completely formatted emails with arbitrary Multipart/MIME structure to be passed. But this would need extensive changes to jolla-email.

If the type is “generic”, data must be of the format:

{
    "context":     ""
    "recipient":   "",
    "summary":     "",
    "description": ""
}

Where context might be used to refer to any of the other elements of the Sailfish.Share::resources. This could be used to e.g. give textual descriptions to images shared. However, how the contents of context and any element of resources might be matched up is undefined. This might be e.g. a file name, checksum, or some internal metadata of the file.
recipient is a “logical receiver” of the shared content. This might be and URL string (e.g. if the files should be uploaded at some web location), a name (such as an email address), or something like the UID of an instant messaging contact).
summary and description are single-line and multi-line (respectively) text.

ShareAction receivers

As an example, see the handling of shared data in /usr/share/jolla-email/email.qml (DBus interface to receive shared data), and /usr/share/jolla-email/pages/ShareComposerPage.qml (handling the data).

Applications should iterate over the received resources and

  1. look for entries having a type of text/x-sailfish-share-hints
  2. if present, load the contents of the appHint property, and parse its JSON string.
  3. read the appHint.type, for types the application knows how to handle
  4. use the contents of the appHint.data object as appropriate

PoC: Implementation for jolla-email

Consider the following:

    ShareAction { id: sharer
         mimeType: "text/x-url" // reduces the share-with application list to "email" and a couple of others
    }
    ListModel { id: filesModel
        ListElement { name: "Test File 1"; filePath: "path/to/a/test/file" }
        ListElement { name: "Test File 2"; filePath: "path/to/another/file" }
    }

    function emailshare()   {
        const body = 'There should be ' + filesModel.count + ' files attached.\n\n'
        const appHint = {
            "type" : "email",
            "data": {
                 "to":      "max@example.org"
                 "cc":      "",
                 "bcc":     "",
                 "subject": "Hello Sailor!"
            }
        }
        var res = [
            { "type": "text/x-url", "linkTitle": body }, // this is one of the current ways to add body text to an email
            { "type": "text/x-sailfish-share-hints", "appHint": JSON.stringify(appHint) }
        ];
        for (var i = 0; i < filesModel.count; ++i) {
            const f = filesModel.get(i)
            res.push( f.filePath )
        }
        sharer.resources = res;
        sharer.trigger();
    }

For this to work, you need this small patch against jolla-email:

diff -Naur work/4.4.0.72/usr/share/jolla-email/pages/ShareComposerPage.qml work/4.4.0.72_mine/usr/share/jolla-email/pages/ShareComposerPage.qml
--- /usr/share/jolla-email/pages/ShareComposerPage.qml  2022-12-10 19:03:55.822739406 +0100
+++ /usr/share/jolla-email/pages/ShareComposerPage.qml  2022-12-10 21:54:57.200451067 +0100
@@ -54,7 +54,7 @@
             for (var i = 0; i < resources.length; ++i) {
                 if (typeof resources[i] === "string") {
                     _fileResources.push(resources[i])
-                } else if (resources[i].type === "text/plain" || resources[i].type === "text/x-url") {
+                } else if (resources[i].type === "text/plain" || resources[i].type === "text/x-url" || resources[i].type === "text/x-sailfish-share-hints") {
                     // Show the contents inline within the email.
                     _contentResources.push(resources[i])
                 } else {
@@ -95,11 +95,27 @@

             Component.onCompleted: {
                 var content = modelData
-                if (content.type !== "text/plain" && content.type !== "text/x-url") {
+                if (content.type !== "text/plain" && content.type !== "text/x-url" && content.type !== "text/x-sailfish-share-hints") {
                     // Other file types should have been converted into temporary files by ShareAction.
                     console.warn("Unexpected inline email content type:", content.type)
                     return
                 }
+                if (content.type === "text/x-sailfish-share-hints") {
+                    try {
+                        // TODO: can we use an Object instead?
+                        var appHint = JSON.parse(content.appHint)
+                        if (appHint && appHint.hasOwnProperty("type") && appHint["type"] === "email") {
+                            var hintData      = appHint["data"]
+                            root.emailTo      = hintData.to
+                            root.emailCc      = hintData.cc
+                            root.emailBcc     = hintData.bcc
+                            root.emailSubject = hintData.subject
+                        }
+                    } catch (exception) {
+                        console.warn("Cound not parse application hints.")
+                        return
+                    }
+                }
                 if (content.type === "text/x-url" && content.linkTitle) {
                     root.emailBody += (content.linkTitle + "\n\n")
                 }

I would have made pull requests against both the Share docs as well as Mail app instead of posting this here - alas there is no open source repository to to this.

5 Likes

I realize that the better design would probably be to add a ShareAction::applicationHints property with similar semantics, separately from resources.

But

  1. The WebView linksharing already uses the resources approach
  2. the resources approach requires no changes to the Sailfish.Share as such (but may have side effects for existing apps, which might handle the hints data as a file).

On the other hand, adding a new property would improve backwards compatability as receiving applications could not misinterpret apphint data as shared files.

1 Like

Tagging @vige if you would like to comment?

I can see that something like this could be useful. Perhaps @pvuorela has more insight?

3 Likes