Monogame – Working with Touch

*NOTE – This may also work on other platforms with touch as well, but this hasn’t been tested on anything other than Android at the current moment.

One fundamental aspect to understand about touch and gestures is that code needs structured around the idea of continuous input. Thus, any touch recognition code should be encapsulated somewhere within the main game loop; where this is would be dependent upon how one elects to parse input. The reason for this is that the nature of all touch processing is a sequence of touch events that get fired in succession; there are only a few exceptions to this rule.

The other aspect to understand clearly is nomenclature mapping. What you think you want to achieve in your design may, unless you’re already familiar with either the Monogame or Xamarin frameworks, not necessarily be what either of those aforementioned frameworks are calling it through its API parlance. So be prepared to step outside your normal wheelhouse and, perhaps, discard some long-standing assertions about what truly a touch/gesture really is.

First, the consideration needs made as to if you’re going to handle touches or gestures – don’t get too caught up on thinking that a touch is a single press, because this will cause you some unnecessary grief later. If your program ebbs toward the former, there’s usually no requisite configuration needed, other than to check for the existence of a touch-capable input device and adjust your engine accordingly (the only other exception here would be if one wishes to perform touch-to-cursor mappings, but this is outside the immediate scope of this article). Conversely, gesture handling requires some configurations be made before they can be used, and this is where the Monogame documentation, as well as a majority of the information available on various forums, falls fatally short. We will focus on this once we start addressing gesture handling, which follows a look at touch handling.

Checking for the existence of hardware with touch capabilities can be queried through the TouchPanelCapabilities object, triaged through the TouchPanel static object as such:

TouchPanel.GetCapabilities ().IsConnected;

The IsConnected property is a boolean that indicates the presence of a touch-capable device. Obviously, the absence of one suggests either that the device is intermittently available, or that other sources of input are necessary. In this case, neither the touch nor gesture APIs would be valid.

Concerning touches, the current state of the touch panel holds a collection, called TouchCollection, of TouchLocation objects. Each of these correspond to a single touch event generated by the user when a press is determined to have occurred on the sensitive surface. It’s fair to suggest that multiple points of contact yield multiple TouchLocation instances within the TouchCollection (i.e. as a practical example, one finger would generate one instance, two fingers would generate two instances, etc. This can best be observed while debugging a program using this technique, and paying attention to the Id property of the TouchLocation instance(s).). In order to properly ascertain the most accurate state of the touch panel, we’ll need to do the following:

  1. Obtain the current state of the touch panel
  2. Determine if there are any TouchLocation instances in the TouchCollection collection
  3. If there are, take relative action based on any of the properties of the current TouchLocation instance

This can be accomplished using the following general template

TouchCollection tc = TouchPanel.GetState ();
foreach (TouchLocation tl in tc) {
 // Do something here
}

Fortunately, TouchLocation instances are very simple to use. There are only four properties that are of any practical significance, and one utility function for trying to determine the previous location (which, at least during my testing, wasn’t useful for anything). They are as follows:

  • Id – An Integer that uniquely identifies a single TouchLocation instance within a TouchCollection. It’s unknown how its potential values are determined, or if orphaned values are subject to recycling after the host TouchLocation has fallen out of scope.
  • Position – A Vector2 that provides the X and Y coordinate serving as the source for the event. Effectively, this is the location on the sensitive surface where contact was made.
  • Pressure – A floating-point number indicating the pressure of the touch. I’m unclear on how exactly this works, since my tests always reported a zero in this property. The only conclusions I can come up with here are either that my device doesn’t support touch sensitivity of this kind, or I missed a configuration step to enable this functionality.
  • State – An instance of TouchLocationState that determines what kind of touch we’re dealing with. This property can have one of four values:
    • Invalid – The press has been somehow deemed invalid; I’ve never seen this occur in testing, and am left to think that either I failed to satisfy conditions to make this occur, or that it’s simply a dump case for an exceptional condition.
    • Moved – Seen when a press has either remained in the same position or has moved. This is a very important state as it has a great deal of practical application, so do try to keep it in mind.
    • Pressed – Observed when a point of contact on the sensitive surface is newly minted. This is only fired once, and will not be seen again, regardless if the contact remains afterward. This could potentially be a source of confusion for a number of programmers.
    • Released – Likely the last state that a TouchLocation would be in before falling out of scope, it will be fired when the source of contact which generated this particular instance is no longer available.

Having said all of that, you now have enough information to properly utilise touches within your programs. As was stated before, simply ensure that your code is contained within the main game loop somewhere, since the data will likely change per frame, or at least as often as it can. An example of how to implement code based off this logic will be illustrated at the end of this discussion.

Gestures, insofar as Monogame is concerned, are a bit of a hassle to work with, mostly due to the lack of upstream documentation. We will seek to correct this imbalance, if not for the official documentation, then at least for programmers who wish to learn more about them.

As was stated previously, although Monogame performs a considerable amount of work to make available the touch and gesture API for programs deployed on capable hardware, gestures are left a bit open-ended by comparison to their touch counterparts. What this means is that as a programmer, you’re required to provide some configuration before the gesture API can be utilised. Here, we assume that you’re only interested in gestures, and not touch-to-cursor mappings, hence will only discuss the former.

Before proceeding, some basic information should be given as to the nature of gestures, and how they’re procedurally handled.

Although there is some very minor overlap with how a touch and gesture are expressed, they are two discrete entities. Gestures can be composed of one or more points of contact, and it’s expected that the locations of these contacts will change, in a particular way, over an indeterminate amount of time (ergonomic measurements would likely dispute this generalisation, but it is for this conversation a generalisation, and not a scientific claim). The ways in which these contacts change, or at least the resultant shape the change yields, as well as the number of contacts involved in the measurement, hints at the particular kind of gesture. In other words, a single point of contact that has a gradual change in its X-axis, be it positive or negative, which would yield a ray (or a series of small rays), is generally considered to be a horizontal drag gesture. When considering the same principles but looking to the Y-axis instead, we now find ourselves dealing with a vertical drag gesture. Pinch and zoom gestures typically involve two or more points of contact that move near concertedly away from or toward each other along the same logical axis. Perhaps paradoxically, at least when considering a contrast between touches and gestures, taps, double taps, and long-presses are registered as gestures as well; these are more concerned with the sustainment of a single point of contact relative to the time from when it was first recognised.

From a stock perspective, Monogame provides eleven types of gestures, referred to as GestureTypes. These types will effectively determine how gesture detection is performed (it’s unclear if the GestureType framework can be extended to facilitate custom gesture types, but this is a considerably advanced topic which will not be discussed here). However, Monogame will not automatically read the touch panel for gestural input. Instead, it needs instructed on which kinds of gestures to detect, and this is provided by the programmer. In any non-looping block of code, preferably during the initialisation routines, you’ll need to specify what are called EnabledGestures, which is a property of the TouchPanel static object. Multiple gestures can be configured by OR’ing one or more of the types together in the assignment statement. For example, if I wanted to parse for both a HorizontalDrag and a DragComplete gesture, I would write the following statement:

TouchPanel.EnabledGestures = GestureType.HorizontalDrag | GestureType.DragComplete;

Once this is complete, you’ll have done enough to get Monogame to start playing nice with at least these two kinds.

Parsing gestural input is, in essence, no different than parsing touch input, but there are some minor differences to the process. To start, we must first determine if there are any gestures with which to read data from. If we do not do this, attempts to read directly from the gesture store will generate fatal exceptions. Fortunately, the TouchPanel static object provides a boolean property called IsGestureAvailable, which will inform clients of the availability of queued gesture data. If we have data, we must convert the data into a sample, which is packaged into the GestureSample class. As with the TouchLocation object, the GestureSample object contains several properties that are of practical interest to the programmer, especially when making contextual decisions that respond to this kind of input. GestureSamples include the following properties:

  • Delta – A Vector2 instance which provides the delta, or difference, data for the first touch point in the gesture. This will change over time, and will always be relative to the coordinates where the touch was first recognised.
  • Position – A Vector2 instance which contains the current coordinates of the first touch point in the gesture.
  • Timestamp – A TimeSpan instance that indicates the time when the gesture was first recognised.
  • GestureType – A GestureType instance that indicates what type of gesture was determined based off several criteria.

Additionally, GestureSample contains properties called Delta2 and Position2, which are used to track a second point of contact that’s being measured as part of the current gesture. What this implies is that insofar as the stock gestures are concerned, Monogame will only be able to handle gestures where no more than two points of contact are involved.

My advice here is to experiment with the data through debugging until you’re comfortable with how these gestures are read, because there are some nuances with how the data continuously polls respective to different gesture kinds. For example, a HorizontalDrag gesture will, while the drag is occurring, constantly emit the HorizontalDrag signal until the contact source is released, terminating the gesture. At this point, if one is checking for the DragComplete signal as well, releasing the contact source will cause the touch panel to emit the DragComplete signal.

Examples:

To determine if a single press has been made:

TouchCollection tc = TouchPanel.GetState ();
foreach (TouchLocation tl in tc) {
 if (TouchLocationState.Pressed == tl.State) {
  // Execute your domain-specific code here
 }
}

To determine if a press was made, and has been held in the same position for an arbitrary period of time:

TouchCollection tc = TouchPanel.GetState ();
foreach (TouchLocation tl in tc) {
 if (TouchLocationState.Moved == tl.State) {
  // Execute your domain-specific code here
 }
}

To track the position of a horizontal drag:

(1) During game initialisation:

TouchPanel.EnabledGestures = GestureType.HorizontalDrag;

(2) During game loop:

while (TouchPanel.IsGestureAvailable) {
 GestureSample gs = TouchPanel.ReadGesture ();
 if (GestureType.HorizontalDrag == gs.GestureType) {
  // Execute your domain-specific code here
 }
}
Advertisements

Configuring Apache Cordova with JetBrains WebStorm

Stepping back into the mobile arena after a bit of an absence, I decided to take a bit of advice from a gentleman who sat in on my 2014 Ohio Linux Fest lecture “Android Development on Linux” (generously, and anonymously, curated on the Internet Archive) and look not just at Android but at cross platform. For the longest time, this has always been a topic of considerable consternation. One has to think, upon reflection of the history of technology, that we’ve experienced in some ways a regression to the days when cross platform littered the landscape and was, in certain respects, paralyzing. Fortunately for myself and others, we weren’t the only ones who recognized this. Some have taken action to help ensure that we do have a shim solution for these cases, and this is where Cordova enters the fray. Being derived from PhoneGap and adopted by Apache, Cordova attempts to bring to mobile developers the ability to write a program using web languages such as HTML, CSS, and JavaScript and deploy it to multiple platforms including but not limited to Android, iOS, and Windows Phone.

As I mentioned before, the focus this time around is on cross platform development. I had a little taste of PhoneGap a few years ago and it never really stuck but there seems to be a little community acquiescence toward Cordova. Not to say that I’m following the grain too much here because there is a genuine personal interest in the framework on my part, but I want to be able to help people as well.

Now, the development environment that I use is detailed here. As you should be aware, you may want to keep this in mind proceeding forward.

  • OS: Linux
    • Distribution: Ubuntu 15.10
    • Only the default upstream repositories are used in dpkg
  • Arch: 64
  • IDE: JetBrains WebStorm (I have a license for this; the unlicensed version only works for thirty days).

As the title indicates, this tutorial is specific to configuring WebStorm to be used with Cordova. As such, there will be a lot of WebStorm-specific information here that, if you were using an alternative IDE, may not be applicable. You’ll want to follow your IDE’s integration instructions or deduce it on your own at that rate.

Prerequisite – NodeJS/NPM

I’m going to be frank here – there is very little that I know about NodeJS other than what it is, that it’s been the subject of quite a lot of hype in the web development community since its conception, and that it is a requirement on behalf of Cordova. While there is still quite a bit of homework that needs to be done on my part, I have been able to successfully install and configure it so that it works for the purposes of Cordova. This will be the topic that is covered here.

Node can either be installed by downloading it in a pre-compiled package from the Node website or through your distribution’s default repositories. If you elect to download from the Node website, you’ll need to be responsible for manual maintenance of the package and for extracting it to a location that you have access to via your permissions or ACLs (if your file system supports them). The method I used was installing was the former since updates are automatic and installation is performed in the appropriate directories. This can be done with the following command

sudo apt-get install nodejs

Once this command completes, you’ll have Node installed on your computer. To test this, you’ll want to start a Node interpreter by issuing the command nodejs at your terminal. If you’re brought to a new prompt lacking the traditional user and host name information, Node is all set to go. You’ll need to press Ctrl+C twice to end the program and return to your traditional prompt.

NPM, a pseudo package manager for JavaScript libraries, is required since this is the preferred method for installing Cordova. It will also help with obtaining Cordova plugins and other JavaScript libraries that you might want to use later on for development. It can be installed in the same was as Node was with the distribution’s package manager.

sudo apt-get install npm

To test the installation, you can simply issue npm at the terminal. If you get back a page of text indicating the usage syntax, NPM has been installed successfully.

Installing Cordova

When you’re using WebStorm, it’s apparently possible to install Cordova entirely through the IDE once you have configured it to locate both Node and NPM. These steps, however, are a little convoluted to follow, especially with all of the potential pitfalls you’re going to encounter, so we’re going to avoid this entirely and install Cordova with NPM on the terminal.

NPM has two install modes: local and global. A local installation will create a node_modules directory in the working directory where the command was issued at and install the module there. The global installation places all of the modules in a consolidated directory and makes that available to the system through environment variables; Cordova is best installed in global mode (recommended by the official install documentation). The installation can be performed with a single NPM command

sudo npm install -g cordova

The installation, when in the global scope, will need to be run as root hence the use of sudo.

Caveats… Already

To test Cordova, you’d do it in the same way that you would with both Node and NPM. However when you type cordova into the terminal and press enter, you will most likely, but certainly not always, get in response an error that may look like this:

/usr/bin/env: node: not found

The issue here is that Cordova is looking for the Node binary with a specific identifier, node. However when Node is installed through the package manager, the identifier of the binary is nodejs. Despite there being several tutorials on the Internet offering advice such as aliasing nodejs as node in your .bashrc file, the solution that needs implemented here is to create a symlink in /usr/bin named node that links back to nodejs. So what your directory tree looks like then is similar to this

cordova-node-usrbin-dirstructure

If the highlighting gets in the way, what you should be paying attention to here is that the bottom highlighted line shows the actual Node binary, nodejs, and the top line shows the node symlink which points straight to the nodejs binary. They’re both in the same directory but Cordova is looking specifically for the node file. I’m unsure if this can be configured in Cordova in some way so if someone knows, please share how to do this. Either way, once this symlink exists in /usr/bin, you should be able to then issue cordova from the terminal and get syntax help printed out to the terminal. If this happens, Cordova can see Node and is ready to go.

Prerequisite – Platform SDKs

As great as Cordova may be, it needs the platform SDKs in order to build for each one specifically. Obviously, while capable of bridging the gap between platforms, you still need the platforms themselves to actually accomplish anything. The good thing is that the Cordova wiki hosts a plethora of information relative to acquiring the corresponding SDKs. Being on a Linux system, you can install without a serious amount of labor the Android, BlackBerry and Ubuntu SDKs. For the sake of this tutorial, we’re only going to be focusing on the Android SDK. If there is further interest in setting up any of the other SDKs, I’ll create them later.

Downloading and installing the Android SDK should be a relatively straightforward process at this stage. I’m going to assume that you either know how to do this or can follow the instructions outlined on the Cordova wiki. Post installation, you’ll want to ensure that you have added an environment variable called ANDROID_HOME and included it in your PATH environment variable that points to the root directory of the SDK; again assuming that you know how to set persistent environment variables on your Linux computer..

As a secondary caveat, if you’re starting WebStorm from a desktop link or a link in the Unity Launcher, there is a bit of a catch in that the invocation context will be such that the program won’t notice the user-modified PATH variable that contains the ANDROID_HOME variable. What this means is that the IDE won’t be able to see the location of the Android SDK (I’m assuming that this would be the case for other SDKs as well). The way to fix this is to modify the EXEC field in the file to preface the issuing command with bash -ic.

cordova-node-wsdesktopstart-mod

Keep in mind that traditional desktop icons are found in ~/.local/share/applications while Unity Launcher icons are located in /usr/share/applications.

So long as all of these conditions are met, you should be able then to start WebStorm and create a Cordova project. Let’s step through that process next.

WebStorm – Creating a Cordova Project

webstorm-cordova-launch001

As you can tell, I’ve been at this for quite some time.

The first thing you’ll want to do is click on Configure at the bottom-right and then select Settings which will be the first item in the subsequent menu.

webstorm-cordova-launch002

First you’ll want to examine to ensure that WebStorm knows the location of Node. It’s highly unlikely that it has automatically determined its location so you may have to set it. Keeping in mind the location of the Node binary that was installed through your system’s package manager, you’ll want to get this fully qualified path name, along with the binary, into the Node interpreter field. Code Assistance may not be enabled for you by default. Frankly I’m unsure what this feature is but I have it enabled because… reasons.

webstorm-cordova-launch003

Next you’ll want to instruct WebStorm as to where to find the Cordova binary. Again, there’s a very high chance that it’s not automatically detected by your installation so you’ll have to manually specify. As we did with specifying the Node installation directory, you’ll need to do that here in the first field labeled PhoneGap/Cordova executable. Note that WebStorm still retains the PhoneGap label for all things Cordova even though Cordova has absorbed PhoneGap. Once the installation directory has been specified and WebStorm sees the Cordova binary, the PhoneGap/Cordova version field should automatically populate. The third field, PhoneGap/Cordova working directory shouldn’t be filled out at this point; ignore my entry here. This field is specific to your current project. This also explains the error that you’re seeing at the bottom of the window in the screen shot.

Once those are set, you can click OK and go back to the WebStorm Greeting Window. Here, you can click on Create New Project.

webstorm-cordova-launch004

Once the New Project dialog appears, you’ll select PhoneGap/Cordova App on the left side and then fill out the Location field. The PhoneGap/Cordova field is simply the location of the Cordova binary; we set this previously in the global settings. Click Create and WebStorm should take care of the work to generate the files necessary for your project.

webstorm-cordova-launch005

Now so long as the steps above were followed to the letter, you shouldn’t have any errors, other than something simply blowing up, that get thrown from WebStorm. Now you can start working on your program.

Deployment

webstorm-cordova-launch006

Testing your Cordova program can be done by creating multiple Run Configurations. Each of these would be distinguished by the value in the Name field. The value in the Command field will determine if Cordova will attempt to delegate to the emulator for the target platform, and the value in the Platform field will determine which platform is being targeted with this Run Configuration. I prefer to test on actual hardware, unless I’m constrained by the lack of, so for Android deployments I’ll deploy strictly to the device.

This should be enough to get you started. You can always read more on the Cordova Wiki to get a primer on the Cordova specifics. Otherwise you can start hacking away using HTML, CSS, and JavaScript

Projekt Vagabond Isn’t Dead. I Swear.

I’m starting to get back to a point where I can start working on this thing again and it’s like flipping through a kindergarten yearbook. Every now and then I’ll find something that makes me think I had a stroke of genius. Other times it’s like seeing a photo of that one kid you hated more than bees. Like you’d prefer taking nails hammered into your ears than listen to that twat for a single second more than you had to.

Tonight I had more of the latter instead of the former.

This thing has taken several forms starting out as a PoC Bash Script that was around sometime shortly before Ohio Linux Fest 2014 to a full-blown 15,000+ line C++ program which would have worked but I didn’t realize how insanely asinine packaging software has to be (I don’t have that kind of time, especially these days). But then I had the bright idea to simply make a Vagrant Box (I was already using Vagrant in the backend for handling a lot of things) and just distribute that instead of all this rigmarole. Funny thing here is that there are still quite a few snags.

As I’m typing this, the box is uploading to a cloud store that I’ll make available to the public tomorrow. I ran into some problems during this process.

  • I wanted to be a somewhat normal person and upload the Box to Atlas, HashiCorps repository of Base Boxes. I thought that would have been a great way to make this available to people. But nope! I don’t know if there’s a size restriction on the Box size or what but it just wouldn’t take it. FYI – the Box is about 780MB in size. The goal here is that someone would have simply been able to issue “vagrant up gregfmartin/vagabond” and get the VM. Man that would have been nice…
  • Google pisses me off to no end these days. Tonight was no exception. It still blows my mind that I can’t update or configure an Android SDK Installation from the terminal without either (A) getting bitched at for some ridiculous reason or (B) having to press ‘yes’ to accept fifty licenses for these libraries instead of just being able to use an option that will opt-in to any license requests that would come up. The former makes it literally impossible to automate an installation. Some people on GitHub have described a work around for this but it’s really hacky and I’m a little concerned about the platform portability of solutions like those so I’m avoiding them like the flu.
  • This all lead to my idea of just making the Box and distributing the Box that I’ve manually tweaked. This has issues in and of itself in that (A) the bundled software is static unless the user wants to manually update or (B) wait for me to to update and republish an updated Box (which I REALLY don’t want to do if I’m being honest). This leads into supplementary tutorial material that will be on the project’s website.

In case anyone is wondering, the reason why the Box is so huge is because it contains a fully updated Ubuntu 14.02 64-bit base image, required prerequisite software to use the Android SDK and associate tools/IDE, the Android SDK installation with all 5.0.1 components as well as all Support Libraries that are compatible with Linux (important to note), and the recent version of IntelliJ Community Edition. So yeah, it’s a little fat. That’s the size it would be on your disk anyway.

Tomorrow I’ll get all of the stuff up on the website like documentation and how to do things with it and what you can expect by using it as well.

Something tells me I’m going to have to make some changes to this before too long. 🙂

Vagabond 1.0 – Nearly Here

Yes that’s right. After much toil, and missing my self-imposed deadline for a release, I’m nearly complete with the program and can give it a 1.0 which means that it’ll go into General Availability.

I’m not posting Vagabond News on its website because I want that strictly to be a place to get the program from and a reference for information that developers would be interested in. Just wanted to get that out of the way as well. 🙂

Right now the program is about 98% done. All that remains is to test two additional features, push to the repository (assuming they work, which they will) and then build Debian, RPM and Source Taballs for deliverables. I haven’t built Debian packages for quite some time so I’m having to go through several refreshers to bring myself back up to speed on the workflow.

After this release, I have some additional features I want to work in so development will continue alongside general maintenance coding. I want to be able to add support for choosing to use either the official Android SDK or to use the Replicant SDK. This is more of an ethical decision as I feel like Replicant is a great choice for developing in an open-source manner but might not be the best idea if you’re writing Android to be published on Play. But the option should be there. I’m also considering the idea of implementing SDK Administrative Task Shortcuts. These are commands that can be passed to Vagabond that will inflate to more robust tasks passed to the VM that maintain the SDK.

If all goes as planned, and work and life leave me alone long enough, I can potentially have this ready to go by the end of the week.

Another Vagabond Update

I wanted to take some time out from working on it to provide you with an update to the Vagabond project.

Recently I had some piqued interest on Twitter for the project and it’s lit a little more of a fire under my arse to get it going. I’d continued to work on it but I was putting in on the back-burner in favor of other projects that I’ve been working on. It’s safe to say that I’ve got enough interest now to get it done.

If you need another synopsis on it, please check out my previous post about what Vagabond is.

Changes

My PoC code was a shell script that did the whole thing for me. I’ve since migrated the project to C++ and am going to build both Debian and RedHat Packages and also provide a source tarball. After some consideration, I may actually take this and eventually migrate to a web-interface in Perl but I’m still not too sure yet. I’ve got too much work going into the C++ conversion that I need to stick with that and see where that takes me. Additionally, I’ve put the development branch up on my GitHub. The project is under the BSD 3-Clause License so you can do what you want with it so long as it carries the license and you don’t use my name on derived works without pinging me first.

Right now the project is sitting at about 3011 lines of code (tallying both insertions and deletions but the graph is laid out on the GitHub). This may seem inflated but I’m adhering to the 80-line gutter since I’m editing exclusively in vim and I hate the word wrap on there. Rightly this could be consolidated and I’d probably manage to shave off a hundred or so lines. A compilation is possible as it is now and the help feature is 100% complete. The create option is responsive to tests but doesn’t do anything useful at this point and the Vagrant Propagation command isn’t coded yet. Most of the code might seem redundant compared to the PoC but I wanted to use some of the more robust features of C++ like Exceptions and Namespacing for proper encapsulation.

I’m still looking to be able to have this done by the end of next week. At least a rev one. If anything comes up that keeps that from happening, I’ll change the date but I’m pouring nearly all of my free time into the project so I should be able to hit it.

Vagabond – Update

Aside from the getandroidsdkdl script that I was offering for people who wanted to get started with Android Development on Linux, another cool tool I wanted to throw out there was a script I call “Vagabond” which is a Vagrant deployable which automatically configures a development environment that you can use for Android Development on your Linux computer. In fact, it may prove far easier to use it instead of the getandroidsdkdl script (it actually builds off of it). The actual Vagabond script isn’t available yet but I’m letting a rather close circle of friends test it out first before I make it public which shouldn’t be too far off from now. I wanted to give an overview of what it does.

If you’re unfamiliar with Vagrant, follow the previous link to get a better understanding of what it is.

What is Vagabond?

Vagabond is little more than a bash script that you’ll download and execute. Once you execute it, all you’ll need to do is sit back and relax while it configures the VM, installs all of the requisite packages to both update the base VM and the required packages for the Android SDK, and then exposes the synced directory from the guest to the host via host’s PATH and that should take care of everything. The user can then install an IDE of their choice on the host and run the Android SDK tools from their host computer.

Got that? To further simplify, the purpose of the Vagrant image is to host and isolate the the Android SDK and to make it even more portable since it’ll exist exclusively in a VM. The Vagrant Provisioner Script, in tandem with the Vagabond Script, exposes the SDK hosted in the guest VM to the host machine. If it still doesn’t make any sense, you may need to read up on what Vagrant is so that it sinks in a little more.

Vagabond Usage

Vagabond is, and will continue to be, stupid simple to use. I’m planning on adding some additional features to it but for right now, it only requires that you provide it with a directory where you want Vagrant to install and configure the VM in. That’s it. The rest is up to the script.

vagabond.sh [vagrant-project-directory]

Vagabond Details

Vagabond relies on three different pieces of software – Vagrant, Git and Zenity. It will check for the existence of these three on the computer before attempting to do anything. If either one of these are missing from the computer, the script will fail and return an error code specific to whichever program was missing. These codes are detailed in the comments in the script.

Clearly, the reliance on Vagrant should be rather obvious. Git needs to be installed since Vagabond will pull down the Vagrantfile and bootstrap provisioning script from my GitHub. Zenity is used to display the output from Vagrant while it’s running the VM configuration and provisioning script. The choice to use Zenity was made since the output from the provisioning script couldn’t easily be piped back to the terminal that was running Vagabond. I’m still not really sure why this was the case but that’s what was happening. If I can figure it out, or if someone a bit more skilled at bash scripting than myself modifies the source, I’ll yank the Zenity dependency out of there. I can’t say I don’t entirely dislike it though.

Vagabond then attempts to make the directory that the user specified while launching the script. No real magic here. The only thing is that it will check to see if the directory exists prior to running mkdir. Some may see it as a redundant check since mkdir will cater to already existing directories but I wanted some conditionally verbose output. Sue me.

Next Vagabond will move into the new directory and use Git to clone from my GitHub repository that contains the Vagrantfile and bootstrap Provisioning Script. Details on the Vagrantfile can be found if you read the Vagrant documentation. The purpose of the bootstrap script will also become evident as well. At least what it does with regard to Vagrantfile. Cloning from the GitHub will make a vagabond directory inside the directory the user specified for the Vagrant Project and we don’t want that. Vagabond will move the cloned files out of the cloned vagabond directory into the Vagrant Project directory and then remove the cloned directory.

At this point, we’re ready to use Vagrant. Now that Vagrantfile and bootstrap are in place, a call to vagrant up will use the Vagrantfile to configure the VM. Vagrantfile will then rely on bootstrap to supply additional configurations and instructions to the VM. The Vagrant Box used is the hashicorp/precise32 (Ubuntu Precise 32-bit). All other Vagrant configurations are defaults. The bootstrap Provisioning Script does the following:

  • Update apt repositories
  • Upgrade the system as well as install base dependencies for the Android SDK
  • Use a modified version of my getandroidsdkdl script to download the Android SDK from Google. This script will also unpack the SDK into /opt in the VM and configure permissions on it.

Once this is done, Vagabond will expose the synced directory, which is linked to the directory in the VM that contains the SDK, to the host system via a modification to PATH. That’s it. After that, so long as the VM is running, the host will have access to the SDK. The user can download and use whatever tools they want. Since the SDK exists exclusively in the VM, you can port the VM to anywhere you want.

What’s Next?

As I mentioned before, I’m letting a few people test this before I give it GA. Once I get those test results back and make some modifications to the script based on those tests, the version will be locked as 1 and released for GA. Stay tuned!