Tag Archives: Programming

Using Android WebView to display a webpage on top of the Unity App view

Hello and welcome to my tutorial on how to show a WebView on top of your Android Unity App, while still allowing the user to interact with your Unity UI.

You can watch the video of this tutorial at https://youtu.be/r1hLo5C50wE.

This tutorial assumes a reasonable knowledge of Unity, C#, Android Studio and Java. The source code for this tutorial can be found at https://github.com/cwgtech/androidwebview.

The plan is to extend the plugin created in my previous tutorials by adding a method that will create an android layout containing an Android WebView object and a blank TextView. We’ll adjust the height of the TextView to create space at the top of the layout that will allow the user to still see and interact with a portion of the Unity viewspace. We’ll add this layout to our App’s content view which will place it on top of the Unity view.

We’ll also add a method to remove this layout from the content view, returning the full screen to Unity.

Get started by loading up the previous version of this project in Unity and the MyPlugin project in Android Studio. If you don’t have it, you can download it from https://github.com/cwgtech/AndroidActivityResult.

Using Android Studio, open the MyPlugin java source and add the following variable declarations above the first method definition:

private LinearLayout webLayout;
private TextView webTextView;
private WebView webView;

We’re going to use these vars to store references to the objects we create when the webview is displayed. This will allow the plugin to close and deallocate those objects when the webview is closed.

Add the following method to the body of the plugin:

public void showWebView(final String webURL, final int pixelSpace)
{
    mainActivity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        Log.i(LOGTAG,"Want to open webview for " + webURL);
        if (webTextView==null)
            webTextView = new TextView(mainActivity);
        webTextView.setText("");
        if (webLayout==null)
            webLayout = new LinearLayout(mainActivity);
        webLayout.setOrientation(LinearLayout.VERTICAL);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        mainActivity.addContentView(webLayout,layoutParams);
        if (webView==null)
            webView = new WebView(mainActivity);
        webView.setWebViewClient(new WebViewClient());
        layoutParams.weight = 1.0f;
        webView.setLayoutParams(layoutParams);
        webView.loadUrl(webURL);
        webLayout.addView(webTextView);
        webLayout.addView(webView);
        if (pixelSpace>0)
            webTextView.setHeight(pixelSpace);
        }
    });
}

The method showWebView takes two parameters, the URL of the webpage you want to display, and the number of screen pixels the layout needs to reserve for the Unity UI. This version assumes that the Unity UI is at the top of the screen and pushes the WebView down, you’ll need to modify the order the views are added to the layout if this is not what you want.

First, we create the TextView and set its contents to an empty string.

Next we create the LinearLayout and set its orientation to vertical, and its layout so that it will fill its parent object, and then add it to the ContentView for our activity.

Lastly, we create the actual WebView and we assign the WebViewClient to be a default WebViewClient. This tells our WebView how to handle links, and with the default client, the links will be opened in our WebView. Without this, when the user clicks on a link, Android will pop-up a chooser asking the user what app they want to send the link to.

We also set the weight of the WebView to 1, which means the layout system will give our WebView as much space as it can. Finally, we tell the WebView to load the URL passed to this method.

The TextView and WebView are added to the LinearLayout object, with the order they are added determining the order they will appear on screen, and the height of the TextView is set to the number of screen pixels we want to push the WebView down by.

We need to add one more method that will allow our Unity app to remove the Layout when it’s no longer needed. Add the following code:

public void closeWebView(final ShareImageCallback callback)
{
    mainActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (webLayout!=null)
            {
                webLayout.removeAllViews();
                webLayout.setVisibility(View.GONE);
                webLayout = null;
                webView = null;
                webTextView = null;
                callback.onShareComplete(1);
            }
            else
                callback.onShareComplete(0);
        }
    });
}

This method is going to reuse the ShareImage callback interface. We could create a new interface just for this method, but there is no harm in using an existing interface that can do the same job, which is to let Unity know when we’ve closed the layout.

To remove the layout, first remove all it’s child views, then set it’s visibility state to GONE. This will cause it to remove itself from its parent and mark it for garbage collection. Setting the vars that hold the references to our views to NULL will also allow the garbage collection system to free the memory used by them.

Lastly, trigger the supplied callback passing a 1 if the close happened as expected, or a 0 if the LinearLayout had already been closed.

That completes the modifications to the plugin, so you can go ahead and let Gradle build it and copy the updated AAR to the Plugin folder in the Unity project.

Switch back to Unity where we’ll modify the canvas object in the hierarchy view to include a new layer for our WebView, but first double click the script PluginTest to open it in Visual Studio.
Add the following two lines to the C# code, after the other public UI vars:

public RectTransform webPanel;
public RectTransform buttonStrip;

These will hold references to the UI objects we’ll create later. The webPanel is the root UI object that will contain all the objects that will be displayed when the WebView is on screen, and buttonStrip holds the title text, and the close button.

Now add the following methods that will call our Java methods, but only if we’re on an Android platform:

public void OpenWebView(string url, int pixelShift)
{
    if (Application.platform == RuntimePlatform.Android)
    {
        PluginInstance.Call("showWebView", new object[] { url, pixelShift });
    }
}

public void CloseWebView(System.Action<int> closeComplete)
{
    if (Application.platform == RuntimePlatform.Android)
    {
        PluginInstance.Call("closeWebView", new object[] { new ShareImageCallback(closeComplete) });
    }
    else
        closeComplete(0);
}

These methods are just wrappers for the Java code and pass the parameters directly to the plugin.
Next, add the method we’ll connect to a UI button that will figure out how much space to reserve at the top of the display and then pass that with the URL to our Java wrapper.

public void OpenWebViewTapped()
{
    Canvas parentCanvas = buttonStrip.GetComponentInParent<canvas>();
    int stripHeight = (int)(buttonStrip.rect.height * parentCanvas.scaleFactor + 0.5f);
    webPanel.gameObject.SetActive(true);
    OpenWebView("http://www.cwgtech.com", stripHeight);
}

We get a reference to the Canvas object that our buttonStrip belongs to, and then use it’s scaling factor along with the height of our ButtonStrip to calculate how many screen pixels we need to push the webview down by. Enable the WebPanel and pass the URL and height to our Java wrapper.

Add the following method:

public void CloseWebViewTapped()
{
    CloseWebView((int result) =>
    {
        webPanel.gameObject.SetActive(false);
    });
}

This method will be connected to the close button child of the buttonStrip. It simply calls our Java wrapper, using the inline function to hide the WebPanel object once the Android views have been cleaned up and removed.

Save the file and return to Unity. Wait a few seconds to let Unity recompile the C# code and then expand the Canvas object.

Right click on the Canvas object, and select UI, then Button and left click. This will create a button in the middle of the screen called Button (1). Rename it to browseButton and expand it. Change the default text on the child Text object to ‘Browse’.

Highlight the browseButton again click the + button on the On Click list of the button script. Now drag the Main Camera object into the reference holder. Click the function selector, click PluginTest and then OpenWebViewTapped.

Right click on the Canvas object, then click UI, then Panel. Rename the panel created to WebPanel. Click on the color gizmo and set the color to pink (#FFD4F7) and alpha to 255.

Right click on WebPanel and then click UI, Image. Click on the Rect Transform gizmo, and select top center, horizontal stretch while hold shift and alt (option on mac). This will move the image to the top of the screen and make it fill the view horizontally. Set the height to 50 and the color to black. Rename the Image to ButtonStrip.

Right click on ButtonStrip and then UI, Text. Click the Rect Transform gizmo and set stretch for vertical and horizontal, while holding shift & alt (option on mac). This will cause the Text object to fill the image area. Set the font size to 28, the color to White, and the text to ‘Web Page View’.

Click the checkboxes to center the text both horizontally and vertically.

Right click on the ButtonStrip again and click UI,Button. Click the Rect Transform gizmo and then top right anchor with shift & alt held (option on mac). Set the width and height of the button to 24. Expand the button object and set the default text of the Text child to ‘X’. Rename the button to ‘CloseButton’.

Just like we did for the BrowseButton, connect the OnClick event for the CloseButton to the CloseWebViewTapped method of PluginTest.

Highlight the Main Camera, and in the PluginTest script object, drag the WebPanel object to the Web Panel holder and the ButtonStrip object to the Button Strip holder. Highlight the WebPanel object in the hierarchy and disable it.

Save and run the scene. If you click the Browse button, you’ll see the WebPanel appear and the close button will hide it. There will be no actual webview as we’re not yet running on an Android platform. Stop execution of the player.

Click ‘File’ and then ‘Build Settings’. Click on ‘Player Settings’ and then ‘Other Settings’. Scroll down to the configuration area and change ‘Internet Access’ from ‘Auto’ to ‘Require’.

Now click ‘Build and Run’ to build the Android version and run it on your connected device. In my case, I’m running it on an emulator I started earlier. We need to tell Unity to include the Internet permission as Unity is unaware that our plugin is making calls to fetch content from the web and won’t add it by itself.

With the app running, tapping on the browse button will make the WebPanel appear, which is why we made it pink, and if you tap a link in the web content, you’ll see the webview follow the link. Tapping the close button will close the Android webView and also disable the WebPanel, allowing our app to behave as before.

I hope you found this tutorial useful. You can use this to show a help page, or information page directly in your Unity app that is either stored as a HTML file, or is downloaded from a website. You can add more controls to the Unity Canvas to allow you to navigate forwards and backwards, and maybe jump to a specific URL.

As always, you can follow me on Twitter @cwgtech, or check out my youtube channel at https://www.youtube.com/channel/UCdrrB0J4ovI4xQkqiK4HEiw. Please feel free to leave any comments or suggestions below, or let me know how you customized this technique for your own purpose. Subscribe to my youtube channel to get notified when I post a new tutorial.

Using Unity UI system for non-gaming app

Hello and welcome to my tutorial on how to use the Unity UI system. We’ll explore the new features and options by building a simple calculator that will respond to different display dimensions and work on multiple platforms.

This tutorial assumes a reasonable familiarity with Unity and C#. The system I’ll illustrate will work for any platform, but we’ll start with the desktop platform.

If you wish, you can download the finished project from github at https://github.com/cwgtech/UICalc.

I’ve also created three videos on youtube showing this tutorial in action:

Part 1: https://youtu.be/vQsI7wHgobw

Part 2: https://www.youtube.com/watch?v=n6gFLLSvS3U

Part 3: https://www.youtube.com/watch?v=n6gFLLSvS3U

Let’s get started by creating a new project in Unity. Unity should default to PC, Mac & Linux platform, but check by opening the Build Settings dialog. If it’s not set, then click on it and then ‘switch platform’.

Our goal here is to recreate a calculator that looks similar to the standard MacOS calculator, but it will be resizable and will adjust the width of the buttons to fill the screen or window. We’re going to achieve this by using nested layout groups to keep all the objects relative to each other, while adjusting their size automatically.

As with my other tutorials, create a few folders in the Project view to keep things organized. For this tutorial, we’ll need a Prefab folder, a Scenes folder, and a Scripts folder.

Set the Main Camera to render a black background, that will provide a backdrop for our UI. Select the Main Camera, set the Background to black (#000000FF), and then set Clear Flags to ‘Solid Color’.

Now in the Hierarchy view create a blank UI Canvas. Click ‘Create’, ‘UI’, ‘Canvas’. Highlight the Canvas object and in the Canvas script, click the ‘Pixel Perfect’ checkbox. Now select the Canvas Scaler script and change the UI Scale Mode to ‘Scale with Screen Size’. Slide the Match slider fully to the right, or type 1 in the text box. Enter ‘1000’ into the Y value for Reference Resolution.

With these changes, the UI system will constantly match the height of the screen, but as far as the UI elements are concerned, the screen is always 1000 pixels tall, irrespective of the actual screen height.

We’ll now create the UI objects that the user will see and interact with. First, create an empty gameobject under our canvas and rename it to ‘MainHolder’. Adjust the Rect Transform so it stretches horizontally and vertically to match its parent, while holding the Alt key (option on Mac).

Add the ‘Vertical Layout Group’ script to this object by clicking on ‘Add Component’, then ‘Layout’, then ‘Vertical Layout Group’. This will layout all this objects children vertically, but first we need to adjust the checkboxes. For both rows, make sure ‘width’ is checked, and ‘height’ is clear. We want this control to adjust the width of each child, but each child to control its own height.

We’re going to have two regions for our calculator. A row that will hold the digit display, and another row that will hold all the user control buttons. These rows will manage their own height, but have their width match that of the display.

Add an Image to the ‘MainHolder’ by right clicking on ‘MainHolder’, click ‘UI’, and then ‘Image’. Set this image’s height to 100 and its color to dark gray (#353535FF). Rename the Image object to ‘DigitStrip’.

Create a Text object as a child of DigitStrip by right clicking on the DigitStrip object, then click UI and then Text. Rename this object to ‘Digits’. Create another Text object as a child of DigitStrip and rename it as ‘Operator’.

Select ‘Digits’ and adjust it’s anchors to stretch horizontal and vertical, while holding the Alt key (option on Mac). Manually set the left margin to 10, and the right margin to 40. Set the default text to ‘1010101010’, the font size to ‘100’, and the color to white. Finally, set the paragraph alignment to right and vertical centered, and enable the ‘best fit’ checkbox.

This label will always fill the DigitStrip row and adjust it’s font size to allow for changes in the screen dimensions. We use the margins to inset it from the edges, and also allow space for our operator text object.

Highlight the ‘Operator’ object and adjust it’s anchors to stretch vertically and right aligned. This time hold down shift & alt (option on mac) when you click the anchor box. Manually set the width to ‘35’.

As with the Digits text, set the font size to ‘100’, color to white and check the best fit checkbox. Set the default text to ‘X’ and the vertical and horizontal alignments to center. This Text object will display the last operator the user entered, and is always to the right of the digit display.

If you now adjust the display width, you can see the Text objects change their size accordingly, and get smaller if needed, but they will always stay next to each other and at the top of the screen.

Add an empty child to the MainHolder (make sure you have MainHolder highlighted) and rename it to ‘ButtonGroup’. Set the height of the ButtonGroup to 625, which will make our buttons reasonably square on the default game view.

With ButtonGroup highlighted, add a Vertical Layout Group script just as we did before. Set the spacing to 5 (the space between rows in the group) and make sure the checkboxes are set to Width for ‘Child Controls Size’ and both Width & Height for ‘Child Force Expand’.

With ButtonGroup still highlighted, create an empty gameobject, and rename it to ‘Row (0)’. Set the height of the Row to 125 and add a Horizontal Layout Group script. Click ‘Add Component’, ‘Layout’ and then ‘Horizontal Layout Group script’. Enter 5 for the spacing value, which will set the amount of space between elements in this row, and make sure that all the checkboxes for ‘Child Controls Size’ and ‘Child Force Expand’ are set. Finally, adjust the Child Alignment to Middle Center.

Now we’re ready to start creating a button. Highlight the ‘Row (0)’ object and right click on it. Select UI and then left click on Button. This will create a default button under our Row. Adjust the color of the button background image to a light gray (#C8C8C8FF). Expand the button object in the Hierarchy view, and select the Text object. Change the Text to ‘c’, and the font size to 100. Check the Best Fit checkbox, and make sure the Min Size is 1 and the Max Size is 100.

The button will be 125 UI pixels tall and full screen width at this point. However, before we add more buttons, we will create a script for our button object, but first save the scene. Click ‘File’, ‘Save Scenes’. Navigate to the Scenes folder we created earlier, and name the scene as ‘UICalc’.

With the scene saved, right click on ‘Scripts’ and create a new C# script. Call it ‘CalcButton’. Double click it in the Project view to open it in Visual Studio. Every button will need a reference to its text label, so in our script, above the start method, add the following:

public Text label;

Right click on Text, and then ‘Quick Fix and select ‘using UnityEngine.UI’ to add this using statement to the top of the file.

Create a new method that we’ll hook up to our button function, add the following:

public void onTapped()
{
    Debug.Log(“tapped: “ + label.text);
}

For now, thats all we need, so save the file and return to Unity.

After Unity updates and compiles the code, drag the ‘CalcButton’ script onto our Button object, and then drag the Text object from the Button onto the Label reference of the ‘CalcButton’ script. Click the ‘+’ button on the On Click list of the Button script, and drag the Button object into the On Click object entry. From the drop down, select ‘CalcButton’, ‘onTapped’. This will forward the event of the user clicking or tapping on our button object to the script we just created and directly to the onTapped method.

Once again, save the scene with these modifications. At this point you can run the scene, and when you click on the button, you should see the debug output in the console window. Stop the execution and continue adding more buttons.

Create a prefab object for our button by dragging the button object into the Prefab folder we created earlier. You can now drag the prefab Button back into the Row (0) object 3 times, so we have four buttons in our row. Make sure the buttons are in the order you created them, so using the Unity default naming, you should see ‘Button’, followed by ‘Button (1)’, ‘Button (2)’, ‘Button (3)’. In the Game and Scene views you’ll see the buttons appear in our row, and adjust their width as more buttons are added.

With the row ready, we can duplicate it to create all the rows in our calculator. Right click on the ‘Row (0)’ object and then click on ‘Duplicate’. Do this until there are 5 rows. You’ll see the rows adjust their vertical position as you add more rows, until we’ve got all 5 created.

At this point you can adjust the size of the Game view in Unity, and see our buttons adjust their width to fully fill the updated size, even if the scene is not running in Unity.

As I said at the start of this tutorial, we’re going to make our calculator look similar to the MacOS calculator utility, so with that in mind, let’s set the color and content for each button. We could do this with a script at runtime, but as it will never change, the editor is as good a place as any to do this.

Start with the second button, expand it to select the text object and add the ‘±’ symbol as the default text. To get the ‘±’ symbol you’ll need to enter a unicode value, which Unity doesn’t seem to support on Windows or Mac directly. Switch to Visual Studio and put the cursor at the end of the comment above our Start method.

If you’re using Windows hold down ALT and type ‘+0177’ (your computer may chime when you type the + key, just ignore it).

On the Mac, unless you have enabled unicode typing, you’ll use the character viewer so tap CTRL-CMD-Space to bring up the viewer, then type u+00B1 in the search box. Double click on the character that appears and it will be inserted into your Visual Studio text.

With the unicode character now in Visual Studio, highlight it, copy and cut it with CTRL-X (CMD-X on Mac) and then switch back to Unity and paste it with CTRL-V (CMD-V on Mac) into the text box for the 2nd button. Use the same procedure for other Unicode characters as we need them.

Set the text for the 3rd button to ‘%’.

For the fourth button, set the background color to orange (#FF6526FF) and the text to the ‘÷’ sign, (Win, ALT+0247, Mac U+00F7). Add the color to your color presets as you’ll use it for other buttons. Set the color of the text of this button to White.

Expand the Row (1) object and set each button’s text to 7,8,9 and ‘x’ respectively. For the first three buttons, set their background color to white, and the fourth button to orange.

Expand the Row (2) object and set each button’s text to 4,5,6 and ‘-’. Use the same color pattern as the previous row.

Expand the Row (3) object and set each button’s text to 1,2,3 and ‘+’. Again, use the same color pattern as the previous row.

Expand the final row, Row (4), and set each button’s text to 0,0,’.’ and ‘=’. Use the same color pattern as before. Disable the 2nd button of this row, and you’ll see the other 3 buttons expand to fill the space. We’ll add code to double the width of the first button later.

Now you can see our calculator take shape, though it has no function yet. Save the scene and run it. When you click on a button you’ll see it’s caption output to the debug console.

Create a new C# script in our scripts folder and call it Manager. When Unity is ready, drag it onto our ‘Canvas’ object. We’ll use this script to handle all the global functions for our calculator including managing the button size and the calculator logic.

Double click on the script to switch to Visual Studio and add the following lines to the top of the class:

public VerticalLayoutGroup buttonGroup;
public HorizontalLayoutGroup bottomRow;
public RectTransform canvasRect;
CalcButton[] bottomButtons;

You’ll need to right click on VerticalLayoutGroup and select ‘using UnityEngine.UI’ to pull in the references for these classes.

Create an Awake method with the following:

private void Awake()
{
    bottomButtons = bottomRow.GetComponentsInChildren<CalcButton>();
}

With this code, we’ll fetch references to all our CalcButton objects that are children of the last row into bottomButtons This will allow our code to manipulate the buttons.

Save the file and return to Unity, so we can hook up the public references to the correct objects in the hierarchy.

Expand the Canvas object, then MainHolder, and the ButtonGroup. With Canvas selected, drag the ButtonGroup object into the ButtonGroup row in the inspector, drag Row (4) into the Bottom Row entry, and finally the Canvas into the Canvas Rect entry.

Save the scene, and then double click on the CalcButton script. We’re going to add a RectTransform getter to this script, which will allow us to fetch a button’s RectTransform when we need it. Add the following after the Public Text Label line.

public RectTransform rectTransform
{
    get
    {
        if (_rectTransform == null)
            _rectTransform = GetComponent<RectTransform>();
        return _rectTransform;
    }
}
RectTransform _rectTransform;

This getter will return the cached copy of this object’s RectTransform, if its set, otherwise it will fetch it, cache it and then return it.

While we’re here, add one more getter method that will allow us to access the Manager script from CalcButton.

public Manager calcManager
{
    get
    {
        if (_calcManager == null)
            _calcManager = GetComponentInParent<Manager>();
        return _calcManager;
    }
}
static Manager _calcManager;

This time we use a static var to cache the reference as its the same for every button object. We get our reference to it by searching back up through the hierarchy.

Save this script and switch to the Manager script.

Before we write the hook code, lets setup a few other items we’ll use in a moment. Add a variable above the Awake method:

bool canvasChanged;

In the Start method add the lines:

bottomRow.childControlWidth = false;
canvasChanged = true; 

This allows the children of Row (4) to control their own width. We could also uncheck the checkbox for Row (4), but then our rows will no longer adjust themselves when we change the Game view in the editor (the rows will only resize to match the width of the widest row).

We’re going to use a method hook called OnRectTransformDimensionsChange that is triggered when a RectTransform of an object that has a script with this method defined is changed. This is why our Manager script is attached to the Canvas object.

Anytime the Canvas or any of its children change, this method will be called on all attached scripts. This method is actually part of UIBehavior which is a subclass of MonoBehavior.

Add the following method after the default Update method:

private void OnRectTransformDimensionsChange()
{
    canvasChanged = true;
}

We set a flag when we detect a change in the RectTransform and don’t actually adjust the buttons until the update method. While this can add a 1 frame lag, it’s not noticeable, but means we only adjust the buttons once per frame and not multiple times. This hook is called when the canvas changes, and any of its children change, so it would be triggered a few times when a change is made.

Now add the method that will adjust the width of the buttons on the bottom row:

void adjustButtons()
{
    if (bottomButtons == null || bottomButtons.Length == 0)
        return;
    float buttonSize = canvasRect.sizeDelta.x / 4;
    float bWidth = buttonSize - bottomRow.spacing;
    for (int i = 1; i < bottomButtons.Length; i++)
    {
        bottomButtons[i].rectTransform.
        SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, bWidth);
    }
    bottomButtons[0].rectTransform.
    SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, bWidth * 2 + bottomRow.spacing);
}

This method gets the current width of our canvas divided by 4, to calculate the size of one button. We adjust to allow for the spacing of the objects and then set all but the 1st button on the last row to this width, which will be the same as the standard button width. For the 1st button on the last row, we set it’s width to be twice the width of a normal button, plus the horizontal spacing.

Finally adjust the Update method to call adjustButtons if the canvasChanged flag is set. Add the following lines to Update:

void Update () {
    if (canvasChanged)
    {
        canvasChanged = false;
        adjustButtons();
    }
}

Now when a change is triggered, we’ll call the adjustButtons method which in turn will set the size of the buttons on the bottom row.

Save the changes and switch back to Unity, let it update and then click Run. You’ll see the 0 button adjust to be double width, and if you change the width of the game view, you’ll see all the buttons change and the 0 button match twice the width of a normal button.

Exit Play mode and save the scene.

We’re going to add the calculator logic, but just before that, we’ll need to add some references to UI objects that we’ll hook up in the Editor and some private vars to manage the calculator logic. Switch back to Visual Studio and add the following lines to the Manager class after the public vars that we’ve already got:

public Text digitLabel;
public Text operatorLabel;
bool errorDisplayed;
bool displayValid;
bool specialAction;
double currentVal;
double storedVal;
double result;
char storedOperator;

Add a method after the adjustButtons method that will be called when we tap the ‘c’ button, to clear the calculator to a known state:

    void clearCalc()
    {
        digitLabel.text = "0";
        operatorLabel.text = "";
        specialAction = displayValid = errorDisplayed =false;
        currentVal = result = storedVal = 0;
        storedOperator = ' ';
    }

A method to update the display label:

    void updateDigitLabel()
    {
        if (!errorDisplayed)
            digitLabel.text = currentVal.ToString();
        displayValid = false;
    }

A method to evaluate the calculation based on the operator passed. As you’ll need to enter the unicode character ‘÷’ when typing this function, either use the method shown in part 1 of this tutorial, or switch back to Unity, highlight the button in question and copy the character from the text field.

    void calcResult(char activeOp)
    {
        switch (activeOp)
        {
            case '=':
                result = currentVal;
                break;
            case '+':
                result = storedVal + currentVal;
                break;
            case '-':
                result = storedVal - currentVal;
                break;
            case 'x':
                result = storedVal * currentVal;
                break;
            case '÷':
                if (currentVal != 0)
                {
                    result = storedVal / currentVal;
                }
                else
                {
                    errorDisplayed = true;
                    digitLabel.text = "ERROR";
                }
                break;
            default:
                Debug.Log("unknown: " + activeOp);
                break;
        }
        currentVal = result;
        updateDigitLabel();
    }

Finally add the method that will process the button taps. Once again, this method requires a unicode character, specifically ‘±’ character, so use the same method as last time.

public void buttonTapped(char caption)
    {
        if (errorDisplayed)
        {
            clearCalc();
        }
        if ((caption >= '0' &amp;&amp; caption <= '9') || caption == '.')
        {
            if (digitLabel.text.Length &lt; 15 || !displayValid)
            {
               if (!displayValid)
                   digitLabel.text = (caption == '.' ? "0" : "");
               else if (digitLabel.text == "0" && caption != '.')
                   digitLabel.text = "";
               digitLabel.text += caption;
               displayValid = true;
            }
        }
        else if (caption == 'c')
        {
            clearCalc();
        }
        else if (caption == '±')
        {
            currentVal= -double.Parse(digitLabel.text);
            updateDigitLabel();
            specialAction = true;
        }
        else if (caption == '%')
        {
            currentVal= double.Parse(digitLabel.text) / 100d;
            updateDigitLabel();
            specialAction = true;
        }
        else if (displayValid || storedOperator == '=' || specialAction)
        {
            currentVal = double.Parse(digitLabel.text);
            displayValid = false;
            if (storedOperator != ' ')
            {
                calcResult(storedOperator);
                storedOperator = ' ';
            }
            operatorLabel.text = caption.ToString();
            storedOperator = caption;
            storedVal = currentVal;
            updateDigitLabel();
            specialAction = false;
        }
    }

We need to add a call into the Start method that will initialize our calculator logic to a clear state when we start. Add this line to Start:

    buttonTapped(‘c’);

This will simulate tapping the ‘c’ key when we start. Note that the logic uses a simple char type to check for what to do with each key. If you didn’t use a lowercase ‘c’ for the first button, then you’ll need to change what the logic expects. Save the file.

Before we can call this logic working, we need to make another change to the CalcButton script. Switch to that file and modify the onTapped method with:

    calcManager.buttonTapped(label.text[0]);

This will pass the first char from the button text string to the calculator logic. There’s no error checking here, as every button has had its text string set in the editor.

Save the file and switch back to Unity. Let it update and when it’s done, highlight the Canvas object in the Hierarchy. Expand DigitStrip and drag the Digits object into the Digit Label holder, and Operator into the Operator Label holder.

Save the scene and click Play.

As you click on the buttons, you’ll see the calculator behave as expected. You can enter a number with or without a fractional part, and add, subtract, multiply, or divide it with another number. More importantly, you can do so while modifying the game view and the buttons and displays will shrink or expand to fill the view horizontally.

You can also build a stand alone app from the scene and see what happens when you modify the window size with the mouse. Click ‘File’ and then ‘Build Settings’. Click ‘Player Settings’ and then expand the ‘Resolution & Presentation’ tab. Enable ‘Resizable Window’. Click ‘Build and Run’ and enter a name for the executable, I used UICalc. Unity will build a run your application.

When the launcher appears, check windowed mode, and set the resolution to be less than full screen and click “Play!” to start our calculator. You can see as you adjust the window size, the buttons and the display will scale with the size, but always keep their relative position, and the ‘0’ button will continue to be twice the width of the other buttons.

Quit the executable, and return to Unity. If you open the Build Settings dialog again, you can now make the app even more ‘app’ like and less game like. Uncheck ‘Default is Full Screen’. Set the default width to 400 and the default height to 600. Change the ‘Display Resolution Dialog’ to ‘Hidden by Default’.

Now if you ‘Build and Run’, you’ll no longer get the resolution dialog, but the app will appear right away.

Connect your android device, or launch the emulator. While you can launch the emulator from the command line, the easiest and most convenient method is to launch Android Studio and then AVD manager. I’m using the emulator here so I can record the app running.

Set the package name to something other than default, I used ‘com.cwgtech.uicalc’. Click ‘Player Settings’, expand the ‘Other Settings’ tab and type in the new package name. We don’t need to make any other changes at this time, so we’ll click ‘Build And Run’ to build the APK and send it to our Android device. Once again, I used the name ‘uicalc’.

With the app running, you can see the calculator looks as expected, and works just like it did on the desktop. If you rotate the device, you’ll see the buttons adjust for the landscape view and fill the new wider look.

You can continue to rotate the device and see that it works in all four orientations.

Assuming you have the ability to build for iOS, open Build Settings and switch to the iOS platform. In my case, I want to build for the simulator, so click ‘Player Settings’, expand the ‘Other Settings’ tab and select ‘Simulator SDK’ in Target SDK.

Click ‘Build and Run’, enter the name of a folder for the project, I like to use iosBuild, and the project will build and launch in XCode.

Now we can run the app on the simulator, and pick an iPhone model. The app contents will adjust to fill the display horizontally, and also continue to do so if we rotate the device.

Likewise, if we run it on an iPad device, the app will adjust for the different display resolution and aspect ratio, and still handle the rotation correctly.

That completes my Unity UI system tutorial. I’m hoping this inspires you to use Unity to create multi-platform non-game apps. Having a system that can provide your users with a UI that adapts to different displays and orientations is a great thing for them. Having a system that lets you use the same code on multiple platforms is a great thing for you!

As always, you can follow me on Twitter @cwgtech, or check out my blog at www.cwgtech.com. Please feel free to leave any comments or suggestions below, see you next time.

Sharing an image via ActivityViewController with iOS and Unity3D

Welcome to my tutorial on how to share a screen image on an iOS device by extending our Unity Plugin.

We will add a share function to our plugin code that will make use of the standard iOS ActivityViewController. Using this View Controller is quite complex and powerful, and will require a setup that is dependent on the device being used.

Once again this tutorial assumes a reasonable familiarity with unity, C sharp, objective-c, iOS programming and XCode.

Let’s get started by loading up our previous project in Unity and adding a share button to the scene.

In the Hierarchy view, click ‘Create’ and select UI->Button. This will create a Canvas and add a button directly to it.

Select the button, adjust it’s anchor to be top-center and set the Y pos to -15, which should put the top of the button against the top of the screen.

Expand the button in the hierarchy and click on Text and change the text string to ‘Share’.

Now double click our PluginTest script to open it up in Visual Studio so we can add the C# share function.

First, let’s add the reference to the external IOS method that we’ll use. In the section with our other extern declarations, add the following two lines:

[DllImport("__Internal")]
 private static extern void IOSshareScreenImage(byte[] imagePNG, long imageLen, string caption, intCallback callback);

We’re going to need a public reference to our UI button. So add the line

 public Button shareButton;

We’ll also need to add the appropriate Using statement, so you can right click on Button, select Quick Fix and then ‘using UnityEngine.UI;’.

Comment out the line in our Start method that randomly shows the alert dialog. We’ll use the alert dialog to let the user know when the share method is finished along with the completion result later.

Let’s add the method that our Button will hook into. This method will call into another method that will actually create the screenshot and share it. To make our screenshot clean, we’ll hide the button before we take the screenshot and then restore it when the screenshot has been shared.

//called when the user taps the 'share' button
public void ShareScreenTapped()
{
  if (shareButton != null)
    shareButton.gameObject.SetActive(false);
  ShareScreenShot(Application.productName + " screenshot", (int result) =>;
    {
      Debug.Log("share completed with: " + result);
      CreateIOSAlert(new string[] { "Share Complete", "Share completed with result " + result, "Ok" });
      if (shareButton != null)
        shareButton.gameObject.SetActive(true);
    });
}

We’re using an anonymous function that will be called when the share function completes. This lets us re-enable the button and pop-up an alert view with the result from the share function.

This next section is quite complex. It involves saving off a reference to the passed function, a callback function that will receive the success/fail status from the iOS ActivityShare function, a method to take the screenshot and then a co-routine to wait for the end of the frame before grabbing a copy of the frame buffer and passing it to our iOS method as a PNG.

First, let’s create two static variables. One to hold the passed function reference:

static System.Action ShareCompleteAction;

And one to hold a bool state that will be true while we’re sharing a screenshot:

static bool isSharingScreenShot;

This is to prevent the method being called while we’re waiting for a previous call to complete.
Now we’ll add the callback function that will be called from iOS. In order to do that, we need to mark it with MonoPInvokeCallback so the compiler knows to marshal the call correctly.

[AOT.MonoPInvokeCallback(typeof(intCallback))]
static void shareCallBack(int result)
{
  Debug.Log("Unity: share completed with result: " + result);
  if (ShareCompleteAction != null)
    ShareCompleteAction(result);
  isSharingScreenShot = false;
}

This is similar to the callback function used by the AlertView, and in fact uses the same intCallback type. When this function is called, it will trigger the stored reference to the function passed into ShareScreenShot, if it’s not null, and clear the isSharingScreenShot flag as we’re finished.

We’re now ready to add the ShareScreenShot method. This method will make sure we’re not already sharing a screenshot and then start a co-routine that will create a texture containing the current frame buffer. As recommended by Unity, we’ll wait for the end of the next frame to ensure the frame buffer is fully rendered. Add the following lines:

public void ShareScreenShot(string caption, System.Action shareComplete)
{
  if (isSharingScreenShot)
  {
    Debug.LogError("Already sharing screenshot - aborting");
    return;
  }
  isSharingScreenShot = true;
  ShareCompleteAction = shareComplete;

  //grab the screenshot & send it to iOS
  StartCoroutine(waitForEndOfFrame(caption));
}

The co-routine will wait for the end of the next frame and then send the texture we create to our iOS method. Add the following lines to complete our C# modifications:

IEnumerator waitForEndOfFrame(string caption)
{
  yield return new WaitForEndOfFrame();
  Texture2D image = ScreenCapture.CaptureScreenshotAsTexture();
  Debug.Log("Image size: " + image.width + " x " + image.height);
  byte[] imagePNG = image.EncodeToPNG();
  Debug.Log("png size: " + imagePNG.Length);
  if (Application.platform == RuntimePlatform.IPhonePlayer)
    IOSshareScreenImage(imagePNG, imagePNG.Length, caption, shareCallBack);
  Object.Destroy(image);
}

After waiting until the end of frame, we grab the frame buffer into a 2D texture. From this, we construct a PNG rendition of the image, and then after ensuring we’re on an iOS platform, call into the iOS method, passing the PNG, it’s length, the caption and a pointer to our shareCallBack function.

With the C# modifications completed, we need to hook up our button and also set the reference to it for our script. I had to adjust my Unity layout so you could see how I connected the button to the ShareScreenTapped method.

Now it’s time to update our iOS code. Double click the file ‘MyPlugin’ in the Plugins/iOS folder. This will launch XCode and open our file.

First, add a method to the MyPlugin class that will package the PNG image and caption into an NSArray and pass them to an ActivityViewController.
Before we do that, we need to add a few extra variables to our class. In the @interface declaration at the top of the file add these two lines:

INT_CALLBACK shareCallBack;
UIPopoverController *popover;

The shareCallBack variable will hold the pointer to our C# callback function, while the popover variable will point to the popover controller we’ll use if the code is running on an iPad device.

With that done, we can add the method’s we’ll need to the main class by adding the following lines before the @end statement for the MyPlugin @implementation:

-(void) shareScreenImage:(const unsigned char*) imagePNG_in length:(long)length caption:(const char*) caption_in callback:(INT_CALLBACK) callback
{
  NSMutableArray *shareableItems = [NSMutableArray arrayWithCapacity:2];
  //This array will hold the caption and image so we can send it to the share activity.
  NSString *caption;
  UIImage *image;
  if (caption_in!=nil)
  {
      caption = [MyPlugin createNSString:caption_in];
      [shareableItems addObject:caption];
  }
  if (imagePNG_in!=nil)
  {
      NSData* pngData = [NSData dataWithBytes:imagePNG_in length:length];
      image = [UIImage imageWithData:pngData];
      [shareableItems addObject:image];
      pngData = nil;
  }

This will convert the caption, if it’s set, to an NSString and the PNG data to a UIImage. Note we have to first convert the supplied byte array into a NSData object, and we need the length of the array to achieve this.

shareCallBack = callback;
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:shareableItems applicationActivities:nil];
activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
    NSLog(@"Activity %@ completed: %d",activityType,completed);
    if (activityError!=nil)
        NSLog(@"Error: %@",[activityError localizedDescription]);
    if (shareCallBack!=nil)
        shareCallBack(completed);
};

Here we create a UIActivityViewController with the shareableItems array we setup and populated earlier. We also construct a completionWithItemsHandler. This function will be called when the user completes the share function, either by selecting a method, or by cancelling the request.

The completion function will pass the completed bool to our callback method, assuming it’s not nil.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    [UnityGetGLViewController() presentViewController:activityViewController animated:YES completion:^{
        NSLog(@"share presented");
    }];
else
{
    popover = [[UIPopoverController alloc] initWithContentViewController:activityViewController];
    UIView *mainView = UnityGetGLView();
    popover.delegate = nil;
    [popover presentPopoverFromRect:CGRectMake(mainView.frame.size.width/2, mainView.frame.size.height-10, 0, 0) inView:mainView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}

In order to present the activity view controller, we need to use two different methods, one for iPhone devices and another using a popover controller for iPad devices. We’ll make the choice by checking the result of the macro UI_USER_INTERFACE_IDIOM.

On iPhone devices, we can present the ViewController using the main Unity ViewController directly, while on iPad devices, we first create a popover controller and then use the main Unity View as the parent view to present the popover.

In both cases, the ActivityViewController finish method is used to notify Unity that the share is complete via our supplied callback function.

Finally, we need to add a C style method that our C# code will call into, which will call our new share method on the plugin. Add the following lines to the extern C block:

void IOSshareScreenImage(const unsigned char* imagePNG, long imageLen, const char* caption, INT_CALLBACK callback)
{
    [[MyPlugin sharedInstance] shareScreenImage:imagePNG length:imageLen caption:caption callback:callback];
}

This completes the iOS code modifications, however we need to add a key to the application’s plist file that let’s iOS know we may want to save screenshots into the user’s camera roll.

Click on the Info.plist file in the file hierarchy in Xcode, and then click the + icon to the right of the first line. In the text box that pops up, type the following:

Privacy - Photo Library Additions Usage Description

And then in the string box to the right, add a description that a user might see when they try to save a screenshot. I used:

Allow access to save screenshots

With all these modifications in place, we’re ready to build and test the code. Switch back to Unity, give it a few seconds to rebuild the C# code and then press CMD-B to start the project building.

Once XCode has finished building the project the code will execute on our simulator. Tap the ‘share’ button at the top of the screen and you’ll see the activity controller appear. You can then select what iOS will do with your image.

Go back to XCode and select an iPad as a target device. Now when you run the code and tap the share button, you’ll see a popover appear with the share activities in it. Unity continues to run behind this popover as you’d expect.

Both types of device will display an alert when the share is completed with the bool completion status. A 0 indicates the share failed, while a 1 indicates success.

If you run on a real device, then you’ll get more share options, including iMessage and Facebook if you have the app installed. If you use iMessage, along with the screenshot image you’ll see the caption text we’ve passed through. Notice that the screenshot doesn’t have our ‘share’ button on it, as we hid that before we grabbed the frame buffer.

We’ve now added a share function to Unity that allows the user to send a screenshot to various activities on their device without leaving your App. Use this to let users send highscore images, or new level images to their friends.

I hope you found this tutorial useful and are able to use it to add sharing to your apps. The code for this plugin can be found at https://github.com/cwgtech/iOSUnityShareScreenShot and the video at https://youtu.be/NwphcgWQMhQ.

As always, please feel free to reach out with any comments or questions.

Using a child activity to wait for onActivityResult with Unity3D

Welcome to my tutorial on how to extend our Unity plugin to get a callback from onActivityResult without overriding the standard Unity Player Activity.

We will add a child activity to our plugin that will be launched when required, and will wait for a call to startActivityForResult, and pass that back to our C# callback function.  Normally, we’d do this by extending the UnityPlayerActivity class, but that means we won’t play nice with any other plugins or extensions that want to do the same thing, and we must make sure our Android project imports the correct version of the UnityPlayer each time we upgrade.

Once again this tutorial assumes a reasonable familiarity with Unity, Java, Android programming and Android Studio.

Start by loading up our previous project in Unity and the MyPlugin project in Android Studio.  All of the changes we’re going to make this time will be entirely to the Java code.

Right click on the Unity tab in the Project View and select New/Activity/Empty Activity.

Call the Activity “OnResultCallback”, deselect ‘backwards compatibility’ and make sure the package name matches the package name you’ve been using.  For me, that is ‘com.cwgtech.unity’. Click Finish.

If you forget to uncheck the backwards compatibility box, you’re new activity will extend AppCompatActivity.  You need to change that to Activity.

Add the following four lines:

public static final String LOGTAG = MyPlugin.LOGTAG + “_OnResult”;
public static MyPlugin.ShareImageCallback shareImageCallback;
String caption;
Uri imageUri;

You’ll get an error on MyPlugin.LOGTAG, so you’ll need to switch back to the MyPlugin class and change the LOGTAG definition from private to protected.  We’re going to use this modified LOGTAG to identify the Log entries from this child activity, while the static callback variable will hold a pointer to the C# callback our main plugin receives.

Switch back to our new activity, and add the following method before the onCreate method:

void myFinish(int result)
{
    if (shareImageCallback!=null)
        shareImageCallback.onShareComplete(result);
    shareImageCallback = null;
    finish();
}

We’ll use this to exit our activity, calling the callback method if it exists, and then clearing it after use.  Now modify the default onCreate method. Remove the line

setContentView(R.layout.activity_on_result_callback);

This new activity will not have a content view, so we’ll not need to set it.

Add the following lines:

Log.i(LOGTAG, "onCreateBundle");
Intent intent = getIntent();
if (intent != null) {
    caption = intent.getStringExtra(Intent.EXTRA_TEXT);
    imageUri = (Uri)intent.getExtras().get(Intent.EXTRA_STREAM);
    Log.i(LOGTAG, "Uri: " + imageUri);
}
if (intent==null || imageUri==null)
{
    myFinish(1);
    return;
}

This will get the intent passed to our activity and grab the caption and imageUri that were included in the intent.  If there is no intent or image, then just exit the activity, as we’ve nothing to do and we’ve been called incorrectly.

Now to call the share intent and wait for a result.  Add the following:

try
{
    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setDataAndType(intent.getData(),intent.getType());
    shareIntent.putExtra(Intent.EXTRA_STREAM,imageUri);
    if (caption!=null)
        shareIntent.putExtra(Intent.EXTRA_TEXT,caption);
    startActivityForResult(Intent.createChooser(shareIntent,"Share with..."),1);
}
catch (Exception e)
{
    e.printStackTrace();
    Log.i(LOGTAG,"error: " + e.getLocalizedMessage());
    myFinish(2);
}

We copy forward the data from the incoming intent to a new intent, which we then pass on to the chooser and wait for a result.  Wrap the whole thing in a try/catch so any errors will be flagged and the app will not crash.

Add a new method that will override the default onActivityResult method and pass the resultCode back to our callback.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.i(LOGTAG,"onActivityResult: " + requestCode + ", " + resultCode + ", " + data);
    myFinish(resultCode);
}

The result code will be -1 or 0 depending on the share activity, so we’ll pass that back to our C# method.  Note that earlier we sent a 1 or 2 depending on the error condition, so our C# code could check for that and give the user more information.

That’s the Java code for our child activity completed, but we’ve a few things to clean up in the manifest and we can also remove the layout that was auto-created.

Expand the res folder in the project hierarchy and right click on the ‘layout’ folder.  Select delete and confirm the deletion. This will remove the folder and the un-needed layout file.

Now expand the manifest folder and double click the AndroidManifest.xml file.  After the name=”.OnResultCallback” but before the closing > add the following line:

android:theme="@android:style/Theme.Translucent.NoTitleBar"

This will cause our child activity to effectively have no display.  If we use the NoDisplay theme, then we run into a problem on Android 6 and higher.  A NoDisplay theme expects an activity to call finish before it’s resumed, and in our case that means we don’t get the OnResultCallback as our activity has been terminated.

Switch back to the MyPlugin java source where we will modify the plugin to use our new child activity.

Go to the section in shareImage where we prepare the shareIntent.  We’re going to replace the call to startActivity with the following three lines:

shareIntent.setClass(mainActivity,OnResultCallback.class);
OnResultCallback.shareImageCallBack = callback;
mainActivity.startActivity(shareIntent);

Make sure you remove the line:

mainActivity.startActivity(Intent.createChooser(shareIntent,"Share with..."));

And you can also remove any references to the ‘result’ variable, as we will no longer use it, including the line:

callback.onShareComplete(result);

As our new child activity will call the callback hook when the OnActivityResult is triggered.

That’s all the modifications completed, so build the plugin by clicking on the green play button, assuming you’ve still got the copyPlugin task indicated in the dropdown.  When gradle completes, you can switch back to Unity and build the project using the new plugin.

I’m going to run the apk on the emulator as before, but now when I tap the share button, I’ll still get the share dialog, however the result pop-up alert will not occur until I’ve finished interacting with the share dialog.

And that’s it done.  We’ve now got a child activity that will send our image to the share system, and wait for it to finish and return a value, which we then forward to our Unity App.  You can use this technique for any intent you need a result from. You’ll need to add custom code that either uses the requestCode passed when the activity is started to decide how to handle the passed intent, or create other child activities that just handle your specific case, whether that is a photo-picker or a QR code scan request.

You can download the source code for this plugin from https://github.com/cwgtech/AndroidActivityResult, and watch the video of this tutorial at https://youtu.be/HrhYWBqxkn8

Please feel free to post any comments or questions.

Joomla 3 Module with selectable template output

I’ve been working on a Joomla based website the past few weeks, and getting my head around the world according to Joomla 3.  I’ve been using modules for various parts of the web pages, and today I wanted to have a module adjust is output based on a configuration setting.

In the Joomla backend, you create a ‘module’ instance that acts as a conduit for the actual module code.  You specify where and when that instance will appear on the user pages. There are quite a lot of parameters you can specify, but I needed to be able to switch how the module renders its content.

To do this I added an entry to the modules XML file in the <config> section:


  
    
      ;    
 

This added an entry ‘Layout’ in the Advanced tab for my module instance in module manager.

I also duplicated the default.php file in my module tmpl folder, and named it alt-view.php for now.  With that done, going to the advanced tab in module manager for my module, I could select either default or alt-view as the layout.

In order for the module to use the right template, I needed to modify the main module code.  My module is called cfviewposts, so mod_cfviewposts.php contains the main entry point for the module.

The last line of the PHP code loads the template and then execution rolls into it.  I changed it to the following line

require JModuleHelper::getLayoutPath('mod_cfviewposts',$params->get("layout","default"));

This pulls the ‘layout’ param from the module parameters, and if it’s not present, defaults to ‘default’ which will load default.php from mod_cfviewposts/tmpl.

So now I can create a 2nd instance of my module, and using the newly minted advanced configuration option, select the alternate output when that module is displayed.