Skip to content

Latest commit

 

History

History
210 lines (197 loc) · 6.34 KB

11-Ti.AudioManager.md

File metadata and controls

210 lines (197 loc) · 6.34 KB

Ti.AudioManager

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);
}

Changing the AudioFocus

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.

requestAudioFocus()

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;

abandomAudioFocus()

public void abandonAudioFocus() {
	am.abandonAudioFocus(afChangeListener);
}

Reacting to changing the audio output-device

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);
}

Listen to the headset-keyboard

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;
}