Friday, October 17, 2014

A Guide to the Android Wear Message API

I originally wrote this post for the good folks over at Binpress.com

While Android Wear provides a lot of great features, such as notifications, out of the box, its real potential lies in the ability to create native apps that communicate with a paired smartphone. Luckily, Android Wear comes with a few new APIs to help make this communication a lot smoother for developers: the Message API, Node API and DataLayer API. Each of these has their own unique purpose; the Message API is designed for "fire and forget it" types of messages, the DataLayer API supports syncing data between a smartphone and a wearable, and the Node API handles events related to local and connected device nodes.

For this tutorial I'll cover using the Message API to send data from a smartphone to an Android Wear device. We'll start a watch activity by using a WearableListenerService and display text in a ListView through the MessageApi.MessageListener interface, though all three APIs are used in a similar way. All sample code for this tutorial can be found on GitHub.

To start, create a project in Android Studio that has both a mobile and wear module:


Once your project and modules are created, you can start building the mobile side of your application. The sample project contains simple ListView, EditText and Button views that allow the user to enter text and, when the Button is pressed, display it in the ListView while also sending that text to the wearable.


To start, open AndroidManifest.xml in the mobile module and add a meta-data tag for Google Play Services

<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

Next, we can focus on the mobile MainActivity class. The first thing we need to do is connect the GoogleApiClient and implement the GoogleApiClient.ConnectionCallbacks interface:

private void initGoogleApiClient() {
    mApiClient = new GoogleApiClient.Builder( this )
            .addApi( Wearable.API )
            .addConnectionCallbacks( this )
            .build();

    if( mApiClient != null && !( mApiClient.isConnected() || mApiClient.isConnecting() ) )
        mApiClient.connect();
}

When the GoogleApiClient has finished connecting, onConnected will be called. When it is, we'll send a message through the Message API to a WearableListenerService (implemented later in this tutorial) running on the Wear in order to start our watch activity.

@Override
public void onConnected(Bundle bundle) {
    sendMessage( START_ACTIVITY, "" );
}

...where START_ACTIVITY is a string value that must begin with /, denoting a message path.

private static final String START_ACTIVITY = "/start_activity";

Aside from our initial message, we also want a message to be sent when our 'send' button is pressed. This is done through a simple OnClickListener that uses a different path value of /message and sends the text present in the EditText at the bottom of the screen.

mSendButton.setOnClickListener( new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String text = mEditText.getText().toString();
        if (!TextUtils.isEmpty(text)) {
            mAdapter.add(text);
            mAdapter.notifyDataSetChanged();

            sendMessage(WEAR_MESSAGE_PATH, text);
        }
    }
});

The sendMessage method is where we really start to see how we interact with the new Android Wear APIs. In order to send a message to the Wear, we must first use the Node API to get a list of nodes connected to the device. Once we have this list, we send a message to each node using MessageAPI with references to the GoogleApiClient, node ID, the path used to determine the type of message being sent, and the message payload as a byte array. A MessageApi.SendMessageResult is returned that can be used to determine if a message was successfully sent to the current node. Once the message is sent, we clear the EditText view to allow the user to enter more text.

private void sendMessage( final String path, final String text ) {
    new Thread( new Runnable() {
        @Override
        public void run() {
            NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes( mApiClient ).await();
            for(Node node : nodes.getNodes()) {
                MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(
                        mApiClient, node.getId(), path, text.getBytes() ).await();
            }

            runOnUiThread( new Runnable() {
                @Override
                public void run() {
                    mEditText.setText( "" );
                }
            });
        }
    }).start();
}


Finally in onDestroy, we need to disconnect from GoogleApiClient.

@Override
protected void onDestroy() {
    super.onDestroy();
    mApiClient.disconnect();
}


Now that the mobile MainActivity is ready, it's time to move into the wear module. First we'll want to create a class that extends from WearableListenerService, which is a service that implements the three Wear communication APIs. For our purposes we only need to override the onMessageReceived method. In this method we check the path that was sent over the MessageApi to determine the type of message, and then, if appropriate, fire off an intent to our wear MainActivity to bring our application to the forefront.

private static final String START_ACTIVITY = "/start_activity";

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    if( messageEvent.getPath().equalsIgnoreCase( START_ACTIVITY ) ) {
        Intent intent = new Intent( this, MainActivity.class );
        intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
        startActivity( intent );
    } else {
        super.onMessageReceived(messageEvent);
    }
}

Once we're done implementing our service, we need to add it into the wear AndroidManifest.xml file.

<service android:name=".WearMessageListenerService">
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
    </intent-filter>
</service>

As mentioned earlier, there are two ways to receive a message on Android Wear. The next way is implementing MessageApi.MessageListener in our Wear MainActivity. To start, we need to connect GoogleApiClient in the same way as our mobile application. In the onConnected callback, we add our wearable listener to the api client

@Override
public void onConnected(Bundle bundle) {
    Wearable.MessageApi.addListener( mApiClient, this );
}


Next, we simply override the onMessageReceived method from the MessageListener interface, check the path value to determine if we want to take action, and then add the payload text to our adapter to display the text in our wear ListView

@Override
public void onMessageReceived( final MessageEvent messageEvent ) {
    runOnUiThread( new Runnable() {
        @Override
        public void run() {
            if( messageEvent.getPath().equalsIgnoreCase( WEAR_MESSAGE_PATH ) ) {
                mAdapter.add(new String(messageEvent.getData()));
                mAdapter.notifyDataSetChanged();
            }
        }
    });
}

This will display the text from our mobile app on the wearable, as seen below (the screen shot was taken from a Moto 360, hence the cut off portion at the bottom).

And with that we now have a working communication channel from a smartphone to a paired Android Wear device. I hope this tutorial helps you build your own applications. Good luck!

Wednesday, September 10, 2014

Getting Started with the Gamepad Controller for Android TV

I originally wrote this tutorial for the good folks over at Binpress.
With the release of Android TV, game developers will have a new platform to contend with. Luckily, there's only one major change that needs to be considered when building your game for Android TV: implementing the new controller.
In this post, I'll go over using the new controller in the context of a simple (i.e. it works, but isn't polished or something you'd spend hours playing) Asteroids-esque OpenGL game. All source code for this example can be found on GitHub.

The first thing to take note of when building a game for Android TV is that the application tag in AndroidManifest.xml has an attribute called 'isGame', which is set to true. This is what places your application into the Games section of the menu selection screen.
  1. <application
  2.     android:allowBackup="true"
  3.     android:icon="@drawable/ic_launcher"
  4.     android:label="@string/app_name"
  5.     android:isGame="true"
  6.     android:theme="@style/AppTheme" >

Next we need to handle input as it happens. We do this by overriding two methods in our main Activity class - dispatchGenericMotionEvent and dispatchKeyEvent. In this example I simply let our controller (MVC controller, not physical controller :)) know that an event has happened, and it handles interpreting what it is and doing something with it.
  1. MainActivity.java:
  2. @Override
  3. public boolean dispatchGenericMotionEvent(MotionEvent event) {
  4.     return mGame.handleMotionEvent(event);
  5. }
  6. @Override
  7. public boolean dispatchKeyEvent(KeyEvent event) {
  8.     return mGame.handleKeyEvent(event);
  9. }
  10. GameView.java:
  11. public boolean handleMotionEvent( MotionEvent motionEvent ) {
  12.     if ( mShip != null ) {
  13.         mShip.getController().setDeviceId( motionEvent.getDeviceId() );
  14.         mShip.getController().handleMotionEvent( motionEvent );
  15.         return true;
  16.     }
  17.     return false;
  18. }
  19. public boolean handleKeyEvent( KeyEvent keyEvent ) {
  20.     if ( mShip != null ) {
  21.         mShip.getController().setDeviceId( keyEvent.getDeviceId() );
  22.         mShip.getController().handleKeyEvent( keyEvent );
  23.         return true;
  24.     }
  25.     return false;
  26. }
As you can see in GameView.java, we check to see if our ship object has been initialized, and if it has we get the controller associated with that ship. (Since this is a single player game, there's no logic to assigning one of multiple controllers to a ship). Finally, the device ID and event are passed to the game controller utility class.
The GamepadController.java class is where things get a bit more interesting. When the controller is initialized, we create a pair of two-dimensional arrays for storing the state of the buttons and joystick positions on the controller. Each button in mButtonState[][] is associated with its own index and keeps track of state during the current and previous frame. Each joystick in mJoystickPositions[][] also has its own index, but the values stored are current positions on the X and Y axes.
  1. // The buttons on the game pad.
  2. public static final int BUTTON_A = 0;
  3. public static final int BUTTON_B = 1;
  4. public static final int BUTTON_X = 2;
  5. public static final int BUTTON_Y = 3;
  6. public static final int BUTTON_R1 = 4;
  7. public static final int BUTTON_R2 = 5;
  8. public static final int BUTTON_L1 = 6;
  9. public static final int BUTTON_L2 = 7;
  10. public static final int BUTTON_COUNT = 8;
  11. // The axes for joystick movement.
  12. public static final int AXIS_X = 0;
  13. public static final int AXIS_Y = 1;
  14. public static final int AXIS_COUNT = 2;
  15. // Game pads usually have 2 joysticks.
  16. public static final int JOYSTICK_1 = 0;
  17. public static final int JOYSTICK_2 = 1;
  18. public static final int JOYSTICK_COUNT = 2;
  19. // Keep track of button states for the current and previous frames.
  20. protected static final int FRAME_INDEX_CURRENT = 0;
  21. protected static final int FRAME_INDEX_PREVIOUS = 1;
  22. protected static final int FRAME_INDEX_COUNT = 2;
  23. // Positions of the two joysticks.
  24. private final float mJoystickPositions[][];
  25. // The button states for the current and previous frames.
  26. private final boolean mButtonState[][];
  27. public GamepadController() {
  28.     mButtonState = new boolean[BUTTON_COUNT][FRAME_INDEX_COUNT];
  29.     mJoystickPositions = new float[JOYSTICK_COUNT][AXIS_COUNT];
  30.     resetState();//initializes values
  31. }
With the controller arrays initialized, we can get back to handling our input.handleMotionEvent has two parts: getting the input from the first joystick on the controller and the second joystick (if there is any input).
  1. public void handleMotionEvent(MotionEvent motionEvent) {
  2.     //Joystick 1:
  3.     mJoystickPositions[JOYSTICK_1][AXIS_X] = motionEvent.getAxisValue(MotionEvent.AXIS_X);
  4.     mJoystickPositions[JOYSTICK_1][AXIS_Y] = motionEvent.getAxisValue(MotionEvent.AXIS_Y);
  5.     //Joystick 2:
  6.     mJoystickPositions[JOYSTICK_2][AXIS_X] = motionEvent.getAxisValue(MotionEvent.AXIS_Z);
  7.     mJoystickPositions[JOYSTICK_2][AXIS_Y] = motionEvent.getAxisValue(MotionEvent.AXIS_RZ);
  8. }
As you can see, the axes are mapped as X and Y for the left joystick, and Z and RZ for the second. The values from those axes are read and stored in our array for reading by our model classes.
We also save key pressed events by determining if the key action is "down," and saving that value in our button state array based on which button is being pressed.
  1. public void handleKeyEvent(KeyEvent keyEvent) {
  2.     boolean keyIsDown = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
  3.     if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_A) {
  4.         mButtonState[BUTTON_A][FRAME_INDEX_CURRENT] = keyIsDown;
  5.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B) {
  6.         mButtonState[BUTTON_B][FRAME_INDEX_CURRENT] = keyIsDown;
  7.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_X) {
  8.         mButtonState[BUTTON_X][FRAME_INDEX_CURRENT] = keyIsDown;
  9.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_Y) {
  10.         mButtonState[BUTTON_Y][FRAME_INDEX_CURRENT] = keyIsDown;
  11.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_R1 ) {
  12.         mButtonState[BUTTON_R1][FRAME_INDEX_CURRENT] = keyIsDown;
  13.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_R2 ) {
  14.         mButtonState[BUTTON_R2][FRAME_INDEX_CURRENT] = keyIsDown;
  15.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_L1 ) {
  16.         mButtonState[BUTTON_L1][FRAME_INDEX_CURRENT] = keyIsDown;
  17.     } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_L2 ) {
  18.         mButtonState[BUTTON_L2][FRAME_INDEX_CURRENT] = keyIsDown;
  19.     }
  20. }
Once the gamepad controller state is stored, we can access that from our object that is associated with that controller - in this case our unstoppable hero, the USS Triangle ship. The game calls the ship's update method every frame, so that's where we're going to handle updating the ship position by reading in joystick values, and wether or not the ship should fire a bullet.
  1. public void update(float delta ) {
  2.     if ( !updateStatus( delta ) ) {
  3.         return;
  4.     }
  5.     updateShipPosition( delta );
  6.     handleKeyInput( delta );
  7. }
updateStatus simply checks to see if the ship is spawned, and -- if it is -- it calls the methods to update position or handle key pressed events. In updateShipPosition, we grab the X and Y axis positions from the controller, and then use those to determine the magnitude of the joystick movement via the Pythagorean theorem (wrapped up in our Utils class methodvector2DLength).
  1. float newHeadingX = mController.getJoystickPosition(GamepadController.JOYSTICK_1,
  2.             GamepadController.AXIS_X);
  3. float newHeadingY = mController.getJoystickPosition(GamepadController.JOYSTICK_1,
  4.             GamepadController.AXIS_Y);
  5. float magnitude = Utils.vector2DLength(newHeadingX, newHeadingY);
Once we have our headings and magnitude, we can see if that magnitude is significant enough to be used (in this case, over 10 percent movement from the center), then store the new headings in our ship's values and set the velocity for our ship. If the magnitude for the joystick is somehow greater than a set max value, then we divide our velocities by the magnitude to provide a max velocity along each axis.
  1. if (magnitude > GamepadController.JOYSTICK_MOVEMENT_THRESHOLD) {
  2.         //Get the heading divided by how much the joystick is being used
  3.         mHeadingX = newHeadingX / magnitude;
  4.         mHeadingY = -newHeadingY / magnitude;
  5.         setVelocity( newHeadingX, -newHeadingY );
  6.         if (magnitude > 1.0f) {
  7.             //Sets a cap velocity
  8.             mVelocityX /= magnitude;
  9.             mVelocityY /= magnitude;
  10.         }
  11.     }
Handling button pressed events is a bit more straight forward in this example - we check to see if a cool down timer has run up for firing a bullet, and we do the following if it has. We check to see if the X button is currently in a down position, reset the cool down, calculate aim based on the direction the ship is facing and create a bullet object at the ship's position.
  1. private void handleKeyInput( float delta ) {
  2.     if( mFireTimer > 0 ) {
  3.         mFireTimer--;
  4.         return;
  5.     }
  6.     if ( mController.isButtonDown( GamepadController.BUTTON_X ) && mFireTimer == 0 ) {
  7.         mFireTimer = FIRE_REFRESH_TIMER;
  8.         calculateAimDirection();
  9.         fireGun();
  10.     }
  11. }
Aim can be calculated with joystick 2 in the same way our heading is calculated for the ship, or it can simply set the aiming value as the heading if the second joystick isn't used for aiming (which is how it's used in the sample project).
Other than handling the new key events, the rest of the game is built in a standard way. This example throws together a quick OpenGL project, but the same ideas can be applied however your game is built. Hopefully this'll help some of you get rolling on getting your games onto the new Android TV platform!