Last updated Dec 8, 2017

Making your macro render in Confluence mobile

Applicable:

This tutorial applies to Confluence 4.3.2 and later.

Level of experience:

This is an advanced tutorial. You should already be familiar with Confluence macro development and the Atlassian Web Resource framework.

Time estimate:

It should take you approximately 2 hours to complete this tutorial.

By default, Confluence Mobile does not render macros. Instead it displays a placeholder which, when tapped, loads the desktop version of the page.

With a little additional effort, you can modify an existing macro for display in the Confluence Mobile interface.

For simple macros, the macro needs to simply specify a device type of "mobile" (see below). However depending on the complexity of the macro UI, you may need to convert its CSS and JavaScript for Confluence Mobile.

Making the macro available to Confluence Mobile

We'll start with the simple case. To allow your macro to be rendered in mobile, open the atlassian-plugin.xml plugin descriptor and add the <device-type> tag with the value mobile. For example:

Macro Descriptor

1
2
<xhtml-macro key="expand"
             name="expand"
             class="fully.qualified.macro.implementation"
             icon="macro_icon.png"
             documentation-url="documentation_url">
    <device-type>mobile</device-type>
    etc...
</xhtml-macro>

You don't add a specific device type entry for the desktop version of Confluence. All macros are implicitly compatible with the desktop interface.

At this point you are ready to test your macro and depending on the needs of its UI you could well be finished now.

Macros with JavaScript and/or CSS

Detecting mobile device type during macro execution

There are a few big differences between the Confluence Mobile UI and the desktop UI. So much so that you may want to consider serving a completely different set of resources depending on the device type the macro is rendering to. This is done in your macro's Java implementation code as follows:

1
2
public class MyMacro implements Macro
{
    private final WebResourceManager webResourceManager;

    public MyMacro(WebResourceManager webResourceManager)
    {
        this.webResourceManager = webResourceManager;
    }

    @Override
    public String execute(Map<String, String> parameters, String body, ConversionContext context)
        throws MacroExecutionException
    {
        String template = null;
        if ("mobile".equals(context.getOutputDeviceType()))
        {
            webResourceManager.requireResourcesForContext("my.macro.mobile.resource.context");
            template = "my-macro-mobile-template.vm";
        }
        else
        {
            webResourceManager.requireResourcesForContext("my.macro.desktop.resource.context");
            template = "my-macro-desktop-template.vm";
        }

        // etc.

This example shows the selection of different web resources and a different template for Confluence Mobile. The web resources are selected by context which is recommended if you have multiple web resources in your macro.

The alternative is to request individual resources using webResourceManager.requireResource(moduleCompleteKey) however this would lead to one or more HTTP requests per resource. If you request by context then the WebResourceManager will batch your requirements into as few requests as possible which is very desirable in a mobile use case.

Detecting Mobile Device Type without coupling your code base to Confluence 4.3.2 or later

In the above example you should pay special notice the call context.getOutputDeviceType(). This method was introduced on the ConversionContext in Confluence 4.3.2. If you are working on an existing macro you will probably prefer not to require a new branch for the mobile compatible macro.

In this case, to detect the mobile device type you would use:

1
2
if ("mobile".equals(context.getPropertyAsString("output-device-type"))

Front end considerations for mobile macros

The previous macro example shows the most involved case where the macro's existing JavaScript and CSS are incompatible with Confluence Mobile and so completely different resources are required per device type. However, in many cases with a little tweaking of existing resources you will be able to use the same JavaScript and/or CSS for both mobile and desktop device types.

The following are some points to consider in creating mobile compatible resources.

Single Page Application

Confluence Mobile is a single page application. Tapping a link does not result in the browser loading the new URL. The new content is instead fetched and displayed within the already loaded application frame. In addition, should your macro resources be required on a page and they have already been fetched previously, then they will not be reloaded.

The most obvious implication here is that you cannot bind functionality to the DOM ready event. Typically this is done in the desktop application using AJS.toInit(function($)) or jQuery(function($) {}) or $(function($) {}).

In Confluence Mobile you instead bind to the "displayed" event on the contentEventAggregator as shown:

1
2
 ConfluenceMobile.contentEventAggregator.on("displayed", function() {});

CSS Differences

The base CSS on mobile is different (and simpler) than that of the desktop application. The HTML structure containing content is also different. On mobile the containing HTML looks like:

1
2
<div class="content-container wiki-content">
</div>

"Retina" Resolution Icons

The latest iPhone and iPad devices as well as some newer Android devices have a high pixel density display. If your macro displays any images you should provide these images in both original and double-sized resolutions.

Separate any CSS references to images into two files and configure the web resources as follows:

atlassian-plugin.xml Snippet

1
2
<web-resource key="css-resource-key" name="Macro CSS Resources">
    <resource type="download" name="icons.css" location="css/icons.css">
        <param name="media" value="(-webkit-min-device-pixel-ratio: 1), (min-device-pixel-ratio: 1)"/>
    </resource>
    <resource type="download" name="icons-2x.css" location="css/icons-2x.css">
        <param name="media" value="(-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5)"/>
    </resource>
</web-resource>

Remember to constraint the pixel size of the images to ensure they are displayed at the correct size. For example:

Base CSS

1
2
.control-icon {
    background-size: 16px; display: inline-block; width: 20px;
}

Icon Specific CSS

1
2
.control-icon {
    background-image: url('blah/blah/icons/myicon.png');
}

Retina Icon Specific CSS

1
2
.control-icon {
    background-image: url('blah/blah/icons/myicon-2x.png');
}

Detecting Mobile in JavaScript

If you want specific behaviour for your JavaScript when running in the mobile application you can detect the mobile context as follows:

1
2
if (window.ConfluenceMobile) {
    // do mobile specific stuff
}

Confluence Mobile Macro Example

The bundled expand macro is a good example of a macro that supports both desktop and mobile interfaces. This macro takes the approach of providing a common JavaScript that provides core functionality for all device types and then providing a different "initialisation" Javascript for desktop and mobile.

atlassian-plugin.xml snippet

1
2
<web-resource key="expand-macro-css-only" name="Core Expand Macro CSS Resource">
    <transformation extension="css">
        <transformer key="cssSubstitution"/>
    </transformation>
    <resource type="download" name="expand-macro.css" location="com/atlassian/confluence/plugins/expand/css/expand-macro.css" />
    <resource type="download" name="expand-icons.css" location="com/atlassian/confluence/plugins/expand/css/expand-icons.css">
        <param name="media" value="(-webkit-min-device-pixel-ratio: 1), (min-device-pixel-ratio: 1)"/>
    </resource>
    <resource type="download" name="expand-icons-2x.css" location="com/atlassian/confluence/plugins/expand/css/expand-icons-2x.css">
        <param name="media" value="(-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5)"/>
    </resource>
</web-resource>

<web-resource key="expand-macro-core" name="Core Expand Macro Resources">
    <resource type="download" name="expand-macro-core.js" location="com/atlassian/confluence/plugins/expand/js/expand-macro-core.js"/>
    <dependency>${atlassian.plugin.key}:expand-macro-css-only</dependency>
</web-resource>

<web-resource key="expand-macro-desktop-resources" name="Expand Macro Resources for Desktop">
    <resource type="download" name="expand-macro.js" location="com/atlassian/confluence/plugins/expand/js/expand-macro.js"/>
    <dependency>${atlassian.plugin.key}:expand-macro-core</dependency>
    <context>atl.confluence.macros.expand.desktop</context>
</web-resource>

<web-resource key="expand-macro-mobile-resources" name="Expand Macro Resources for Mobile">
    <resource type="download" name="expand-macro-mobile.js" location="com/atlassian/confluence/plugins/expand/js/expand-macro-mobile.js"/>
    <dependency>${atlassian.plugin.key}:expand-macro-core</dependency>
    <context>atl.confluence.macros.expand.mobile</context>
</web-resource>

The mobile specific initialisation JavaScript -

expand-macro-mobile.js

1
2
ConfluenceMobile.contentEventAggregator.on("displayed", function() {
    Confluence.Plugins.ExpandMacro.bind(Zepto, $(".container"), "touchstart");
});

The desktop specific initialisation JavaScript:

expand-macro.js

1
2
AJS.toInit(function ($) {
    Confluence.Plugins.ExpandMacro.bind($, $("#content"), "click keyup", function(e) {
        return !(e.type == "keyup" && e.keyCode != 13);
    });
});

User Macros

User macros can be created via the Administrator UI or via a plugin. Unlike typical macros, both types of user macro are automatically rendered in Confluence Mobile.

Rationale

A user macro is completely accessible to the site admins so if it was found that a particular macro had problems on mobile it can be fixed. The same goes for plugin supplied user macros - their implementation is completely accessible to the admin within the plugin.

Rate this page: