This Titanium module exposes all interfaces of the AudioManager class of Android.
In the constructor, we create the following objects:
public AudiomanagerModule() {
super();
context = TiApplication.getInstance().getApplicationContext();
am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
receiver = new NoisyAudioStreamReceiver();
keylistener = new RemoteControlReceiver();
intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
}
With multiple apps potentially playing audio, it's important to think about how they should interact. To avoid that every music app is playing at the same time, Android uses audio focus to moderate playback—only apps that currently hold the audio focus while playing audio.
To realize this, we use two functions: requestAudioFocus
and abandonAudioFocus
.
First, we define the behavior after focusChanging
and we make a callback after it's appearing:
@Kroll.method
public int requestAudioFocus(KrollDict opts) {
afChangeListener = new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
KrollDict dict = new KrollDict();
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
dict.put("state", "paused");
break;
case AudioManager.AUDIOFOCUS_GAIN:
dict.put("state", "resumed");
break;
case AudioManager.AUDIOFOCUS_LOSS:
dict.put("state", "stopped");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
dict.put("state", "duck");
break;
}
if (onChangedCallback != null)
onChangedCallback.call(getKrollObject(), dict);
}
};
}
The request has two parameters. In the first setp, we set the default values:
int streamType = AudioManager.STREAM_MUSIC;
int focusType = AudioManager.AUDIOFOCUS_GAIN;
and then we read the parameters and the callback function:
if (opts != null) {
if (opts.containsKeyAndNotNull("streamType")) {
streamType = opts.getInt("streamType");
}
if (opts.containsKeyAndNotNull("focusType")) {
focusType = opts.getInt("onchanged");
}
if (opts.containsKeyAndNotNull("onChanged")) {
onChangedCallback = (KrollFunction) opts.get("onChanged");
}
}
After this import of the Javascript properties, we call the system-function with our imported parameters:
int result = am.requestAudioFocus(afChangeListener, streamType,focusType);
return result;
public void abandonAudioFocus() {
am.abandonAudioFocus(afChangeListener);
}
First we can subscribe the module to listen to the callback:
@Kroll.method
public void registerRouteListener(KrollDict opts) {
if (opts.containsKeyAndNotNull("onAction")) {
// Bind the local KrollFunc to a JavasScript function
onActionCallback = (KrollFunction) opts.get("onAction");
}
context.registerReceiver(receiver, intentFilter);
}
After that, we can use the result:
private class NoisyAudioStreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
KrollDict dict = new KrollDict();
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
dict.put("device", AUDIOROUTE_LOUDSPEAKER);
}
if (AudioManager.ACTION_HDMI_AUDIO_PLUG.equals(intent.getAction())) {
dict.put("device", AUDIOROUTE_HDMI);
}
if (AudioManager.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
dict.put("device", AUDIOROUTE_HEADSET);
}
if (onActionCallback !=null) onActionCallback.call(getKrollObject(), dict);
}
}
And because we are fine people, we finally clean-up the receiver:
@Kroll.method
public void unregisterRouteListener(KrollDict opts) {
context.unregisterReceiver(receiver);
}
And if the Javascript-programmer forgot:
public void onDestroy(Activity activity) {
context.unregisterReceiver(receiver);
super.onDestroy(activity);
}
This is a simple action. Before we implement the receiver, we need the possibility to register and unregister:
@Kroll.method
public void registerMediaButtonEventReceiver(KrollDict opts) {
if (opts.containsKeyAndNotNull("keypressed")) {
onKeyCallback = (KrollFunction) opts.get("keypressed");
}
context.registerReceiver(keylistener, intentFilter);
}
@Kroll.method
public void unregisterMediaButtonEventReceiver() {
context.unregisterReceiver(keylistener);
}
And now:
private class RemoteControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
KrollDict dict = new KrollDict();
dict.put("keycode", event.getKeyCode());
onKeyCallback.call(getKrollObject(), dict);
}
}
}
##List of all devices: Because we cannot (?) return an array, only an object, we put the result in an object:
@Kroll.method
public KrollDict getDevices() {
AudioDeviceInfo[] devices = am.getDevices(AudioManager.GET_DEVICES_ALL);
KrollDict dict = new KrollDict();
ArrayList<KrollDict> listOfDevices = new ArrayList<KrollDict>();
for (AudioDeviceInfo device : devices) {
KrollDict deviceDict = new KrollDict();
deviceDict.put("productName", device.getProductName());
deviceDict.put("isSource", device.isSource());
String type = "";
switch (device.getType()) {
case AudioDeviceInfo.TYPE_AUX_LINE:
type = "aux_line";
break;
case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
type = "bluetooth_a2dp";
break;
case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
type = "bluetooth_sco";
break;
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
type = "builtin_speaker";
break;
case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
type = "builtin_earpiece";
break;
case AudioDeviceInfo.TYPE_BUILTIN_MIC:
type = "builtin_mic";
break;
case AudioDeviceInfo.TYPE_DOCK:
type = "dock";
break;
case AudioDeviceInfo.TYPE_FM:
type = "fm";
break;
case AudioDeviceInfo.TYPE_FM_TUNER:
type = "fm_tuner";
break;
}
deviceDict.put("type", type);
listOfDevices.add(deviceDict);
}
dict.put("devices", listOfDevices);
return dict;
}