Developing Android Apps for the HD4000 | Part 3 - JSON Image Model

Introduction

In the previous two parts of this series, we’ve covered the basics: connecting to & configuring the HUD, as well as some of the intermediate usages: displaying text & images and leveraging android layouts to create more complex HUD views.

In the final section of this series, we’re going to be covering the JSON Image Model or JIM, for short.

What is a JSON Image Model

So, before we get into more detail, we need to cover off what a JSON Image Model is in the context of the HUD. At a high level, a JIM is a representation of a HUD screen, written in JSON format. Essentially, we can create JSON files that represent the contents, layout & design of a HUD screen, without having to code anything ourselves like we did when we leveraged the android layout functionality. As with all JSON files, there is a published schema that we need to follow, this is discussed in the next section.

JSON Image Model Schema

The schema essentially consists of two parts:

The first is a collection of four JSON objects that define the “top-level” settings for the view on the HUD: A version number (currently non-functional but required one the less), Whether to display a frame around the view, and if so what colour, and whether to apply any scaling to the view.

The second part is the Widgets. In the JSON file, we need to include an array of Widgets as these are what is shown on the display. Widgets can either be text or images and each widget has several objects that define their content e.g. whether it is a text or image widget, what text to display if it’s a text widget & what image to display if it’s an image widget and so on…

The following is the complete JSON schema for the JIM – I’ve added comments where I think some clarification is required so you should be able to get a solid understanding of what you can and can’t do using the JIM.

{
  "title": "JSON schema for the ZebraHud Json Image Model configuration data",
  "$schema": "http://json-schema.org/draft-07/schema#",

  "type": "object",
  "additionalProperties": false,

  "properties": {
    "ver": {
      "enum": ["1.0"],
      "description": "Version of this schema"
    },
    "showFrame": {
      "type": "boolean",
      "description": "Shows frame around image if == true"
    },
    "frameColor": {
      "type": "string",
      "description": "Frame color: string is parsed using Android [Color.parseColor]"
    },
    "screenResize": {
      "type": "number",
      "description": "Resize HUD screen image (in %): 95, 90, 85, 80, 75"
    },
    "widgets": {
      "type": "array",
      "description": "A list of overlayed images and text, to draw on the HUD screen.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "description": "For a specific widget.",
        "properties": {
          "visible": {
            "type": "boolean",
            "description": "Show/hide this widget, similar to View.VISIBLE and View.GONE"
          },
          "sort": {
            "type": "number",
            "description": "Z-position, higher-valued widgets are overlayed on top of lower-valued widgets"
          },
          "type": {
            "enum": ["TEXT", "IMAGE"],
            "description": "Widget type (defaults to TEXT). IMAGE type data (default HUD screen is 640x400) uses URL or Base64 encoding"
          },
          "data": {
            "type": "string",
            "description": "type=TEXT: string to display. type=IMAGE: path to file (such as box2.png) or Base64 encoded image data"
          },
          "position": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "description": "Widget position [x, y, width, height] from 0% to 100%. For example: [5, 5, 90, 90] provides a 5% border from fullscreen.",
            "items": {
              "type": "integer"
            }
          },
          "positionPoint": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "description": "Alternate widget position [center-x, center-y, width, height] from 0% to 100%. For example: [50, 50, 10, 10] creates a 10% width/height box, with its center-point in the middle of the screen.",
            "items": {
              "type": "integer"
            }
          },
          "lines": {
            "type": "integer",
            "description": "type=TEXT: lines to use for the text"
          },
          "color": {
            "type": "string",
            "description": "type=TEXT: text color. type=IMAGE: color applied using Android [PorterDuff.Mode.MULTIPLY]. String is parsed using Android [Color.parseColor]"
          },
          "colorBack": {
            "type": "string",
            "description": "Background color: string is parsed using Android [Color.parseColor]"
          },
          "bold": {
            "type": "boolean",
            "description": "type=TEXT: bolded text"
          },
          "imageScale": {
            "enum": ["FIT_CENTER", "FIT_XY", "FIT_START", "FIT_END", "MATRIX", "CENTER", "CENTER_CROP", "CENTER_INSIDE"],
            "description": "type=IMAGE: matches Android [ImageView.ScaleType] enumeration"
          },
          "gravity": {
            "enum": ["LEFT", "RIGHT", "CENTER"],
            "description": "type=TEXT: gravity applied to the text, using Android [View.Gravity]"
          }
        }
      }
    }
  },
  "required": ["widgets"]
}

JSON Image Model Examples

Now that we have a basic understanding of what a JSON Image Model is and what the schema includes, I think it’ll make more sense to run through some example JIM’s, from simple to more complex.

The first example, and the simplest, is using a JIM to display some text on the HUD screen:

{
	"ver": "1.0",
	"showFrame": false,
	"widgets": [
		{
			"sort": 70,
			"note": "instruction string",
			"position": [00,50, 100, 10],
			"data": "Remove hub cap label 3",
			"colorBack": "#A0111111",
			"bold" true
		}
	]
}
text jim preview.png
text jim preview.png, by JS6845

Next, we have an example of using a JIM to display an image:

{
	"ver": "1.0",
	"showFrame": false,
	"widgets": [
		{
			"sort": 10,
			"note": "slide1 background",
			"position": [00, 00, 100, 100],
			"type": "IMAGE",
			"imageScale": "FIT_XY",
			"data": "images/engine.png",
		}
	]
}
image jim example.jpg
image jim example.jpg, by JS6845

That’s the basics covered, but what if we wanted to display an Image and overlay some text? Here’s how we would do that:

{
	"ver": "1.0",
	"showFrame": false,
	"widgets": [
		{
			"sort": 70,
			"note": "instruction string",
			"position": [00,50, 100, 10],
			"data": "Remove hub cap label 3",
			"colorBack": "#A0111111",
			"bold" true
		},	{
			"sort": 10,
			"note": "slide1 background",
			"position": [00, 00, 100, 100],
			"type": "IMAGE",
			"imageScale": "FIT_XY",
			"data": "images/engine.png",
		}
	]
}
images and text jim preview.png
images and text jim preview.png, by JS6845

Lastly, lets take it a step further and try and create a complete view. We will use an image as the background, overlay some text, and at the top of the view place another image to highlight a part of the screen:


{
	"ver": "1.0",
	"showFrame": false,
	"widgets": [
		{
			"sort": 10,
			"note": "slide1 background",
			"position": [00, 00, 100, 100],
			"type": "IMAGE",
			"imageScale": "FIT_XY",
			"data": "images/engine.png",
		},	{
			"sort": 20,
			"note": "slide1 background",
			"position": [37, 44, 25, 25],
			"type": "IMAGE",
			"imageScale": "FIT_XY",
			"data": "images/arrow.png",
		},	{
			"sort": 70,
			"note": "instruction string",
			"position": [00,50, 100, 10],
			"data": "Remove hub cap label 3",
			"colorBack": "#A0111111",
			"bold" true
		}
	]
}
image image text jim example.jpg
image image text jim example.jpg, by JS6845

Applying the JSON Image Schema

Now that we know how to create JIMs, we need to know how to send this information to the HUD to be displayed. This is very easy, all we need to do is call the showJim(String jsonImageModel) method on an instance of our ZebraHud.

If we’re using a very simple JIM and only want to display text, we would do that by calling showJim() method, and pass through our JIM as a string as the only parameter:

// Verify Connection
if (mZebraHud.isConnected()) {
    // Get JSON Image Model As String
    String jsonImageModel = "{ }";

    // Pass JIM to Hud
    mZebraHud.showJim(jsonImageModel);
}

If we want to include images, we do the same as above, but we need to pass through the absolute directory path for all relative-path image file names. As you may have noticed in our JIM examples above, when we referred to an image, we simply pass through their relative path, e.g.: images/engine.png but obviously this isn’t enough for Android to determine where our images are stored. To give Android the information it needs, we need to base through the base absolute directory path. So, for example, if our images are stored in: /sdcard/data/android/com.myexample.app/files/images/ and we’ve using images/example.png as our relative path, we will need to call the showJim() method passing through the /sdcard/data/android/com.myexample.app/files/ as the second variable, as this is the directory path for our images. I’ve put a complete example below:

// Verify Connection
if (mZebraHud.isConnected()) {
    // Get JSON Image Model As String
    String jsonImageModel = "{ }";

    // Get Absolute Path for Images Directory
    String imageBaseDirectory = "/sdcard/android/data/com.example.app/files/";

    // Pass JIM to Hud
    mZebraHud.showJim(jsonImageModel, imageBaseDirectory, null, null);
}

Leveraging a Base JSON Image Schema

The observant reader will have noticed in the last example there were two extra parameters that we passed through null too. What are these you ask? These are for when we want to show multiple views that display some of the same content. The two parameters are for a background image & base JIM that will be applied to all of our JIMs. To leverage this functionality, we just pass through the relative path of the background image for our slides, as well as a base JIM to apply to all of our slides. Parameters that aren't present in our JIMs will be extracted from the base JIM and applied consistently across all of our screens:

// Verify Connection
if (mZebraHud.isConnected()) {
    // Get JSON Image Model As String
    String jsonImageModel = "{ }";

    // Get Absolute Path for Images Directory
    String imageBaseDirectory = "/sdcard/android/data/com.example.app/files/";

    // Get Background Image
    String backgroundImagePath = "images/background.jpg";

    // Get Base Jim
    String baseJim = "{ }";

    // Pass JIM to Hud
    mZebraHud.showJim(jsonImageModel, imageBaseDirectory, backgroundImagePath, baseJim);
}

Conclusion

And that brings us to the end of our Developing Android Apps for the HD4000 series. In the series, we've gone from covering the basics to leveraging some intermediate APIs and finally covering  JIMs. You should now be in a position to go ahead and start creating your own HUD screens. Until next time!