Unity completions for Sublime Text

This might be a technical post, about Sublime Text. Or it could be a community post, about Unity editors. I don’t know yet. In any case it’s about Unity Completions, my first package for ST.

Unity Completions does one thing: It allows me to develop for Unity using ST. I know ST’s got its quirks, but it’s homey. And I eventually fell for its simultaneous editing – it’s stupendous! I’ve been using it quite contently for a long while. But the lack of Unity auto-completions, though, was dissatisfying. I couldn’t let it slide.

What Was

I searched the forums for solutions. There was no solution available for Boo developers. I found a post with a good solution for C-sharp developers only, based on CompleteSharp. And I found Jacob Pennock’s package. It fell short on content — it only offered completions for a handful of GUI-related completions — but it showed me how it can be done.

What Is

The next step was natural: I had to create a plugin that covers the full API. But I needed the info – the Unity API. I took a naive approach and wrote a crawler that just scans the entire scripting reference section and produces all the class names and definitions, including variables and function definitions. The output contained a little over 10,000 items. I then wrote a generator that converts it all to ST snippet files for 3 languages. It worked.

I wrote the package in March (9 months ago), when Unity 4.1 was out. I also tried to add it to Package Control, but those guys were wondering why it is should be added when a package with a similar name already exists (Jacob’s). At this point I tried to contact Jacob, but got no reply. By the time I got back to the subject, a new version of PC was in the pipes, soon to be released. So I decided to wait. Then I forgot about it for a while.

A couple of weeks ago I was reminded about it, and soon after added it to PC. It was immediately downloaded by users. In the first 14 days it was downloaded 128 times. It’s not a lot, but it seems a demand exists. The next step was to update the package – Unity v4.3.1 was recently released. I had to update the crawler because changes in Unity’s website broke it.

What’s Next

Now, two challenges remain. One is how to make the package smaller. The package currently holds snippets for a little over 12,000 Unity items. Each has two files (upper case and lower case – or it won’t work well) and all this for each of the 3 languages. This brings the total number of files to 72,399 and the entire package size to 26 MB.

Another challenge is how to keep updating the package easily. The process breaks whenever something changes in Unity’s website. Perhaps a better approach would be to dig into the DLLs?

Let my folder go!

Recycle Bin

Your computer is yours. The files in it are yours. And you should have the right to execute them! No, not as in running them… But as in putting them to death.

But sometimes this happens:

The action can't be completed because the folder or a file in it is open in another program. Close the folder or file and try again.

Surely you wonder at this point why anyone would want to delete Shpongle. Relax, of course no one would ever want that – that’s ludicrous! But let’s say for a moment that you want to share Shpongle with a friend. And instead of emailing files directly from your music folder you decide to copy them first into a temporary folder, maybe on your desktop, then pick a few of your favs and zip them. Then you might attach the zip file from that folder to an email like I taught you and send it to spread happiness. And then you may try to delete the temporary folder – to no avail.

Why can’t I delete my own folder?

Well, technically, some app is using it. Sometimes you simply forgot to close it. But sometimes an app may erroneously keep an open handle to a folder even after it’s finished using it. (Gmail, for example, seems to have this bug where it keeps an open handle to a folder after attaching a file from it.)  Anyway, I think we can both agree that it would have been easier if the error message included the name of app.

So how do I fix this?

This would be one of these situations where it would be nice to use the Force. That might work… but today we will use Process Explorer. This little tool can easily identify which app is using your file and thus keeping the folder undeletable. It can even close the handle, freeing the file or folder without killing the app.

If you don’t have Process Explorer yet you can get it here. Notice you’ll get a ZIP file with 3 files in it. It’s what is known as a portable app – there’s no installation. Just extract the 3 files to wherever you want. Then open procexp.exe with administrator permissions (right click => Run as administrator => allow it to make changes to the computer when prompted.)

By the way, did you notice the “Windows Sysinternals” title on top of the page? Process Explorer is part of a great set of tools called Sysinternals which fulfills many fundamental duties. So fundamental that Microsoft acquired Winternals, the company that made them, back in 2006 and made the tools its own. And they’re all totally free.

To the point. Short instructions:

  1. Open Process Explorer
  2. Find the open handle to your file or folder
  3. Close the app or close the handle

I’ll explain.

Step #1: Identifying the culprit

Ok, so you’ve got Process Explorer running. And yeah, it looks kinda intimidating. That’s because this little tool is fully capable of replacing your Task Manager. What you can see here is a list of all the running processes (top section) and the list of open handles for the selected process (bottom section).

But that’s besides the point right now. Kindly ignore all that and hit the Find icon, or press Ctrl+F:

This should open a dialog box with a search field. Enter the name of the file or folder you (desperately) want to delete, or just part of it, and hit Search. After a little while you should see the results:

Process Explorer - Search Results

I guess we found the culprit.

Step #2: You could simply close the app

Ok, so now you know the name of the process, which should help you identify the app, in our example Chrome. At this point you could simply close the app and fix the problem. That would be the safest option and usually it is also the best. You could also kill the process (quite brutally) via Process Explorer. There is another option.

Or you could close the handle

Sometimes you may not want to close the bothersome app. For example if the app is your browser and you have many open pages. Instead, you can close only the handle, and that should fix the problem. But be aware that this is an aggressive act – you’d be pulling the rug from under the app, which might crash or behave unexpectedly. Still, if you believe the app is not really using the file but just hogging it, then there should be no problem.

So, how’s it done? In the results box choose the result from the list. The main window (behind) should change to reflect the selection:

The main window now shows the process (top section) and, more importantly, the specific handle (bottom section). Right click the handle and choose Close Handle. You will be warned: “Forcing a handle closed can lead to an application crash and system instability. Continue with close?”  You know what to do.

That’s it, you’re free!

And now for something completely different

A couple of alternatives to Process Explorer:

Unlocker – Supposedly provides the same service and with less clicks. According to the description it is ad supported, which could be annoying. Moreover, the app comes with an “Assistant” that runs quietly in the background. I’m not sure why.

WhoLockMe – A free tool that seems to integrate into Explorer. From what I can read it tells you who locked the file, but doesn’t provide a way to unlock it.

I haven’t tried these myself, but knock yourself out.

Google Analytics for mobile apps in Unity

Google Analytics + Unity

There are several solutions out there for integrating Google Analytics into Unity apps. However, as far as I can tell they all wrap the good old web tracking service. The guys at Google have long ago tailored a more suitable solution for mobile platforms: Mobile App Analytics. It provides a few mobile-only features (e.g.: user engagement, crashes and exceptions, device info) and has the added benefit of being official. Google have released SDKs for Android and iOS. In this post I will demonstrate how to integrate the Android SDK v2 into Unity apps. Be patient, iOS is on the road map.

Setup your Analytics Account

First of all you need to have a Google Analytics account. To use Mobile App Analytics you also need to add to your account an app tracking property and a profile. When you’re done you shall be the proud owner of a Tracking ID. It should look something like this: UA-123456-7.

Create an Android Plugin

I already covered the subject of creating an Android plugin in another post. I will expand on it to keep this post short.

Get the Analytics SDK

Download the Android SDK. (At the time of writing the version was 2.0 beta 5 and the download page warned that the SDKs are under “active development”.)  After downloading the ZIP file, extract libGoogleAnalyticsV2.jar from it (or whatever the JAR file is called) and add it to your Eclipse project.

This includes copying it to the libs directory and adding it to the build path. You may not see the JAR file in Eclipse until you refresh (F5). To add the JAR file to the build path right click on it and select Build Path => Add to Build Path.

MainActivity.java

We will subclass UnityPlayerActivity and use the EasyTracker class:

package com.companyname.appname;

import android.util.Log;

import com.google.analytics.tracking.android.EasyTracker;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {
    @Override
    protected void onStart() {
        super.onStart();
        Log.d("Unity", "Starting Analytics activity");
        EasyTracker.getInstance().activityStart(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d("Unity", "Stopping Analytics activity");
        EasyTracker.getInstance().activityStop(this);
    }

    public void sendView(String screenName) {
        Log.d("Unity", "Sending Analytics view: " + screenName);
        EasyTracker.getTracker().sendView(screenName);
    }

    public void sendEvent(String category, String action, String label, Long value) {
        // value is optional
        Log.d("Unity", "Sending Analytics event: " + category + ", " + action + ", " + label + ", " + value);
        EasyTracker.getTracker().sendEvent(category, action, label, value);
    }

    public void sendTiming(String category, long intervalInMilliseconds, String name, String label) {
        // name and label are optional
        Log.d("Unity", "Sending Analytics timing: " + category + ", " + intervalInMilliseconds + ", " + name + ", " + label);
        EasyTracker.getTracker().sendTiming(category, intervalInMilliseconds, name, label);
    }
}

The calls to activityStart and activityStop are fundamental. They frame the session, allowing tracking of users, demographics and uncaught exceptions. Calling activityStart also sets the context for later sendXxxx calls to work.

The sendViewsendEvent and sendTiming methods allow manual sending of specific app information (screens, events and timings appropriately). These will be called by Unity scripts. By the way, don’t use trackXxxx methods – they’re deprecated.

There’s more stuff you can track which I haven’t covered here such as: campaigns, caught exceptions and in-app payments. See the dev guide for information on those.

You may want to know that EasyTracker is a wrapper for two other classes: GoogleAnalytics and Tracker. It makes life easier, but leaves out a few advanced features.

A note about onStop: When I pressed the Home button, my app was paused and onStop was called. When I called Application.Quit, it wasn’t. I don’t think it matters, but if it does to you feel free to read about session management.

analytics.xml

This is the configuration file for EasyTracker. Here we’ll setup our app’s Tracking ID as well as some other parameters:

<!--?xml version="1.0" encoding="utf-8" ?-->

    <!--Tracking ID-->
    UA-123456-7

    <!--Enable automatic activity tracking-->
    true

    <!--Enable automatic exception tracking-->
    true

    <!--Enable debug mode-->
    true

    <!--Set dispatch period in seconds-->
    120

Some of these parameters are self explanatory. And you can read about them and others here. A few notes though:

  • ga_debug – Enables writing of debug information to the log. The default is false (disabled). This may be useful for debugging, but be prepared to see error messages even when everything’s working smoothly (examples below.)  The output will be printed to logcat using the tag GAV2. For example, you can read both Unity’s logs and Analytics with the following command:
    adb logcat Unity:V GAV2:V *:S
  • ga_dispatchPeriod – The dispatch period in seconds. The default is 1800 (30 minutes). Yup, that’s a lot! It’s only 2 minutes on iOS. Feel free to reduce it. Setting it to 0 (zero) is useful for debugging. (But I suggest you don’t leave it that way in release versions.) Even then you can only find real-time information in the “Real-time” section in Analytics dashboard. Everything else is updated daily.
  • ga_appVersion – Your app version. No, this is not a mistake – it’s not used in the file above. No reason to! It defaults to the version found in the package, which in turn is determined by the bundle version in Unity’s player settings.

The analytics.xml file should generally be placed in the res/values directory in the project. However, the location doesn’t matter much until we copy it into the Unity project later. (It will reside in Assets/Plugins/Android/res/values.)

The Unity App

In your Unity app you’d probably want to call sendXxxx methods to track user engagement. The following Boo code calls the Java methods:

ifdef UNITY_ANDROID:
    static def Call(methodName as string, *args):
        jc = AndroidJavaClass("com.unity3d.player.UnityPlayer")
        jo = jc.GetStatic[of AndroidJavaObject]("currentActivity")
        jo.Call(methodName, *args)

    static def SendView(screenName as string):
        Call("sendView", screenName)

    static def SendEvent(category as string, action as string, label as string, value as long):
        joValue = AndroidJavaObject("java.lang.Long", value)
        Call("sendEvent", category, action, label, joValue)

    static def SendEvent(category as string, action as string, label as string):
        Call("sendEvent", category, action, label, null)

    static def SendTiming(category as string, intervalInMilliseconds as long, name as string, label as string):
        Call("sendTiming", category, intervalInMilliseconds, name, label)

The SendEvent method’s value parameter is optional. So are SendTiming method’s name and label parameters.

Adding the Plugin

To integrate the plugin we created into Unity:

  1. Copy the JAR file we created earlier to Assets/Plugins/Android.
  2. Copy libGoogleAnalyticsV2.jar to Assets/Plugins/Android.
  3. Copy analytics.xml to Assets/Plugins/Android/res/values. Create the subdirectories if needed.
  4. Edit your AndroidManifest.xml file in Assets/Plugins/Android and add the INTERNET and ACCESS_NETWORK_STATE permissions (see below).
AndroidManifest.xml

See this post for more information about this file. Here I will only mention the additions required by Google Analytics:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  .....
</manifest>

Logcat

As I mentioned earlier it make take a while before you can see results online. So how can you tell if it’s working? Assuming you enabled debug mode (“ga_debug”) you should be able to get some feedback from the logs. However, some messages are quite confusing. Here are a few examples I encountered.

False error #1: Need to call initialize() and be in fallback mode to start dispatch

W/GAV2 ( 4211): Thread[main,5,main]: Need to call initialize() and be in fallback mode to start dispatch.
I/GAV2 ( 4211): Thread[main,5,main]: ExceptionReporter created, original handler is com.unity3d.player.ab

This error may appear when you call activityStart from your onStart method. I ignore it.

False error #2: connect: bindService returned false for Intent

I/GAV2 ( 3680): Thread[GAThread,5,main]: connecting to Analytics service
I/GAV2 ( 3680): Thread[GAThread,5,main]: connect: bindService returned false for Intent { act=com.google.android.gms.analytic
s.service.START (has extras) }
W/GAV2 ( 3680): Thread[GAThread,5,main]: Service unavailable (code=1), will retry.
I/GAV2 ( 3680): Thread[GAThread,5,main]: No campaign data found.
I/GAV2 ( 3680): Thread[GAThread,5,main]: putHit called

And then some:

I/GAV2 ( 3680): Thread[Service Reconnect,5,main]: connecting to Analytics service
I/GAV2 ( 3680): Thread[Service Reconnect,5,main]: connect: bindService returned false for Intent { act=com.google.android.gms
.analytics.service.START (has extras) }
W/GAV2 ( 3680): Thread[Service Reconnect,5,main]: Service unavailable (code=1), using local store.
I/GAV2 ( 3680): Thread[Service Reconnect,5,main]: falling back to local store
I/GAV2 ( 3680): Thread[GAThread,5,main]: Sending hit to store

It appears when the Analytics service tries to dispatch the data. This may be a few seconds after the app started, or maybe later if ga_dispatchPeriod is high. Anyway, it seems to happen consistently for me. It’s fine to ignore it, assuming it’s followed by good stuff.

Good stuff

Hopefully you’ll see something like this:

V/GAV2 ( 4211): Thread[GAThread,5,main]: dispatch running...
I/GAV2 ( 4211): Thread[GAThread,5,main]: User-Agent: GoogleAnalytics/2.0 (Linux; U; Android 4.0.4; en-us; GT-P7500 Build/IMM76D)
I/GAV2 ( 4211): Host: ssl.google-analytics.com
I/GAV2 ( 4211): GET /collect?................. HTTP/1.1
V/GAV2 ( 4211): Thread[GAThread,5,main]: sent 1 of 1 hits

Hits sent? You’re good to go.

A word on legality

The Google Analytics Terms of Service prohibit sending of any personally identifiable information (PII) to Google Analytics servers. So don’t.

Sending data from Unity

Share Text + Unity

I’m pretty new to Unity-Android integration, so I’m filled with pride after every little success. And I bet this one is pretty trivial, especially for Android veterans. But I’m posting it anyway – for the sake of Unity developers. There just aren’t enough examples out there that tie the pieces together.

I wanted my Unity app to have a share function. All it needs to do is send some text as is easily done on Android. All that is missing is a tiny Android plugin.

Android Plugin

I already covered the subject of creating an Android plugin in another post. I will expand on it to keep this post short.

MainActivity.java

Again I subclassed UnityPlayerActivity. This utilizes the main activity as context for starting a new intent. I then added a small method, shareText:

package com.companyname.appname;

import android.content.Intent;

public class MainActivity extends UnityPlayerActivity {
    public void shareText(String subject, String text, String chooserTitle) {
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
        sendIntent.putExtra(Intent.EXTRA_TEXT, text);
        sendIntent.setType("text/plain");
        startActivity(Intent.createChooser(sendIntent, chooserTitle));
    }
}

shareText takes 3 string parameters:

  1. subject – The desired subject line of a message. Note that not all senders send the subject. Make sure that your text is sufficient by itself.
  2. text – The actual text message to be sent.
  3. chooserTitle – The title for the chooser dialog. It is displayed when the user is prompted to choose how to send the data. For example, it can be “Share via”.

The Unity App

Now all that is left is to call the shareText method from within Unity. This Boo method does the trick:

ifdef UNITY_ANDROID:
    static def ShareText(subject as string, text as string, chooserTitle as string):
        jc = AndroidJavaClass("com.unity3d.player.UnityPlayer")
        jo = jc.GetStatic[of AndroidJavaObject]("currentActivity")
        jo.Call("shareText", subject, text, chooserTitle)

This method simply calls the Java method as documented by Unity.

That’s the whole deal. Enjoy.

The Duplicator duplicates

Duplicator Plugin

Just popped in to say thanks to the guys at Life in the Grid for their awesome WordPress plugin, Duplicator.

I had to switch to another host as my previous one, 8Tar, was crumbling down to the ground. (I guess that’s what you get for $0…) I installed the Duplicator plugin and created a backup in seconds. The next day 8Tar exhaled its final breath. After finding a new host (paid this time) I uploaded the backup I created and activated it. It just worked. Everything was restored in one go: database and files (posts, images, plugins, permalinks, users) – the whole package.

When thanks are due…

Serverless authentication with Instagram

Instagram + Unity

In this post I will explain how to implement a Unity app for Android which allows the user to authenticate using an Instagram account – without a server (i.e., directly against Instagram’s server.) In the process we’ll also learn how to write an Android plugin.

Overview

Instagram supports client-side authentication (OAuth implicit flow, a.k.a. 2-legged OAuth). This flow enables an app to obtain a user’s OAuth credentials directly: the access token is delivered to the app via the response URI. However, the flow was designed with JavaScript applications in mind, as the app is expected to have access to the response URI (“Simply grab the access_token off the URL fragment and you’re good to go.”)  Alas, this is a challenge for a mobile app, as the app needs to relinquish control to the browser when directing the user to authenticate. How then can the app regain control and obtain the response URI with the access token?

The solution is to configure a redirect URI with a custom scheme (instead of https). An intent filter will be added to the app to handle this custom scheme. This will result in the browser directing the flow back to the app when the response URI arrives. Adding an intent filter to an Android Unity app is seemingly a simple task, but it has its share of pit falls and online examples are scarce. Full instructions ahead.

Flow

The chronological order of events in runtime (once everything is already set up and running):

  1. A user opens the app manually
  2. The user chooses to sign in. The app redirects the user to a browser to sign in on Instagram’s page
  3. Upon successful authentication Instagram redirects the browser to a URI with a custom scheme
  4. The Android OS opens the app automatically to handle this URI
  5. The plugin (Java) code is called, which parses the URI and then hands over the data to a script in the Unity project

Tools

I’m using Windows 7. The tools I used for the job:

  1. Unity Android Pro v4.1.3
  2. Android SDK v21
  3. Eclipse 4.2.1 + ADT v22

For help with installation see my previous post.

Note: The Unity scripts in this example are written in Boo. There’s about a 98% chance you’re writing in C# or UnityScript, but I’m sure you’ll find no problem understanding the scripts – they’re quite minimal.

Register Your Application with Instagram

First you will need an Instagram account. Then you should register your app.

Configure the Redirect URI using a custom scheme.
For example: mycustomscheme://oauth/callback/instagram/.
The important part is the scheme: mycustomscheme. Choose something unique.

The Website URL is not important. Choose whatever you want.

When the registration is complete you will get a Client ID. It looks something like this: c46692e6a1e57748a2b3f2b553f84c67. Keep it for later. You can access and edit this information any time.

Tip: You can manage your applications to revoke access. This might come in handy later, during debugging, in case you’ve already signed in and granted your application access but want to deny it. (Suppose you want to test the error case.)

Create an Android Plugin

The plugin will handle the response URI, extract the access token and send it to the Unity app. It will be written in Java.

Time to open Eclipse. Create a new Android Application Project. Most of the parameters are not important, such as application name, project name or Android SDK versions. They are controlled by Unity’s build settings. You should, however, pay attention to 3 things:

  1. Package name. For example: com.companyname.appname. Make sure this is the same as the Bundle Identifier in Unity’s Player Settings.
  2. Check the “Mark this project as a library” to produce a JAR file. (This can also be done later from the project properties under Android.)
  3. Activity Name. I’ll use MainActivity (the default) in this example. You will need to use this later when adding the plugin to your Unity project.

Add classes.jar to the Classpath

The location of classes.jar depends on your installation of Unity. The default location is: C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar. More info here.

  1. Open: Project => Properties => Java Build Path => Add External JARs…
  2. Locate and select classes.jar

MainActivity.java

We will subclass UnityPlayerActivity and implement onNewIntent:

package com.companyname.appname;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleAccessToken(intent);
    }

    private void handleAccessToken(Intent intent) {
        Uri uri = intent.getData();
        if (uri != null && uri.toString().startsWith("mycustomscheme")) {
            String accessToken = "";
            if (uri.getFragment() != null) {
                accessToken = uri.getFragment().replace("access_token=", "");
                Log.d("Unity", "Found access token: " + accessToken);
            } else {
                Log.d("Unity", "Access token not found. URI: " + uri.toString());
            }
            UnityPlayer.UnitySendMessage("AccessManager", "OnAccessToken", accessToken);
        }
    }
}

Our new activity extends the standard UnityPlayerActivity class. The onNewIntent method will be called when the activity is re-launched. If a URI is found, it is parsed and the access token is extracted. UnityPlayer.UnitySendMessage sends the token to a script inside Unity, where AccessManager is the name of the game object and OnAccessToken is the name of the method. We’ll later create them in Unity.

That’s it. You should find an output JAR file in the project’s bin folder. We’ll copy it later to the Unity project.

Tip: When writing log output (Log.d) I used “Unity” for the tag. This is the same tag that Unity uses for Debug.Log. So when I use logcat to read the log output I can filter easily:

adb logcat Unity:V *:S

Error Handling

The response URI for a successful sign-in looks like this:

mycustomscheme://oauth/callback/instagram/#access_token=316140977.f598def.cf36764c5c6f4488fa95362592f14f91

For a failure it looks something like this:

mycustomscheme://oauth/callback/instagram/?error_reason=user_denied&error=access_denied&error_description=The+user+denied+your+request.

When the code above detects there is no fragment, it passes an empty string instead of an access token.

The Unity App

The Unity app needs to open the sign-in URL when the user chooses to sign in. And it needs to receive the access token from the plugin we wrote earlier.

Open Sign-In URL in Browser

All you need to do is open this link:

https://instagram.com/oauth/authorize/?client_id=c46692e6a1e57748a2b3f2b553f84c67&redirect_uri=mycustomscheme%3A%2F%2Foauth%2Fcallback%2Finstagram%2F&response_type=token
  • Fill in the Client ID you received from Instagram.
  • Use the Redirect URI you configured earlier. Replace the colon with “%3A” and all the slashes with “%2F” as above.

Notice the last parameter: response_type=token. This instructs Instagram to use the implicit flow and return the access token in the response URI.

The following Boo code simply formats the link string and calls Application.OpenURL:

SIGN_IN_URL_FORMAT = "https://instagram.com/oauth/authorize/?client_id={0}&redirect_uri={1}&response_type=token"
CLIENT_ID = "c46692e6a1e57748a2b3f2b553f84c67"
SIGN_IN_REDIRECT_URL = "mycustomscheme%3A%2F%2Foauth%2Fcallback%2Finstagram%2F"

signInUrl = SIGN_IN_URL % (CLIENT_ID, SIGN_IN_REDIRECT_URL)
Application.OpenURL(signInUrl)

Handling the Response

Create a game object named AccessManager and attach a script to it with a method named OnAccessToken. Make sure to use the same game object and method names as in the Java plugin code (UnityPlayer.UnitySendMessage). The method should have one parameter of type string, which will receive the access token. You can then use it to access Instagram API. (You may also want to store it using PlayerPrefs.)

Example code in Boo:

def OnAccessToken(accessToken as string):
    Debug.Log("Received access token: " + accessToken)
    PlayerPrefs.SetString("access_token", accessToken)
    // do something useful here

Adding the Plugin

The last part is to add the plugin we created earlier to the Unity project:

  1. Create a subdirectory Assets/Plugins/Android in your Unity project directory
  2. Copy the JAR file we created earlier to Assets/Plugins/Android.
  3. Create an AndroidManifest.xml file in Assets/Plugins/Android.
AndroidManifest.xml

Here we define our activity, MainActivity, as the main activity and also add an intent filter:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">
    <activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singleTask" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="sensor">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <intent-filter>
        <data android:scheme="mycustomscheme" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
      </intent-filter>
    </activity>
  </application>
</manifest>

Use the same names you used in the Java plugin:

  • Activity (.MainActivity). Mind the prepending dot!
  • Custom scheme (mycustomscheme)

There is no need to specify the package name (com.companyname.appname) here. It is determined by Unity’s Player Settings (“Bundle Identifier”).

Notice the parameter android:launchMode=”singleTask”. This is the only mode that worked for me.

During a build Unity will extend this file and merge a few bits into it, such as the bundle identifier (package name), Android versions and permissions. The extended file, with the same name, can be found in the Temp/StagingArea subdirectory, which is deleted when Unity is closed.

That’s it! Should be working now.

Testing the Custom Scheme URI

If you open a browser on your mobile and try to open your URI (e.g. mycustomscheme://oauth/callback/instagram/) you will most likely find yourself in a Google search results page. That’s just how the browser works. To manually open your URI you’ll have to find a creative way to open a URI directly. For example:

  • Create a simple HTML that redirects to your URI, copy (email?) it to the mobile device and open it with some HTML viewer
  • Create a redirecting HTML and put it on your server, if you have one
  • Open the URI using your Unity app using Application.OpenURL (haven’t tried this one myself)

What’s Next?

There are two issue worth mentioning. I haven’t finished investigating them and tinkering with them yet, but I’ll say a few words.

UnityPlayerNativeActivity and UnityPlayerProxyActivity

In this solution we subclassed UnityPlayerActivity. We ignored two other (important?) classes, UnityPlayerNativeActivity and UnityPlayerProxyActivity, which were introduced in Gingerbread.

  • UnityPlayerNativeActivity – Parallel to UnityPlayerActivity. Can also be subclassed.
  • UnityPlayerProxyActivity – The top activity; it determines which of the other two activities (regular or native) should be started.

It is possible that ignoring the native player results in reduced performance in some cases.

WebView

I managed to open a WebView inside Unity using gree’s plugin. I will soon try to use a WebView for signing in instead of sending the user to the browser.

Edit: I’ve tried using WebView and it didn’t work. It would not delegate handling of a custom scheme. Instead it shows an error. I assume this behavior could be overridden if I had a way to set my own WebViewClient and override shouldOverrideUrlLoading.

iOS

See you then.

Enjoy!

Building Unity for Android with Eclipse

Eclipse + Unity

While working on a Unity project for Android I found the need to move the build process to Eclipse. This process used to be complicated and tricky, but thankfully it became rather simple with Unity 4.

Note: The instructions below relate to Windows 7. YMMV.

Before we begin, you’ll need to install two tools:

  1. Android SDK
  2. Eclipse + ADT Plugin

You can download them both here. They’re bundled together, named “ADT Bundle”. In the same page you can click “Use an existing IDE” to download only the SDK.

Android SDK

If you’re not familiar with the Android SDK, you should know that it consists of multiple components. After you install it you can manage which components are installed using the SDK Manager. The components that you must have are SDK Tools (installed by default) and at least one SDK Platform. I decided to go with Android 4.0.3 (API 15). Choose whatever suits you. Once you choose your platform, you only have to install “SDK Platform”. The rest is optional. (You may want “ARM EABI v7a System Image” to use the emulator.)

I also recommended installing whatever the SDK Manager suggest under Tools and Extras, namely: SDK Platform-tools, SDK Build-tools, Android Support Library and Google USB Driver.

Invokation Failed

I’ve encountered a bug with version 22 of the SDK. Unity could not build with it. I’d get the following error:

Edit: This bug seems to have been solved with version 22.0.1 of Android SDK.

At least this is the case with version 4.1.2f1 of Unity. If you have this problem, try downgrading your Android SDK version: Uninstall version 22 and then install version 21 (available here). Strangely enough, after you install version 21 you can let the SDK Manager upgrade “Android SDK Tools” (under “Tools”) to rev. 22 and Unity will still be able to work with it. Voodoo.

After you downgrade to rev. 21 and you’ll be seeing this error each time you open Eclipse:

android_sdk_tools_22_required

Don’t worry about it. Just hit Close.

Eclipse & ADT Plugin

If you’ve already built stuff for Android using Unity then you probably already have the SDK installed. You could install Eclipse for Mobile separately and then the ADT Plugin. But you can also download the ADT Bundle and take from it only the “eclipse” directory. It’s a download overkill (>400MB), but it’s easier to install.

Anyway, all Eclipse installations are simply ZIP files. There is no installer. Extract the ZIP file contents to wherever you want, e.g. “C:\eclipse”.

Export Eclipse Project from Unity

It’s time to open Unity. Open the build settings for Android and you’ll see a check box “Create Eclipse project”. Selecting it directs Unity to create an Eclipse project instead of building an APK file. When asked, choose a target directory for the Eclipse project.

Import Project into Eclipse

Open Eclipse. Choose “File => Import”. Under “Android” choose “Existing Android Code Into Workspace”. Fill the “Root Directory” field with the path of the project directory and hit Enter. You should see your project in the list. You might see more than one project if you’re using plugins. Deselect the plugins, you don’t need to import them – they’re included in the main project. Click “Finish”. That’s it. Your project is ready to be built.

Windows tech tip of the week #1

Open File dialog box

Hi.

It’s been a long time since I posted. Sorry about that  😉

It took me a while to figure out what I want to write here in my blog. I started the blog thinking I’d write about technical stuff. Maybe software development, or game design. It was right after I stopped working on Swat and I had the burning feeling that I needed to help the world by warning it about the dangers of using ActionScript. I wanted to tell the world about it’s rounding bug when changing an object’s alpha property. I wanted to rant about how animations started playing just because I had the audacity to pre-load them. And how I got around it by muting the speaker. I was young and infused with a sense of mission. But, alas, my passion faded away. At first it was simply too painful, the wounds still fresh. I always found excuses not to do it. After some time I was thinking: there are some good solutions out there, though few and scattered. Shall I point them out? Fill out the blog a little with a few good pointers? Nah… that’s not very interesting. And then, after a while, I just thought about Flash less and less, like most of the world, I presume.

So what shall I write about? Maybe some stupid hello-world-first-post-in-the-blog thingie? (No, that’s boring, right?)  I could just write about what I’m developing – about Windows Phone 8. That’s annoying enough! Maybe I’ll share my frustrating experiences with simply trying to install the damn development platform? I only laid my hands on it and already had several new warnings to share with the world. Should I start my blog with a good, long rant? I do love to rant.

Nay. I’ll have enough time for that later! Here’s a useful little tip instead.

Windows tech tip of the week #1: If you’re using Windows, this might help you save some time whenever you need to attach a file.

Know that annoying “open file” dialog box? Well, it’s annoying if you’ve already got the directory open in front of your eyes. You can see the file, but you can’t simply drag and drop it, now can you? What you can do is copy the file (ctrl+c) and then in the dialog box in the file name field paste it (ctrl+v). It will paste the file *name*, complete with the path. Hit OK and get back to your life.

You can also copy the file path by holding down the Shift key and right-clicking the file. The Shift key miraculously add the “Copy as path” option to the pop-up menu.

Oh, and speaking of Windows 8, don’t install it on your laptop just yet if you’re a keyboard man like me.

peace out