By Barry Burd

Android drives many of Google’s innovative projects. Take, for example, the tablet device known as Project Tango. At first glance, a Project Tango device looks like an ordinary seven-inch tablet. When you turn on the device, you see the familiar Android Home screen with icons for launching apps, and with indicators for WiFi signal and Battery strength.

The device even runs Google’s Play Store app, so you can use it to browse the web, edit documents, and do all the other things that ordinary tablet devices do.

But Project Tango’s hardware isn’t ordinary. The back of the tablet has a depth sensor, a fish eye camera, and an IR camera. Combining data from these sources with its internal motion tracking hardware, Project Tango creates a 3D map of the surrounding space.

You can make a map of your own living room and store the map on a Project Tango device. When you bring the device back to your living room, the device remembers the space and all the objects inside the space.

Tango comes with a few demo apps. With one app, you aim the device at two points to measure the distance between those points. You can stand several feet from the starting and ending points because the device knows how far you are from each of the points.

With another app, you walk through virtual worlds. To change your point of view in a world, you don’t twist a joystick, move a mouse, or drag your fingers along a screen. Instead, you watch the device’s screen as you move around your own real-life environment. The device shows you what you’d see in the virtual world as you walk forward, turn to look sideways, and navigate your way around virtual 3D objects.

To get you started doing development, Project Tango comes with some sample apps. Opening the very basic QuickStart app in Android Studio displays translation and rotation values as you move the device from place to place. (See the following figure.)

The QuickStart app.
The QuickStart app.

At the core of the QuickStart app is an Android Activity. Some of the app’s code appears below.

/*
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import com.google.atap.tangoservice.Tango;
import com.google.atap.tangoservice.Tango.OnTangoUpdateListener;
import com.google.atap.tangoservice.TangoConfig;
import com.google.atap.tangoservice.TangoCoordinateFramePair;
import com.google.atap.tangoservice.TangoPoseData;
// There are more import declarations here
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTranslationTextView = 
                         (TextView) findViewById(R.id.translation_text_view);
        mRotationTextView = (TextView) findViewById(R.id.rotation_text_view);
        // Instantiate Tango client
        mTango = new Tango(this);
        // Set up Tango configuration for motion tracking
        mConfig = mTango.getConfig(TangoConfig.CONFIG_TYPE_CURRENT);
        mConfig.putBoolean(TangoConfig.KEY_BOOLEAN_MOTIONTRACKING, true);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, 
                           Intent data) {
        try {
            setTangoListeners();
        } catch (TangoErrorException e) {
        }
        try {
            mTango.connect(mConfig);
        } catch (TangoOutOfDateException e) {
        } catch (TangoErrorException e) {
        }
    }
    private void setTangoListeners() {
        // Select coordinate frame pairs
        ArrayList<TangoCoordinateFramePair> framePairs = 
                                    new ArrayList<TangoCoordinateFramePair>();
        framePairs.add(new TangoCoordinateFramePair(
                TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
                TangoPoseData.COORDINATE_FRAME_DEVICE));
        // Add a listener for Tango pose data
        mTango.connectListener(framePairs, new OnTangoUpdateListener() {
            @Override
            public void onPoseAvailable(TangoPoseData pose) {
                // Format Translation and Rotation data
                final String translationMsg = String.format(sTranslationFormat,
                        pose.translation[0], pose.translation[1],
                        pose.translation[2]);
                final String rotationMsg = String.format(sRotationFormat,
                        pose.rotation[0], pose.rotation[1], pose.rotation[2],
                        pose.rotation[3]);
                // Display data in TextViews
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mTranslationTextView.setText(translationMsg);
                        mRotationTextView.setText(rotationMsg);
                    }
                });
            }
        });
    }
}

What you see here isn’t the entire 210-line MainActivity class, but some of the class’s important highlights have been summarized.

Have a look at the MainActivity class. The heart of the activity’s code is inside the anonymous OnTangoUpdateListener class with its onPoseAvailable method.

In everyday life, a pose is the way you orient your head compared to your surroundings. And in Project Tango terminology, a pose is the way you orient the device relative to its surroundings. A pose is described by seven numbers, like the translation and rotation numbers that you see in the preceding figure.

When the device’s hardware senses a new pose, the software calls the onPoseAvailable method. The onPoseAvailable method must display messages like Translation: -2.6622, 3.214773, 2.824979 in the activity’s text fields.

But there’s a problem. Android’s threading rules dictate that no thread other than the main thread can update an application’s user interface. In the MainActivity class, the OnTangoUpdateListener instance runs in a thread all its own. So the instance’s onPoseAvailable method can’t display messages in the activity’s text fields by directly calling the mTranslationTextView.setText and mRotationTextView.setText methods.

But as Professor Farnsworth in Futurama always says, there’s “Good news, everyone!” Android has a runOnUiThread method for just such occasions. The runOnUiThread method’s parameter is a class that implements Java’s Runnable interface. In the QuickStart app’s code, this class’s run method displays text on behalf of the OnTangoUpdateListener instance. In the end, the device’s user sees the Translation and Rotation data, and all is well.