Flex Audio SDK for Android: Quickstart
The Aircore Flex SDK for Android allows you to quickly add high-quality, real-time voice chat to your Android app. This lets you avoid dealing with the complexities of building real-time social infrastructure on your own.
Install the SDK
Get this SDK using Maven Central. The package is called aircore-media
.
Add
mavenCentral()
to therepositories
section of your app'sbuild.gradle
orbuild.gradle.kts
file:// build.gradle repositories { mavenCentral() }
Add an implementation dependency for this SDK to your
build.gradle
orbuild.gradle.kts
file.// build.gradle dependencies { implementation "io.aircore.media:aircore-media:$flex_version" }
To get the latest version to use for
$flex_version
, visit the Maven Central Repository.
Set up your app
Logging
This SDK logs key events and conditions to help with debugging. By default, the SDK logs messages to the system log.
To customize logging, see More options.
Permissions
To fully use this SDK, get the required runtime permissions. For each permission listed below:
Add the permission to your app's Android manifest.
Request the permission from the user.
For general info, see Request runtime permissions in the Android docs.
Required permissions
Get these permissions to record audio and use the microphone on a connected Bluetooth device:
BLUETOOTH_CONNECT
(Android 12 and later)
Extra permission for API level 32 or earlier
If your app targets Android API level 33 or later, you can skip this step.
If your app targets level 32 or earlier, get this permission on devices running Android 13:
This SDK uses a foreground service for audio quality. Even without the POST_NOTIFICATIONS
permission, the foreground service can start. Users can see the foreground service in the Task Manager.
If your app does have this permission, users see a notification when the service is running.
With API level 32 or earlier, Android 13 users see a request for the permission. If you don't set up the request, Android shows a default request. If a user ignores the request, they see it again every time the app starts until they make a choice.
Authenticate
In this guide, we use a publishable API key for a quick setup. To get your API key:
Create an app in the Developer Console.
Copy your publishable API key and use it to create channels in the next section.
For an overview of API keys, see Authentication.
Create and use a channel
A channel is a connection to a real-time session with other users. See Channels for a general overview.
Joining a channel allows your app to:
Publish a local stream to share audio with other users
Receive remote streams to play audio from other users
Create a channel
The singleton provided by Engine.getInstance()
is the central location for configuring this SDK.
Use it to create a channel:
Call the
createChannel()
method and keep the resultingChannel
object:import io.aircore.media.Channel; import io.aircore.media.Engine; // The publishable API key for joining the channel. String key = "PUBLISHABLE_API_KEY"; // Set up variables for notification. String appName = ""; // Message to display on the notification when the app is running in the // background. String message = ""; // Icon for the notification. @DrawableRes int icon = R.mipmap.launcher; // Create the channel parameters. String userId = "user_id"; String channelId = "channel_id"; ChannelParams channelParams = new ChannelParams.Builder(key, userId, channelId) .build(); // Create the channel. Channel channel = Engine.getInstance(context, appName, message, icon) .createChannel(channelParams); // Check if the channel was successfully created. if (channel == null) { // Handle error. }
To connect to the channel and start receiving audio, call its
join()
method:// Join the channel. channel.join();
Only connect to one
Channel
object at a time.To set the volume for the channel, use its
setOutputVolume()
method. You can do this before or after joining.
Leave a channel
When you no longer need a channel, call its leave()
method. You can use this method at any time, except when the channel is already terminated.
Leaving destroys all objects for local and remote streams, releasing system resources used by the channel.
The local channel instance also terminates in these cases:
All references to the channel go out of scope
An unrecoverable error occurs
To see why a channel terminated, use its getTerminationCause()
method.
You cannot rejoin a terminated channel. To return to the session with the same users, create a new channel
.
Track the status of a channel
Use a channel's getJoinState()
method to see its connection status.
To track changes to this property, register a channel delegate and listen for the joinStateDidChange()
method.
import io.aircore.media.Channel;
class ChannelDelegate implements Channel.Delegate {
// Implement other notification handlers.
// ...
@Override
public void joinStateDidChange(Channel.JoinState newState, Channel.JoinState oldState) {
switch (newState) {
case NOT_JOINED:
// The initial state. The channel has not yet connected.
// No interaction with other users is possible.
// NOTE: A notification for NOT_JOINED is not sent to the delegate.
break;
case JOINING:
// The channel is connecting for the first time.
break;
case JOINED:
// The channel is connected and automatically plays remote streams.
break;
case REJOINING:
// The channel connected and then disconnected.
// It is now reconnecting.
break;
case TERMINATED:
// The channel has permanently disconnected and its resources
// are being cleaned up. You can check the termination cause
// here.
// NOTE: You can't reuse the channel, but you can detect if the
// channel terminated unexpectedly and either create a new
// channel or show an error to the user.
Channel.TerminationCause terminationCause = channel.getTerminationCause();
break;
default:
}
}
}
Channel.Delegate channelDelegate = new ChannelDelegate();
// Register a delegate with the channel before joining to make sure that you
// receive all notifications.
channel.addDelegate(channelDelegate);
You can use the join state to build your user experience and UI. See More options for examples.
Publish audio
Users share audio within a channel as streams. To publish audio to other users, you create a local stream.
Use a channel's
createLocalStream()
method to create a local stream.The channel's join state must be
JOINED
orREJOINING
.import io.aircore.media.Channel; import io.aircore.media.LocalStream; import io.aircore.media.LocalStreamParams; LocalStreamParams params = new LocalStreamParams.Builder().build(); // Make sure that the channel is connected. Channel.JoinState joinState = channel.getJoinState(); if (joinState != Channel.JoinState.JOINED || joinState != Channel.JoinState.REJOINING) { return; } // Create a local stream. LocalStream localStream = channel.createLocalStream(params); if (localStream == null) { // Can't create a stream. }
Use
start()
to publish audio from the microphone.The channel's join state must be
JOINED
orREJOINING
. The channel must not already have an active local stream attached.// Make sure that you have microphone permissions before calling start(). if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { return; } // Start the local stream. localStream.start();
Use
muteAudio()
to stop capturing and publishing audio:// Mute the local stream. localStream.muteAudio(true); // Unmute the local stream. localStream.muteAudio(false);
To create a muted stream, see More options.
Use
stop()
to stop the stream:// Stop the local stream and clean up its resources. localStream.stop();
See More options for other tasks related to local streams:
Receive audio
When your app connects to a channel, it receives audio streams from other users. These are called remote streams.
The SDK automatically connects to remote streams and plays the audio through the active output device. You don't have to do any setup for your app to play the audio from remote streams.
For tasks such as getting metadata and muting a stream, you can use remote stream objects.
Check remote streams in a channel
To get a list of active remote streams within a channel, use its getRemoteStreams()
method. You can use this to show active streams in your UI.
Each item in the list is a RemoteStream
object, which represents a stream from one user.
To get notifications for added and removed streams, see More options.
Check the connection to a remote stream
To see if a remote stream is connected, use the getConnectionState()
method.
To get notifications for remote stream connection status, see More options.
Mute audio from a remote stream
To mute the incoming audio from a remote stream, use its muteAudio()
method. Your app still receives the audio but doesn't play it.
Track local audio muting
To track local muting of an incoming remote stream with muteAudio()
, you can:
Call the remote stream's
getLocalAudioMuted()
method.Register a delegate to the remote stream to listen for the
localAudioMuteStateDidChange()
method.
Track remote audio muting
The publisher can also mute a remote stream. To track when the publisher mutes audio that your app is receiving, you can:
Call the remote stream's
getRemoteAudioMuted()
method.Register a delegate to the remote stream to listen for the
remoteAudioMuteStateDidChange()
method.
Check for voice activity
To check if a remote stream has audible speech, call its hasVoiceActivity()
method.
To track changes to this property, register a delegate to the remote stream and listen for the voiceActivityStateDidChange()
method.
Check termination cause
To see why a remote stream ended, call its getTerminationCause()
method.
Terminated streams are removed from the set returned by the channel's getRemoteStreams()
method. RemoteStream
objects are destroyed when your app no longer holds a reference to them.
More info
To continue building your app, see More options.
See the full API reference.
Download our sample app.