Remote Control of Audio/Video Using Bluetooth
Motorola Bluetooth Remote Control API
This article introduces the Motorola Bluetooth Remote Control (BRC) API, which helps you easily implement your application.
The Audio/Video Remote Control Profile is a Bluetooth profile that allows Bluetooth headset devices to control media playback on remote handset devices. This profile is needed to enable a number of additional operations, such as Play, Pause, and Stop, from a Bluetooth headset via a MIDlet.
Motorola Java ME BRC API
All Motorola OS handsets released since November 2005 support the Audio/Video Remote Control Profile. This API, implemented as the Motorola-proprietary class com.motorola.extensions.RemoteControl, is supported in MOTODEV Studio for Java ME v.1.3.
Table 1: com.motorola.extensions.RemoteControl class
| method | comment |
|---|---|
Public RemoteControl() |
Create a new RemoteControl object instance for interaction |
protected void handleEvent(int event) |
Receives notification about specific events that occur with remote control devices for example control loss, play press etc. These events shall be sent only to a Remote Control object that has called gainControl() previously and has not yet called loseControl(). |
Public void gainControl() |
Gains control (or token) and starts event reception. |
Public void loseControl() |
Gains control (or token) and starts event reception. |
Public boolean hasControl() |
Returns true if control (or token) is currently available. |
Key codes
RemoteControl defines the following key codes:
public static final int PLAY_PRESS = 1;
public static final int PLAY_RELEASE = 2;
public static final int PAUSE_PRESS = 3;
public static final int PAUSE_RELEASE = 4;
public static final int STOP_PRESS = 5;
public static final int STOP_RELEASE = 6;
public static final int FORWARD_PRESS = 7;
public static final int FORWARD_RELEASE = 8;
public static final int BACKWARD_PRESS = 9;
public static final int BACKWARD_RELEASE = 10;
public static final int FAST_FORWARD_PRESS = 11;
public static final int FAST_FORWARD_RELEASE = 12;
public static final int REWIND_PRESS = 13;
public static final int REWIND_RELEASE = 14;
public static final int VOLUME_UP_PRESS = 15;
public static final int VOLUME_UP_RELEASE = 16;
public static final int VOLUME_DOWN_PRESS = 17;
public static final int VOLUME_DOWN_RELEASE = 18;
public static final int MUTE_PRESS = 19;
public static final int MUTE_RELEASE = 20;
public static final int CONTROL_GAINED = 21;
public static final int CONTROL_LOST = 22;
static final int REMOTE_TOKEN_GAINED = 23;
static final int REMOTE_TOKEN_REVOKED = 24;
public static final int EXIT_PRESS = 25;
public static final int EXIT_RELEASE = 26;
static final int REMOTE_TOKEN_TIMEOUT = 1000;
Developing applications using the BRC API
Developing applications using MOTOROLA Remote Control API can be done as follows:
- Create a Player but do not start the player in your MIDlet.
- Create a class (such as RC) which extends the class
com.motorola.extensions.RemoteControland add code into the methodprotected void handleEvent(int event)to process the event.
void handleEvent(int event) { switch(event) { case 2: // PlayRelease try { player.start(); } catch (MediaException mediaexception) { } break; case 6: // StopRelease try { player.stop(); } catch (MediaException mediaexception4) { } break; } } - Control must be obtained using
rc.gainControl().This method specifies thatrcis ready to receive event notifications from the remote control device. We recommend calling this method when the player is ready to play or in similar situations. - Control can be lost using
rc.loseControl(). When using this method, it is necessary to specify that rc shall not receive event notifications from the remote control device. IfloseControl()is called more than once orgainControl()is not called before hand, this incorrect call should be ignored. ACommandListenercan be used to executerc.gainControl()andrc.loseControl().
NOTE: Refer to key codes defined by the class RemoteControl for the parameter int event.
Code Sample
The following is a code sample to show how to use the BRC API. This is a full source code listing for a MIDlet which uses the BRC API to control the audio player through a remote Bluetooth headset.
package com.motodev.dts.midlet;
import java.io.InputStream;
import java.util.Vector;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.TextField;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.midlet.MIDlet;
import remoteControl.ui.DebugForm;
public class RemoteControlMIDlet extends MIDlet
implements CommandListener
{
public static final String CONTENT = "audio/mp3";
public static final String VALID_NAME = "/res/small.mp3";
public static Vector vector = new Vector();
Player player;
InputStream stream;
private RC rc1;
private String status;
private String startA;
private String pauseA;
private Display display;
boolean paused;
public Form form;
private Command menuCommand;
private Command exitCommand;
private Command backCommand;
private Command selectCommand;
private Command viewLogCommand=new Command("ViewLog", Command.ITEM, 3);
private String exception;
private TextField exceptionField;
private int itemNum;
String elements[] = {
"Run", "Lost"
};
public RemoteControlMIDlet()
{
status = "";
startA = "StartupCompleted";
pauseA = "pauseApp";
paused = false;
menuCommand = new Command("Menu", 8, 1);
exitCommand = new Command("Exit", 7, 2);
backCommand = new Command("Back", 2, 0);
selectCommand = new Command("Select", 4, 1);
exception = "none";
exceptionField = new TextField("Exeption: ", exception, 256, 0);
display = Display.getDisplay(this);
}
public void startApp()
{
if(!paused)
{
displayForm();
runPlayer();
status += " " + startA;
log("----->StartupCompleted");
itemNum = form.append(status);
}
log("----->Try to create Object(first Object) of RemoteControl....");
try
{
rc1 = new RC("rc1", this, status, player,form,itemNum);
}
catch(Exception exception1)
{
updateException(exception1.toString());
log("----->FAILED: exception while creating Object of RemoteControl_1: " + exception1.toString());
}
log("----->Try to create Object(second Object) of RemoteControl....");
}
public void displayForm()
{
form = new Form("Test");
form.addCommand(menuCommand);
form.addCommand(exitCommand);
form.addCommand(viewLogCommand);
form.setCommandListener(this);
display.setCurrent(form);
}
public void runPlayer()
{
if(getClass().getResourceAsStream(VALID_NAME) == null)
log("----->ERROR getResourceAsStream ...");
stream = getClass().getResourceAsStream(VALID_NAME);
try
{
player = Manager.createPlayer(getClass().getResourceAsStream(VALID_NAME), CONTENT);
}
catch(Exception exception1)
{
log("----->FAILED: exception for createPlayer(): " + exception1.toString());
}
try
{
player.realize();
}
catch(MediaException mediaexception)
{
log("----->FAILED: exception for realize(): " + mediaexception.toString());
}
try
{
player.prefetch();
}
catch(MediaException mediaexception1)
{
log("----->FAILED: exception for prefetch(): "
+ mediaexception1.toString());
}
player.setLoopCount(3);
}
public void destroyApp(boolean flag)
{
}
public void pauseApp()
{
paused = true;
status += " " + pauseA;
log("pauseApp");
}
public static void log(String s)
{
System.out.println("" + s);
vector.addElement(s);
}
public void updateException(String s)
{
exceptionField.setString(s);
form.append(exceptionField);
log(s);
}
public void commandAction(Command command, Displayable displayable)
{
String s = command.getLabel();
try
{
if(s.equals("Exit"))
exit();
else
if(s.equals("ViewLog")){
DebugForm debug=new DebugForm(this, form,vector);
display.setCurrent(debug);
}
else
if(s.equals("Menu"))
testList();
else
if(s.equals("Back"))
{
display.setCurrent(form);
} else
{
List list = (List)display.getCurrent();
switch(list.getSelectedIndex())
{
case 0: // '\0'
Run();
break;
case 1: // '\001'
Lost();
break;
}
}
}
catch(Exception exception1)
{
updateException(exception1.toString());
}
}
private void exit()
{
try
{
destroyApp(true);
notifyDestroyed();
}
catch(Exception exception1)
{
updateException(exception1.toString());
}
}
private void testList()
{
List list = new List("Menu", 3, elements, null);
list.addCommand(backCommand);
list.addCommand(selectCommand);
list.setCommandListener(this);
display.setCurrent(list);
}
private void Run()
{
log("----->Try to gain control(first Object)....");
try
{
rc1.gainControl();
}
catch(Exception exception1)
{
updateException(exception1.toString());
log("----->FAILED: exception while gaining control: " + exception1.toString());
}
display.setCurrent(form);
}
private void Lost()
{
log("----->Try to lose control....");
try
{
rc1.loseControl();
}
catch(Exception exception1)
{
updateException(exception1.toString());
log("----->FAILED: exception losing gaining control: " + exception1.toString());
}
display.setCurrent(form);
}
}
package com.motodev.dts.midlet;
import javax.microedition.lcdui.Form;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import com.motorola.extensions.RemoteControl;
public class RC extends RemoteControl {
private String ControlGained = "ControlGained";
private String ControlLost = "ControlLost";
private String Play = "Play";
private String PlayRelease = "PlayRelease";
private String PauseRelease = "PauseRelease";
private String Stop = "Stop";
private String StopRelease = "StopRelease";
private String Pause = "Pause";
private String Forward = "Forward";
private String ForwardRelease = "ForwardRelease";
private String Backward = "Backward";
private String BackwardRelease = "BackwardRelease";
private String FastForward = "FastForward";
private String FastForwardRelease = "FastForwardRelease";
private String Rewind = "Rewind";
private String RewindRelease = "RewindRelease";
private String VolumeUp = "VolumeUp";
private String VolumeUpRelease = "VolumeUpRelease";
private String VolumeDown = "VolumeDown";
private String VolumeDownRelease = "VolumeDownRelease";
private String Mute = "Mute";
private String MuteRelease = "MuteRelease";
private RemoteControlMIDlet midlet = null;
private String status = null;
private Player player = null;
private Form form = null;
private int itemNum;
private String name;
public RC(String s, RemoteControlMIDlet midlet, String status, Player player,
Form form, int item) {
name = s;
this.midlet = midlet;
this.status = status;
this.player = player;
this.form = form;
this.itemNum = item;
}
public String getName() {
return name;
}
protected void handleEvent(int i) {
midlet
.log("----->RemoteControl: event " + i + " received in handleEvent");
switch (i) {
default:
break;
case 21: // '\025'
midlet.log("----->" + name + " " + ControlGained);
status+= " " + ControlGained + name;
break;
case 22: // '\026'
midlet.log("----->" + name + " " + ControlLost);
status+=" " + ControlLost + name;
break;
case 1: // '\001'
midlet.log("----->" + name + " " + Play);
status+=" " + Play + name;
break;
case 2: // '\002'
midlet.log("----->" + name + " " + PlayRelease);
status += " " + PlayRelease + name;
if (player != null && player.getState() != 0) {
try {
player.start();
} catch (MediaException mediaexception) {
midlet.log("----->FAILED: exception for start(): "
+ mediaexception.toString());
}
midlet.log("----->player.start() finished");
break;
}
try {
player = Manager.createPlayer(getClass().getResourceAsStream(
midlet.VALID_NAME), midlet.CONTENT);
} catch (Exception exception1) {
midlet.log("----->FAILED: exception for createPlayer(): "
+ exception1.toString());
}
try {
player.realize();
} catch (MediaException mediaexception1) {
midlet.log("----->FAILED: exception for realize(): "
+ mediaexception1.toString());
}
try {
player.prefetch();
} catch (MediaException mediaexception2) {
midlet.log("----->FAILED: exception for prefetch(): "
+ mediaexception2.toString());
}
player.setLoopCount(3);
try {
player.start();
} catch (MediaException mediaexception3) {
midlet.log("----->FAILED: exception for start(): "
+ mediaexception3.toString());
}
midlet.log("----->player.start() finished");
break;
case 5: // '\005'
midlet.log("----->" + name + " " + Stop);
status += " " + Stop + name;
break;
case 6: // '\006'
midlet.log("----->" + name + " " + StopRelease);
status += " " + StopRelease + name;
if (player == null || player.getState() == 0)
break;
try {
player.stop();
} catch (MediaException mediaexception4) {
midlet.log("----->FAILED: exception for stop(): "
+ mediaexception4.toString());
}
midlet.log("----->player.stop() finished");
player.deallocate();
player.close();
midlet.log("----->player.close() finished");
break;
case 3: // '\003'
midlet.log("----->" + name + " " + Pause);
status += " " + Pause + name;
break;
case 4: // '\004'
midlet.log("----->" + name + " " + PauseRelease);
status += " " + PauseRelease + name;
try {
player.stop();
} catch (MediaException mediaexception5) {
midlet.log("----->FAILED: exception for stop(): "
+ mediaexception5.toString());
}
midlet.log("----->player.stop() finished");
break;
case 7: // '\007'
midlet.log("----->" + name + " " + Forward);
status += " " + Forward + name;
break;
case 8: // '\b'
midlet.log("----->" + name + " " + ForwardRelease);
status += " " + ForwardRelease + name;
break;
case 9: // '\t'
midlet.log("----->" + name + " " + Backward);
status += " " + Backward + name;
break;
case 10: // '\n'
midlet.log("----->" + name + " " + BackwardRelease);
status += " " + BackwardRelease + name;
break;
case 11: // '\013'
midlet.log("----->" + name + " " + FastForward);
status += " " + FastForward + name;
break;
case 12: // '\f'
midlet.log("----->" + name + " " + FastForwardRelease);
status += " " + FastForwardRelease + name;
break;
case 13: // '\r'
midlet.log("----->" + name + " " + Rewind);
status += " " + Rewind + name;
break;
case 14: // '\016'
midlet.log("----->" + name + " " + RewindRelease);
status += " " + RewindRelease + name;
break;
case 15: // '\017'
midlet.log("----->" + name + " " + VolumeUp);
status += " " + VolumeUp + name;
break;
case 16: // '\020'
midlet.log("----->" + name + " " + VolumeUpRelease);
status += " " + VolumeUpRelease + name;
break;
case 17: // '\021'
midlet.log("----->" + name + " " + VolumeDown);
status += " " + VolumeDown + name;
break;
case 18: // '\022'
midlet.log("----->" + name + " " + VolumeDownRelease);
status += " " + VolumeDownRelease + name;
break;
case 19: // '\023'
midlet.log("----->" + name + " " + Mute);
status += " " + Mute + name;
break;
case 20: // '\024'
midlet.log("----->" + name + " " + MuteRelease);
status += " " + MuteRelease + name;
break;
}
form.delete(itemNum);
form.append(status);
super.handleEvent(i);
}
}
package remoteControl.ui;
import java.util.Vector;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.midlet.MIDlet;
public class DebugForm extends Form implements CommandListener {
private Command backCommand = new Command("Back", Command.BACK, 0);
private Command clearCommand = new Command("Clear", Command.ITEM, 1);
private MIDlet midlet;
private Displayable parent;
private Vector vector;
public DebugForm(MIDlet midlet, Displayable parent,Vector vector) {
super("debug");
this.midlet = midlet;
this.parent = parent;
this.vector=vector;
this.addCommand(backCommand);
this.addCommand(clearCommand);
setCommandListener(this);
displayVector();
}
public void commandAction(Command command, Displayable displayable) {
String s = command.getLabel();
if (s.equals("Back"))
Display.getDisplay(midlet).setCurrent(parent);
else if (s.equals("Clear")){
vector.removeAllElements();
displayVector();
}
}
private void displayVector(){
this.deleteAll();
for(int i=0;i<vector.size();i++){
String item=(String )vector.elementAt(i);
this.append(item);
}
}
}
Testing
The following notes apply when testing an application using the BRC API:
- A mobile Bluetooth headset is required – before testing you must set the Bluetooth connection between the handset and a Bluetooth headset. The MIDlet has been developed and tested with the following Motorola Bluetooth headsets: HT820 & S90.
- Setting the Bluetooth connection – With the Motorola headset off, press and hold the Multifunction button until the light shines steadily – at least 3 seconds. On your handset perform a device discovery. Go to Settings ->Connection ->Bluetooth link ->Hands Free ->Look for Devices. Select the Bluetooth headset (for example Motorola HT820) from the list of discovered devices. Enter the passkey (0000 for the HT820) when prompted, and then press OK. The handset and Bluetooth headset should now be paired.
Testing steps
- Launch the application “Remote Control Midlet”
- Select “Options”
- Select “Menu”
- Select “Run”
- Press and release the play button on the headset. Music is played.
- Press and release the pause button on the headset. Music is stopped.
- Select “Options”
- Select “Menu”
- Select “Lost”
- Press and release the play button on the headset. No music is played.
- Select Exit.
Summary
When developing applications using the Motorola Remote Control API, you can extend the class com.motorola.extensions.RemoteControl and add code into the method protected void handleEvent(int event). The MIDlet must be run on the latest Motorola OS handsets. All Motorola OS handsets released since November 2005 support the Audio/Video Remote Control Profile. The Bluetooth connection between the handset and the Bluetooth headset must be established before running the MIDlet. The RemoteControl class can control both audio and video players.