Android SDK - Low Level APIs
The following low level APIs are provided in the Android sdk:
- PCast - Use the Phenix platform to Publish and Subscribe to streams
- Room - Use the Phenix platform to broadcast changes between all of your users
- Chat - Use the Phenix platform to send and receive chat messages between users
Android SDK
Initializing
Before using a PCast object, it is required to be initialized as shown.
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.PCastInitializeOptions;
import com.phenixrts.pcast.android.AndroidPCastFactory;
PCast pcast = AndroidPCastFactory.createPCast(this);
// #1 Default initialization options
pcast.initialize();
// OR
// #2 Custom initialization options (initialized by constructor)
final boolean enableProcessTerminationSignalHandling = false;
PCastInitializeOptions initOptions = new PCastInitializeOptions(
enableProcessTerminationSignalHandling);
pcast.initialize(initOptions);
Initialize Options
Name |
Description |
enableProcessTerminationSignalHandling |
Controls if process termination signal handling is enabled or not |
configureLogging |
Control if phenix logging is enabled |
streamingSourceMapping |
Allows partial override of streaming source URIs |
Stream Source Mapping
Optional field that can be provided with PCastInitializeOptions
. It only has an effect on the subscriber side, and only for streams with the “streaming” capability (on both publisher and subscriber side). The property allows you to redirect requests from the SDKs player to your own CDN.
import com.phenixrts.pcast.PCastInitializeOptions;
import com.phenixrts.pcast.StreamingSourceMapping;
final String patternToReplace = "https:\\/\\/phenixrts\\.com\\/video";
final String replacement = "https://myown.cdn.com";
final StreamingSourceMapping streamingSourceMapping = new StreamingSourceMapping(
patternToReplace, replacement);
final boolean enableProcessTerminationSignalHandling = false;
final boolean configureLogging = true;
final PCastInitializeOptions pcastInitOptions = new PCastInitializeOptions(
enableProcessTerminationSignalHandling,
configureLogging,
streamingSourceMapping);
Name |
Description |
patternToReplace |
A regular expression indicating with part of the incoming streaming source URI to replace |
replacement |
The replacement string to insert into the streaming source URI |
Note: The regular expression has to be properly escaped to work correctly.
Connect and Authenticate
Please follow the self explanatory example for connecting and authenticating to PCast.
import android.util.Log;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.RequestStatus;
// Obtained via Admin API
String authenticationToken = ...;
// Previously initialized
PCast pcast = ...;
pcast.start(
authenticationToken,
new PCast.AuthenticationCallback() {
@Override
public void onEvent(
PCast pcast,
RequestStatus requestStatus,
String sessionId) {
if (requestStatus == RequestStatus.OK) {
Log.i("Phenix SDK Example", "PCast started...");
} else {
Log.e("Phenix SDK Example", "Failed to start PCast...");
}
}
},
new PCast.OnlineCallback() {
@Override
public void onEvent(PCast pcast) {
Log.i("Phenix SDK Example", "We are online...");
}
},
new PCast.OfflineCallback() {
@Override
public void onEvent(PCast pcast) {
Log.i("Phenix SDK Example", "We are offline...");
}
});
Parameters
Name |
Type |
Description |
authenticationToken (required) |
string |
The authentication token generated using the Admin API |
authenticationCallback (required) |
PCast.AuthenticationCallback |
Called upon successful authentication or when authentication failed or has to be redone. Upon successful authentication, the authenticationCallback will be called with status=RequestStatus.OK. If at any time a new authenticationToken is required, then the authenticationCallback is called with status=RequestStatus.UNAUTHORIZED to indicate that we are no longer authenticated. |
onlineCallback (required) |
PCast.OnlineCallback |
Called when the client is connected to the streaming platform |
offlineCallback (required) |
PCast.OfflineCallback |
Called when the client is disconnected from the streaming platform. Ongoing streams may continue while we are temporarily disconnected. However, no new streams can be started while being disconnected. The client automatically tries to reconnect and will call onlineCallback when it succeeds in doing so or eventually call authenticationCallback to indicate that re-authentication is required. |
Authentication Callback Status Codes
Status |
Valid Fields |
Description |
RequestStatus.OK |
sessionId |
Authentication succeeded, the sessionId is populated |
RequestStatus.UNAUTHORIZED |
<none> |
Authentication failed or re-authentication required |
<varies> |
<none> |
Authentication failed for other reasons |
Disconnect
Please follow the self explanatory example for disconnecting from PCast.
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.RequestStatus;
// Previously initialized and started
PCast pcast = ...;
pcast.stop();
// Once you are done using PCast (e.g. exiting the app)
pcast.shutdown();
Please follow the self explanatory example for getting local user media.
import com.phenixrts.pcast.FacingMode;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.UserMediaOptions;
import com.phenixrts.pcast.UserMediaStream;
import com.phenixrts.pcast.android.AndroidPCastFactory;
// Previously initialized and started
PCast pcast = ...;
UserMediaOptions gumOptions = new UserMediaOptions();
// Customize options if desired
gumOptions.getVideoOptions().capabilityConstraints.put(
DeviceCapability.FACING_MODE, Arrays.asList(new DeviceConstraint(FacingMode.USER)));
gumOptions.getAudioOptions().enabled = false;
pcast.getUserMedia(
gumOptions,
new PCast.UserMediaCallback() {
@Override
public void onEvent(
PCast pcast,
RequestStatus status,
UserMediaStream userMediaStream) {
// Check status and store 'userMediaStream'
}
});
Parameters
Name |
Type |
Description |
options (required) |
UserMediaOptions |
The options defining the requested user media stream |
userMediaCallback (required) |
PCast.UserMediaCallback |
Upon acquiring of the user media stream, the userMediaCallback will be called with status=RequestStatus.OK. If the user media is currently used by another application, then you may receive a code status=RequestStatus.CONFLICT . If the operation fails with status=RequestStatus.FAILED then please check the logs for more information |
Device Capability
Constraint Type
Name |
Description |
ConstraintType.MIN |
Hard constraint: Capability must have at least the specified value |
ConstraintType.MAX |
Hard constraint: Capability must have at most the specified value |
ConstraintType.EXACT |
Hard constraint: Capability must have exactly the specified value |
ConstraintType.IDEAL |
Soft constraint: Capability should have specified value, but other values are acceptable (default) |
Facing Mode
Name |
Description |
FacingMode.AUTOMATIC |
Select a facing mode automatically (default) |
FacingMode.ENVIRONMENT |
Facing the surrounding environment (e.g., back camera) |
FacingMode.USER |
Facing the user (e.g., front camera) |
Flash Mode
Only applicable to video devices
Name |
Description |
FlashMode.AUTOMATIC |
Flash is turned on automatically when needed (default) |
FlashMode.ALWAYS_ON |
Flash is on (if available) |
FlashMode.ALWAYS_OFF |
Flash is off |
Device Location
Name |
Description |
Location.AUTOMATIC |
Select any device (default) |
Location.UPPER |
Mounted on top of phone/tablet |
Location.LOWER |
Mounted at bottom of phone/tablet |
Polar Pattern
Only applicable to audio devices
Name |
Description |
PolarPattern.AUTOMATIC |
Automatically select pattern (default) |
PolarPattern.OMNIDIRECTIONAL |
Equally sensitive to sound from any direction |
PolarPattern.CARDIOID |
Most sensitive to sound from the direction in which the data source points and is (nearly) insensitive to sound from the opposite direction |
PolarPattern.SUBCARDIOID |
Most sensitive to sound from the direction in which the data source points and is less sensitive to sound from the opposite direction |
Note: This option is currently not supported on Android
Audio Echo Cancelation Mode
Only applicable to audio devices
Name |
Description |
AudioEchoCancelationMode.AUTOMATIC |
Automatically select AEC mode (default) |
AudioEchoCancelationMode.ON |
Enable AEC if available |
AudioEchoCancelationMode.OFF |
Disable AEC |
Note (as of v2019.2.0): Automatic mode is currently always set to “off” on Android.
Note 2 (as of v2019.2.0): AudioEchoCancelationMode must be set to ON in the UserMediaOptions for all published streams and RendererOptions for all subscribed streams in order for AEC to work reliably on all Android devices.
Updating Options
Sometimes you find it useful to change the camera while a stream is running or just would like to turn on the flash light temporarily.
import com.phenixrts.pcast.UserMediaOptions;
import com.phenixrts.pcast.UserMediaStream;
// Previously obtained via 'getUserMedia'
UserMediaStream userMediaStream = ...;
// Previously initialized and used with 'getUserMedia'
UserMediaOptions gumOptions = new UserMediaOptions();
gumOptions.getVideoOptions().capabilityConstraints.put(
DeviceCapability.FACING_MODE, Arrays.asList(new DeviceConstraint(FacingMode.ENVIRONMENT)));
gumOptions.getVideoOptions().capabilityConstraints.put(
DeviceCapability.FLASH_MODE, Arrays.asList(new DeviceConstraint(FlashMode.ALWAYS_ON)));
gumOptions.getVideoOptions().capabilityConstraints.put(
DeviceCapability.HEIGHT,
Arrays.asList(new DeviceConstraint(720, ConstraintType.EXACT)));
gumOptions.getVideoOptions().capabilityConstraints.put(
DeviceCapability.WIDTH,
Arrays.asList(
new DeviceConstraint(800, ConstraintType.MIN),
new DeviceConstraint(1500, ConstraintType.MAX)));
userMediaStream.applyOptions(gumOptions);
Enumerating Source Devices
You can get a list of available source devices.
import android.util.Log;
import com.phenixrts.pcast.MediaType;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.SourceDeviceInfo;
// Previously initialized and started
PCast pcast = ...;
pcast.enumerateSourceDevices(
MediaType.VIDEO,
new PCast.EnumerateSourceDevicesCallback() {
@Override
public void onEvent(PCast pcast, SourceDeviceInfo[] devices) {
// Store devices as needed
}
});
Parameters
Name |
Type |
Description |
mediaType (required) |
MediaType |
The media type for which to enumerate source devices |
SourceDeviceInfo fields
Name |
Type |
Description |
id |
String |
Source device ID |
name |
String |
Source device Name |
mediaType |
MediaType |
Source device media type |
deviceType |
SourceDeviceType |
Source device type |
facingMode |
FacingMode |
Source device facing mode |
Name |
Description |
MediaType.VIDEO |
Video |
MediaType.AUDIO |
Audio |
Source Device Type
Name |
Description |
SourceDeviceType.NULL |
Null device (e.g. blank screen or silence) |
SourceDeviceType.PHYSICAL |
Physical device (e.g. camera or microphone) |
SourceDeviceType.SYSTEM_OUTPUT |
System output capture (screencast) |
Screencasting android 5.0 or later
You can also stream the screen of your phone.
In order to do this, you must first enumerate the available source devices to find the screencast device ID.
Find the screencast device ID
import android.util.Log;
import com.phenixrts.pcast.MediaType;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.SourceDeviceInfo;
import com.phenixrts.pcast.SourceDeviceType;
// Previously initialized and started
PCast pcast = ...;
pcast.enumerateSourceDevices(
MediaType.VIDEO,
new PCast.EnumerateSourceDevicesCallback() {
@Override
public void onEvent(PCast pcast, SourceDeviceInfo[] devices) {
for (SourceDeviceInfo info : devices) {
if (info.sourceDeviceType == SourceDeviceType.SYSTEM_OUTPUT) {
Log.i("Phenix SDK Example", "Screencasting is available");
String screenCaptureDeviceId = info.id;
// Store screenCaptureDeviceId
}
}
}
});
pcast.enumerateSourceDevices();
The screen capture device requires a valid android.media.projection.MediaProjection object to be passed to the SDK before it can be used. The MediaProjection must stay valid for the duration of the screencast.
Next, pass a valid android.media.projection.MediaProjection object
import android.media.projection.MediaProjection;
import com.phenixrts.pcast.android.AndroidPCastFactory;
// Previously obtained by calling
// android.media.projection.MediaProjectionManager.getMediaProjection(),
// see Android API reference.
MediaProjection mediaProjection = ...;
AndroidPCastFactory.setMediaProjection(mediaProjection);
Finally, pass the screen capture device ID
Next, pass the screen capture device ID to getUserMedia() in the user media options.
import com.phenixrts.pcast.FacingMode;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.UserMediaOptions;
import com.phenixrts.pcast.UserMediaStream;
// Previously initialized and started
PCast pcast = ...;
// Found by calling enumerateSourceDevices() as described above
String screenCaptureDeviceId = ...;
UserMediaOptions gumOptions = new UserMediaOptions();
gumOptions.getVideoOptions().capabilityConstraints.put(
DeviceCapability.DEVICE_ID, Arrays.asList(new DeviceConstraint(screenCaptureDeviceId)));
pcast.getUserMedia(
gumOptions,
new PCast.UserMediaCallback() {
@Override
public void onEvent(
PCast pcast,
RequestStatus status,
UserMediaStream userMediaStream) {
// Check status and store 'userMediaStream'
}
});
Please follow the self explanatory code snippets for understanding above steps.
Publish a Stream
import android.util.Log;
import com.phenixrts.pcast.MediaStream;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.Publisher;
import com.phenixrts.pcast.RequestStatus;
import com.phenixrts.pcast.StreamEndedReason;
import com.phenixrts.pcast.UserMediaOptions;
import com.phenixrts.pcast.UserMediaStream;
// Previously initialized and started
PCast pcast = ...;
// Previously obtained via Admin API
String streamToken = ...;
// Previously obtained via either PCast.subscribe()
// or UserMediaStream.getMediaStream()
MediaStream mediaStream = ...;
String[] tags = { "my-tag" };
pcast.publish(
streamToken,
mediaStream,
new PCast.PublishCallback() {
@Override
public void onEvent(
PCast pcast,
RequestStatus requestStatus,
Publisher publisher) {
// Check status and store 'publisher'
// The "streamId" of the publisher
String streamId = publisher.getStreamId();
if (publisher.hasEnded()) {
// Checks if the publisher has ended
}
// Attach publisher ended callback
publisher.setPublisherEndedCallback(
new Publisher.PublisherEndedCallback() {
@Override
public void onEvent(
Publisher publisher,
StreamEndedReason reason,
String reasonDescription) {
// Called when the stream has ended
Log.i("Phenix SDK Example",
"Publish stream ended with reason ["
+ reasonDescription + "]");
}
});
// To stop later
publisher.stop("I-am-done-publishing");
}
},
tags);
Parameters
Name |
Type |
Description |
streamToken (required) |
String |
The publish token is generated using the Admin API |
mediaStream (required) |
MediaStream |
The user media stream acquired through PCast.subscribe(…) or locally with UserMediaStream.getMediaStream() |
publishCallback (required) |
PCast.PublishCallback |
Called upon completion of the operation |
tags (optional) |
String[] |
Tags that will be provided with the stream notifications to your backend callback endpoint |
StreamEndedReason
Reason |
Description |
StreamEndedReason.ENDED |
The stream ended normally |
StreamEndedReason.FAILED |
The stream failed |
StreamEndedReason.CENSORED |
The stream was censored |
StreamEndedReason.MAINTENANCE |
A maintenance event caused this stream to be terminated |
StreamEndedReason.CAPACITY |
The stream was terminated due to capacity limitations |
StreamEndedReason.APP_BACKGROUND |
The stream was terminated due to the mobile app entering into the background |
StreamEndedReason.CUSTOM |
A custom termination reason is provided in the “reasonDescription” field |
Process Raw Frames
The frame-ready API allows for pre-processing of raw audio and video frames before they are encoded and transmitted.
Any processing needs to keep up with the incoming frame rate; otherwise some of the incoming frames will be dropped to compensate.
import android.graphics.Bitmap;
import com.phenixrts.media.audio.android.AndroidAudioFrame;
import com.phenixrts.media.video.android.AndroidVideoFrame;
import com.phenixrts.pcast.FrameNotification;
import com.phenixrts.pcast.MediaStreamTrack;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.UserMediaStream;
import com.phenixrts.pcast.android.AndroidReadAudioFrameCallback;
import com.phenixrts.pcast.android.AndroidReadVideoFrameCallback;
// Previously obtained via PCast.getUserMedia
final UserMediaStream userMediaStream = ...;
final MediaStreamTrack[] videoTracks = userMediaStream.getMediaStream().getVideoTracks();
for (MediaStreamTrack videoTrack : videoTracks) {
userMediaStream.setFrameReadyCallback(videoTrack,
(frameNotification) -> {
frameNotification.read(new AndroidReadVideoFrameCallback() {
@Override
public void onVideoFrameEvent(AndroidVideoFrame videoFrame) {
final Bitmap pixelBuffer = videoFrame.bitmap;
final int displayHeightInPixels = pixelBuffer.getHeight();
final int displayWidthInPixels = pixelBuffer.getWidth();
// Manipulate bitmap as needed
...
frameNotification.write(videoFrame);
}
});
});
}
final MediaStreamTrack[] audioTracks = userMediaStream.getMediaStream().getAudioTracks();
for (MediaStreamTrack audioTrack : audioTracks) {
userMediaStream.setFrameReadyCallback(audioTrack,
(frameNotification) -> {
frameNotification.read(new AndroidReadAudioFrameCallback() {
@Override
public void onAudioFrameEvent(AndroidAudioFrame audioFrame) {
// Attenuate audio:
for (int sampleIndex = 0; sampleIndex < audioFrame.audioSamples.length; ++sampleIndex) {
audioFrame.audioSamples[sampleIndex] *= 0.1;
}
frameNotification.write(audioFrame);
}
});
});
}
Frame Notification API
Name |
Argument |
Description |
read |
FrameReadyForProcessingCallback |
Retrieves current raw frame in form of an AndroidAudioFrame or AndroidVideoFrame |
write |
AndroidAudioFrame/AndroidVideoFrame |
Writes back a processed or newly generated frame. The frame can have a different resolution and timestamps |
drop |
(none) |
Instructs stream to drop the current frame |
AndroidAudioFrame
Name |
Type |
Description |
sampleRateInHz |
int |
Audio sampling frequency, e.g. 48000 |
numberOfChannels |
int |
Number of channels, e.g. 1 for mono, 2 for stereo |
timestampInMicroseconds |
long |
Timestamp of current audio frame in us |
audioSamples |
short[] |
Audio samples; if more than one channel is present, then samples will be interleaved |
AndroidVideoFrame
Name |
Type |
Description |
bitmap |
android.graphics.Bitmap |
Bitmap containing the pixel data. This object also contains other properties such as width and height. |
timestampInMicroseconds |
long |
Timestamp of current video frame in us |
durationInMicroseconds |
long |
Duration of the current video frame in us |
Limit Bitrate
The published video bitrate can be limited temporarily if needed. The returned disposable allows you to control for how long the limitation should stay in effect. If limitBandwidth
is called multiple times before any of the previous disposables are released, then only the most recent override will remain in effect until its disposable is released. Relasing any of the disposables from earlier limitBandwidth
calls will have no effect.
To release a Disposable deterministrically, call dispose
on it.
import com.phenixrts.common.Disposable;
import com.phenixrts.pcast.Publisher;
// Previously obtained
Publisher publisher = ...;
Disposable disposable = publisher.limitBandwidth(200000);
// ... after some time: force dispose to cancel bandwidth limitation:
disposable.dispose();
Parameters
Name |
Type |
Description |
bandwidthLimitInBps (required) |
long |
Maximum bitrate limit in bps for video |
Subscribe to a Stream
import android.util.Log;
import com.phenixrts.pcast.MediaStream;
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.RequestStatus;
import com.phenixrts.pcast.StreamEndedReason;
// Previously initialized and started
PCast pcast = ...;
// Previously obtained via Admin API
String streamToken = ...;
pcast.subscribe(
streamToken,
new PCast.SubscribeCallback() {
@Override
public void onEvent(
PCast pcast,
RequestStatus requestStatus,
MediaStream mediaStream) {
// Check status and store 'mediaStream'
// Attach stream ended callback
mediaStream.setStreamEndedCallback(
new MediaStream.StreamEndedCallback() {
@Override
public void onEvent(
MediaStream mediaStream,
StreamEndedReason reason,
String reasonDescription) {
Log.i("Phenix SDK Example",
"Subscriber stream ended with reason ["
+ reasonDescription + "]");
}
}
});
// To stop later
mediaStream.stop();
}
});
Parameters
Name |
Type |
Description |
streamToken (required) |
String |
The publish token is generated using the Admin API |
subscribeCallback (required) |
PCast.SubsribeCallback |
Called upon completion of the operation |
View a Stream
In order to view a stream you have to supply a Surface for the Renderer to draw on.
import com.phenixrts.pcast.MediaStream;
import com.phenixrts.pcast.Renderer;
import com.phenixrts.pcast.RendererStartStatus;
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
// Previously obtained via either PCast.subscribe
// or PhenixUserMediaStream.mediaStream
MediaStream mediaStream = ...;
// Previously obtained, e.g., from a SurfaceView
Surface renderSurface = ...;
Renderer renderer = mediaStream.createRenderer();
RendererStartStatus status =
renderer.start(new AndroidVideoRenderSurface(renderSurface));
if (status == RendererStartStatus.OK) {
Log.i("Phenix SDK Example", "Renderer started successfully");
} else {
Log.e("Phenix SDK Example", "Renderer start failed");
}
// To stop later
renderer.stop();
Renderer options
It is possible to pass additional options when creating a renderer.
import com.phenixrts.pcast.AspectRatioMode;
import com.phenixrts.pcast.MediaStream;
import com.phenixrts.pcast.Renderer;
import com.phenixrts.pcast.RendererOptions;
// Previously obtained via either PCast.subscribe or UserMediaStream.mediaStream
MediaStream mediaStream = ...;
RendererOptions options = new RendererOptions();
options.aspectRatioMode = AspectRatioMode.FILL;
Renderer renderer = mediaStream.createRenderer(options);
Properties
Aspect Ratio Mode
Name |
Description |
AspectRatioMode.AUTOMATIC |
Defaults to fill |
AspectRatioMode.FILL |
Fill entire render area. Video may be truncated |
AspectRatioMode.LETTERBOX |
Black bars are added on sides or top/bottom of render area, video will not be truncated |
Note: This option is currently only supported for Real-Time streams
Hardware Accelerated Decoding Mode
Name |
Description |
HardwareAcceleratedDecodingMode.AUTOMATIC |
Use hardware decoding on certified devices |
HardwareAcceleratedDecodingMode.ON |
Always use hardware decoding |
HardwareAcceleratedDecodingMode.OFF |
Always use software decoding |
import com.phenixrts.pcast.Renderer;
import com.phenixrts.pcast.UserMediaStream;
// Previously obtained via 'getUserMedia'
UserMediaStream userMediaStream = ...;
Renderer renderer = userMediaStream.getMediaStream().createRenderer();
Muting and Unmuting of Audio
import com.phenixrts.pcast.Renderer;
// Previously obtained from media stream
Renderer renderer = ...;
boolean isMuted = renderer.isAudioMuted();
renderer.muteAudio();
renderer.unmuteAudio();
Taking a Screenshot
If you would like to show a preview, you can take a still image from a renderer.
import com.phenixrts.pcast.Renderer;
import com.phenixrts.android.AndroidLastFrameRenderedCallback;
// Previously obtained from media stream and started
Renderer renderer = ...;
renderer.setLastVideoFrameRenderedReceivedCallback(
new AndroidLastFrameRenderedCallback() {
@override
public void onEvent(Renderer renderer, Bitmap videoFrame) {
// Process the frame as needed
}
});
renderer.requestLastVideoFrameRendered();
You can also take a still image from a local stream.
import com.phenixrts.pcast.UserMediaStream;
import com.phenixrts.android.AndroidLastFrameCapturedCallback;
// Previously obtained via 'getUserMedia'
UserMediaStream userMediaStream = ...;
userMediaStream.setLastVideoFrameCapturedReceivedCallback(
new AndroidLastFrameCapturedCallback() {
@override
public void onEvent(Renderer renderer, Bitmap videoFrame) {
// Process the frame as needed
}
});
userMediaStream.requestLastVideoFrameCaptured();
Data Quality Feedback
If you like to show the user feedback about how their internet connectivity affects the stream quality, you can listen for data quality notifications.
import com.phenixrts.pcast.DataQualityReason;
import com.phenixrts.pcast.DataQualityStatus;
import com.phenixrts.pcast.Publisher;
import com.phenixrts.pcast.Renderer;
// Previously obtained from media stream and started
Renderer renderer = ...;
renderer.setDataQualityChangedCallback(
new Renderer.DataQualityChangedCallback() {
@Override
public void onEvent(
Renderer renderer,
DataQualityStatus quality,
DataQualityReason reason) {
// Inform user, take action based on status and reason
}
});
// Previously obtained via PCast.publish
Publisher publisher = ...;
publisher.setDataQualityChangedCallback(
new Publisher.DataQualityChangedCallback() {
@Override
public void onEvent(
Publisher publisher,
DataQualityStatus quality,
DataQualityReason reason) {
// Inform user, take action based on status and reason
}
});
Data Quality Status for Publishers
Status |
Reason |
Description |
DataQualityStatus.NO_DATA |
DataQualityReason.NONE |
The publisher has a bad internet connection and no data is being streamed. |
DataQualityStatus.NO_DATA |
DataQualityReason.UPLOAD_LIMITED |
The publisher has a bad internet connection and no data is being streamed. |
DataQualityStatus.ALL |
DataQualityReason.NONE |
Good internet connection and no quality reduction in effect. |
DataQualityStatus.ALL |
DataQualityReason.UPLOAD_LIMITED |
The publisher has a slow internet connection and the quality of the stream is reduced. |
DataQualityStatus.ALL |
DataQualityReason.NETWORK_LIMITED |
Subscribers have bad internet connections and the quality of the stream is reduced. |
DataQualityStatus.AUDIO_ONLY |
DataQualityReason.UPLOAD_LIMITED |
The publisher has a bad internet connection and only audio is streamed. |
DataQualityStatus.AUDIO_ONLY |
DataQualityReason.NETWORK_LIMITED |
Subscribers have bad internet connections and only audio is streamed. |
Data Quality Status for Viewers
Status |
Reason |
Description |
DataQualityStatus.NO_DATA |
DataQualityReason.NONE |
The subscriber has a bad internet connection and no data is being received. |
DataQualityStatus.NO_DATA |
DataQualityReason.DOWNLOAD_LIMITED |
The subscriber has a bad internet connection and no data is being received. |
DataQualityStatus.NO_DATA |
DataQualityReason.PUBLISHER_LIMITED |
The publisher has a bad internet connection and no data is being received. |
DataQualityStatus.NO_DATA |
DataQualityReason.NETWORK_LIMITED |
The network is limiting the quality of the stream and no data is being received. |
DataQualityStatus.ALL |
DataQualityReason.NONE |
Good internet connection and no quality reduction in effect. |
DataQualityStatus.ALL |
DataQualityReason.DOWNLOAD_LIMITED |
The subscriber has a bad internet connection and the quality of the stream is reduced. |
DataQualityStatus.ALL |
DataQualityReason.PUBLISHER_LIMITED |
The publisher has a bad internet connection and the quality of the stream is reduced. |
DataQualityStatus.ALL |
DataQualityReason.NETWORK_LIMITED |
Other subscribers have bad internet connections and the quality of the stream is reduced. |
DataQualityStatus.AUDIO_ONLY |
DataQualityReason.NONE |
Audio only stream, good internet connection and no quality reduction in effect. |
DataQualityStatus.AUDIO_ONLY |
DataQualityReason.DOWNLOAD_LIMITED |
The subscriber has a bad internet connection and is only receiving audio. |
DataQualityStatus.AUDIO_ONLY |
DataQualityReason.PUBLISHER_LIMITED |
The publisher has a bad internet connection and the subscriber is only receiving audio. |
DataQualityStatus.AUDIO_ONLY |
DataQualityReason.NETWORK_LIMITED |
The network is limiting the quality of the stream and the subscriber is only receiving audio. |
Handling Dimension Changes
Cameras may be switched at runtime and devices may be rotated. Register a handler to receive a notification whenever the video dimension changes.
import com.phenixrts.pcast.Dimensions;
import com.phenixrts.pcast.Renderer;
// Previously obtained from media stream
Renderer renderer = ...;
renderer.setVideoDisplayDimensionsChangedCallback(
new Renderer.VideoDisplayDimensionsChangedCallback() {
@Override
public void onEvent(
Renderer renderer,
Dimensions displayDimensions) {
// Can get called multiple times while rendering a stream
// Values in displayDimensions.width, displayDimensions.height
}
});
Room Service
Set up a room and manage members, streams, and chat. Some typical uses for rooms include:
- Broadcast video chats between multiple people - few to many
- Large-scale video chat
- Conference calls with integrated video chat
- Real-time web channels for hosting media content. See View a Channel with the Express API
- Town hall meetings
To get started, create a room of the type associated with your need.
Initializing
First you will need to initialize a room service object. This object is used for creating, joining, and leaving a room. As well, use this object for managing the room you enter (active room), members, and the model of your Self.
Note: PCast must be initialized and started when invoking createRoomService
so that a valud session ID is present.
import android.content.Context;
import com.phenixrts.environment.android.AndroidContext;
import com.phenixrts.pcast.PCast;
import com.phenixrts.room.RoomService;
import com.phenixrts.room.RoomServiceFactory;
// IMPORTANT: Before accessing any of the static factories, make sure the context is passed to Phenix:
final Context context = ...; // e.g. Activity.getApplication();
AndroidContext.setContext(context);
final PCast pcast = ...; // previously obtained
final RoomService roomService = RoomServiceFactory.createRoomService(pcast);
Parameters
Name |
Type |
Description |
pcast (required) |
PCast |
Instantiated PCast object. Must already be authenticated |
RoomService
Name |
Signature |
Returns |
Description |
getRoomInfo |
(roomId, alias, callback) |
Room |
See Get Room Info |
createRoom |
(room, callback) |
Room |
See Create a Room |
joinRoom |
(roomId, alias, callback) |
Room |
See Join a Room |
leaveRoom |
(callback) |
void |
See Leave a Room |
destroyRoom |
(callback) |
void |
See Destroy a Room |
getSelf |
() |
Observable of Member) |
Observable of self member |
getObservableActiveRoom |
() |
Observable of Room) |
Observable of the active room |
Instantiate Self Member Model
Before entering a room the RoomService must have a valid model of the Self member. The Self member model may be updated at any time by changing observable values and calling commitChanges. See Update Self Model.
Update Self Model
Broadcast local changes of the Self model to all the members in the room. These changes are sent to our server which forwards the changes to all members in the room. This works whether or not you are currently in a room.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Member;
import com.phenixrts.room.MemberRole;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
final Member selfMember = roomService.getSelf();
selfMember.getObservableScreenName().setValue("My New Screen Name");
selfMember.getObservableRole().setValue(MemberRole.AUDIENCE);
// Commit changes to self if already in a room
selfMember.commitChanges((RequestStatus status, String message) -> {
if (status == RequestStatus.OK) {
// Successfully updated self
} else {
// Handle error
}
});
To undo uncommitted local changes, use reload:
import com.phenixrts.room.Member;
import com.phenixrts.room.MemberRole;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
final Member selfMember = roomService.getSelf();
selfMember.getObservableScreenName().setValue("My New Screen Name");
selfMember.getObservableRole().setValue(MemberRole.AUDIENCE);
// Decided to undo changes
selfMember.reload();
Update Self Callback Arguments
Name |
Type |
Description |
status |
RequestStatus |
The status of the operation |
message |
String |
Error message, if applicable |
Set Self Streams
The self model must have at least one stream when joining the room as any member role besides Audience.
import com.phenixrts.room.Member;
import com.phenixrts.room.RoomService;
import com.phenixrts.room.Stream;
import com.phenixrts.room.StreamType;
import com.phenixrts.room.TrackState;
final RoomService roomService = ...; // previously obtained
final Member selfMember = roomService.getSelf();
final Stream selfStream = roomService.createStream(
"http://myuri.stream", StreamType.USER, TrackState.ENABLED, TrackState.ENABLED);
selfMember.getObservableStreams().setValue(new Stream[] {selfStream});
// Now commit changes
Parameters
Name |
Type |
Description |
uri (required) |
String |
Unique identifier for stream. |
type (required) |
StreamType |
Type of stream - see Stream Types |
audioState (required) |
TrackState |
State of the audio track - see Stream Track States |
videoState (required) |
TrackState |
State of the video track - see Stream Track States |
Get Room Info
Get room info with the provided room details without joining the room.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
// Either provide alias or roomId, other can be null or empty
final String roomId = "us-central#demo#exampleRoomId";
final String alias = null;
roomService.getRoomInfo(roomId, alias, (RoomService roomService, RequestStatus status, Room room) -> {
if (status == RequestStatus.OK) {
// Do something with room
} else {
// Handle error
}
});
Parameters
Name |
Type |
Description |
roomId (required) |
String |
ID of the room |
alias (optional) |
String |
Alternative to roomId - alias of the room |
getRoomInfoCallback (required) |
RoomService.GetRoomInfoCallback |
Callback with the status of the request and the room model |
Get Room Info Callback Arguments
Name |
Type |
Description |
roomService |
RoomService |
The room service making the callback |
status |
RequestStatus |
The status of the operation |
room |
Room |
Immutable room object |
Get Room Info Callback Status Codes
Status |
Description |
ok |
Get room info succeeded |
not-found |
Get room info failed. Room does not exist - verify room data or create the room |
<varies> |
Get room info failed for other reasons |
Create a Room
Create a room with the provided details
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomOptions;
import com.phenixrts.room.RoomService;
import com.phenixrts.room.RoomServiceFactory;
import com.phenixrts.room.RoomType;
final RoomService roomService = ...; // previously obtained
final String roomName = "My Room Name";
final String description = "Deep thoughts only";
final RoomOptions roomOptions = RoomServiceFactory.createRoomOptionsBuilder()
.withName(roomName)
.withDescription(description)
.withType(RoomType.MULTI_PARTY_CHAT)
.buildRoomOptions();
roomService.createRoom(
roomOptions,
(RoomService service, RequestStatus status, Room room) -> {
if (status == RequestStatus.OK) {
// Do something with room
} else {
// Handle error
}
});
Parameters
Name |
Type |
Description |
options (required) |
RoomOptions |
Room creation options |
callback (required) |
RoomService.CreateRoomCallback |
Callback with the status of the request and the room model |
Create Room Callback Arguments
Name |
Type |
Description |
roomService |
RoomService |
The room service making the callback |
status |
RequestStatus |
The status of the operation |
room |
Room |
Immutable room object |
Create Room Callback Status Codes
Status |
Description |
ok |
Create room succeeded. This can also mean that the room already existed |
<varies> |
Create room failed for other reasons |
RoomOptionsBuilder
Name |
Type |
Default |
Description |
withName (required) |
String |
|
Name of room |
withType (required) |
RoomType |
|
Room type |
withAlias (optional) |
String |
<generated> |
Room Alias. If not passed in, it will be generated for you |
withDescription (optional) |
String |
<empty> |
Room description |
buildRoomOptions |
<none> |
|
Builds the RoomOptions |
Join a Room
Join a room with the Self model. After successfully joining a room that room becomes the active room and can be returned via the getObservableActiveRoom method.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
// Either provide alias or roomId, other can be null or empty
final String roomId = null;
final String alias = "exampleRoom";
roomService.joinRoom(roomId, alias, (RoomService roomService, RequestStatus status, Room room) -> {
if (status == RequestStatus.OK) {
// Do something with room
} else {
// Handle error
}
});
Parameters
Name |
Type |
Description |
roomId (required) |
String |
ID of the room |
alias (optional) |
String |
Alternative to roomId - alias of the room |
callback (required) |
RoomService.JoinRoomCallback |
Callback with the status of the request and the room model |
Join Room Callback Arguments
Name |
Type |
Description |
roomService |
RoomService |
The room service making the callback |
status |
RequestStatus |
The status of the operation |
room |
Room |
Immutable room object |
Join Room Callback Status Codes
Status |
Description |
ok |
Join room succeeded |
not-found |
Join room failed. Room does not exist - create room first |
<varies> |
Join room failed for other reasons |
Subscribe to Room Member Streams
After entering a room you may use the active room to get the observable members and associated metadata. Here we provide an example for accessing each member’s streams whenever the list of members is updated. Remember: Since each observable behaves like a Behavior Rx Subject, your subscriber will always get called back immediately with the current value (in this case the list of members), see Observable.
import com.phenixrts.common.Disposable;
import com.phenixrts.common.Observable;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Member;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomService;
import com.phenixrts.room.Stream;
final RoomService roomService = ...; // previously obtained
final Observable<Room> observableActiveRoom = roomService.getObservableActiveRoom();
// For this simple example, we assume that there is an active room.
// In a production environment, this can return null if we have not currently joined a room. In that case
// you would want to listen as a subscriber on the 'observableActiveRoom' for updates
final Room activeRoom = observableActiveRoom.getValue();
final Observable<Member[]> observableMembers = activeRoom.getObservableMembers();
final Disposable subscription = observableMembers.subscribe((change) -> {
final Member[] currentMembers = (Member[])change;
for (Member member : currentMembers) {
final Stream[] currentStreams = member.getObservableStreams().getValue();
// Now do something with this member's streams
}
});
// The subscription will remain active for as long as 'subscription' is kept alive or not disposed
Update Room Model
Broadcast local changes of the Room model to all the members in the room. These changes are sent to our server which forwards the changes to all members in the room.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
final Room room = roomService.getObservableActiveRoom().getValue();
room.getObservableDescription().setValue("My New Room Description");
room.commitChanges((RequestStatus status, String message) -> {
if (status == RequestStatus.OK) {
// Successfully updated room
} else {
// Handle error (`message` may provide more information about what went wrong)
}
});
Parameters
Name |
Type |
Description |
callback (required) |
Room.CommitCallback |
Callback with the status of the request |
Update Room Callback Arguments
Name |
Type |
Description |
status |
RequestStatus |
The status of the operation |
message |
String |
Error message, if applicable |
Update Room Callback Status Codes
Status |
Description |
ok |
Update room succeeded |
<varies> |
Update room failed for other reasons |
Leave a Room
Leave the current active room. Self is not modified.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
roomService.leaveRoom((RoomService roomService, RequestStatus status) -> {
if (status == RequestStatus.OK) {
// Successfully left room, no action required
} else {
// Handle error
}
});
Parameters
Name |
Type |
Description |
callback (required) |
RoomService.LeaveRoomCallback |
Callback with the status of the request |
Leave Room Callback Arguments
Name |
Type |
Description |
roomService |
RoomService |
The room service making the callback |
status |
RequestStatus |
The status of the operation |
Update Room Callback Status Codes
Status |
Description |
ok |
Leave room succeeded |
<varies> |
Leave room failed for other reasons |
Destroy a Room
Destroys the current active room assuming your self member has the permissions.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.room.Room;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
roomService.destroyRoom((RoomService roomService, RequestStatus status) -> {
if (status == RequestStatus.OK) {
// Successfully destroyed room, no action required
} else {
// Handle error
}
});
Parameters
Name |
Type |
Description |
callback (required) |
RoomService.DestroyRoomCallback |
Callback with the status of the request |
Leave Room Callback Arguments
Name |
Type |
Description |
roomService |
RoomService |
The room service making the callback |
status |
RequestStatus |
The status of the operation |
Update Room Callback Status Codes
Status |
Description |
ok |
Destroy room succeeded |
<varies> |
Destroy room failed for other reasons |
Observe Room size
Subscribe to observable to receive periodic room size updates. The room size corresponds to the total number of members currently in the room, which does include audience members.
This is a so called cold observable, which means that you may not immediately receive an update. It also means that both getValue
and setValue
methods will cause a failure.
Updates will be provided for as long as the disposable
reference is kept alive.
import com.phenixrts.common.Disposable;
import com.phenixrts.room.ImmutableRoom;
final ImmutableRoom room = ... // previously obtained
final Disposable disposable = room.getObservableEstimatedSize().subscribe((change) -> {
final Long currentEstimatedRoomSize = (Long) change;
// E.g. update UI
});
Room Model
The following details technical specifications for the Room, Members, and Streams.
ImmutableRoom
Name |
Signature |
Returns |
Description |
getRoomId |
() |
String |
The room ID |
getObservableAlias |
() |
Observable(of String) |
Observable of room alias |
getObservableName |
() |
Observable(of String) |
Observable of room name |
getObservableDescription |
() |
Observable(of String) |
Observable of room description |
getObservableType |
() |
Observable(of RoomType) |
Observable of room type |
getObservableMembers |
() |
Observable(of Member[]) |
Observable of array of visible members in the room |
getObservableBridgeId |
() |
Observable(of String) |
Observable of room bridgeId |
getObservablePin |
() |
Observable(of String) |
Observable of room pin |
getObservableEstimatedSize |
() |
Observable(of Long) |
Observable of estimated room size. This is a cumulative count of all members in the room, including audience members |
Room
Room extends ImmutableRoom
Name |
Signature |
Returns |
Description |
commitChanges |
(Room.CommitCallback) |
void |
Attempts to update server room model with any changes made locally |
reload |
() |
void |
Reverts all local changes to the server room model |
Room Types
Type |
Description |
RoomType.DIRECT_CHAT |
One to one chat |
RoomType.MULTI_PARTY_CHAT |
Many to many or few to many chat |
RoomType.MODERATED_CHAT |
Moderated chat with integrated SIP Bridge for conferencing abilities (join by phone). |
RoomType.TOWN_HALL |
Chat with a few active participants or presenters and many audience or inactive participants |
RoomType.CHANNEL |
View only room with a single active member at a time. Broadcast content to many viewers. |
Member
Name |
Signature |
Returns |
Description |
getSessionId |
() |
String |
The session ID of the member in the room |
getObservableRole |
() |
Observable(of MemberRole) |
Observable of member role |
getObservableState |
() |
Observable(of MemberState) |
Observable of member state |
getObservableScreenName |
() |
Observable(of String) |
Observable of member screen name |
getObservableStreams |
() |
Observable(of Stream[]) |
Observable of array of streams belonging to the member |
getObservableLastUpdate |
() |
Observable(of Date) |
Observable of utc timestamp |
commitChanges |
(Member.CommitCallback) |
void |
Attempts to update server member model with any changes made locally |
reload |
() |
void |
Reverts all local changes to the server member model |
Member Roles
Role |
Description |
MemberRole.PARTICIPANT |
Member with streams and is visible to all members |
MemberRole.AUDIENCE |
Member with no streams and is not visible to other members |
MemberRole.PRESENTER |
Presenter participant member |
MemberRole.MODERATOR |
Privileged participant member |
Member States
Role |
Description |
MemberState.ACTIVE |
Member is active |
MemberState.PASSIVE |
Member is not active |
MemberState.HAND_RAISED |
Member is requesting to become active |
MemberState.INACTIVE |
Member is not away |
MemberState.OFFLINE |
Member has left the room |
Stream
The stream object may be used to represent any value that will be shared between all members.
Name |
Signature |
Returns |
Description |
getUri |
() |
String |
The stream uri (may or may not be associated with phenix stream ID) |
getType |
() |
StreamType |
The stream type |
getObservableAudioState |
() |
Observable(of TrackState) |
Observable of stream video state |
getObservableVideoState |
() |
Observable(of TrackState) |
Observable of stream audio state |
Stream Types
Type |
Description |
StreamType.USER |
General purpose stream |
StreamType.PRESENTATION |
Privileged stream |
StreamType.AUDIO |
Audio-only stream |
Stream Track States
State |
Description |
TrackState.ENABLED |
Track is enabled |
TrackState.DISABLED |
Track is disabled |
TrackState.ENDED |
Track has ended |
Chat Service
The Chat Service provides the ability to:
- Send messages to a room
- Receive messages from a room
- Get a room’s chat message history
All room members that are subscribed to the chat will receive the text messages. You must first enter the room before you may send and receive messages.
Initializing
First, instantiate the chat service. A Room Service is required to obtain the chat service.
import android.content.Context;
import com.phenixrts.chat.RoomChatService;
import com.phenixrts.chat.RoomChatServiceFactory;
import com.phenixrts.environment.android.AndroidContext;
import com.phenixrts.room.RoomService;
// IMPORTANT: Before accessing any of the static factories, make sure the context is passed to Phenix:
final Context context = ...; // e.g. Activity.getApplication();
AndroidContext.setContext(context);
final RoomService roomService = ...; // previously obtained
final RoomChatService chatService = RoomChatServiceFactory.createRoomChatService(roomService);
// Use a custom batch size, i.e. max number of message returned by 'getObservableChatMessages'
final int batchSize = 10;
final RoomChatService chatServiceWithBatchSize = RoomChatServiceFactory.createRoomChatService(
roomService, batchSize);
RoomChatService
Name |
Signature |
Returns |
Description |
getObservableChatMessages |
() |
Observable(of ChatMessage[]) |
Get the observable of the most recent chat messages. This is a buffer of the most recent N messages up to a max of 100. See Listen for Chat Messages |
getObservableChatEnabled |
() |
Observable(of Boolean) |
Get the observable of the enabled status of the chat service. Use this to disable or enable the chat. |
sendMessageToRoom |
(message, callback) |
void |
Send a message to room. |
getMessages |
(batchSize, afterMessageId, beforeMessageId, callback) |
void |
Get chat message history |
ChatMessage
Name |
Type |
Description |
getMessageId |
String |
Unique ID of message |
getObservableTimeStamp |
Observable(of Date) |
Server UTC timestamp of message |
getObservableFrom |
Observable(of ChatUser) |
Information on the member that sent the message |
getObservableMessage |
Observable(of String) |
Chat message |
ChatUser
Name |
Type |
Description |
getSessionId |
String |
Member’s session ID |
getObservableScreenName |
Observable(of String) |
Screen name of the member |
getObservableMemberRole |
Observable(of MemberRole) |
Member’s role. See Member Roles |
getObservableLastUpdate |
Observable(of Date) |
Last time member was updated as UTC timestamp |
Listen for Chat Messages in the Active Room
The chat service exposes an observable of the most recent N (batchSize) messages in the active room up to a max of 100 messages. To view older messages you will need to use the getMessages method.
import com.phenixrts.common.Disposable;
import com.phenixrts.common.Observable;
import com.phenixrts.chat.ChatMessage;
import com.phenixrts.chat.RoomChatService;
import com.phenixrts.chat.RoomChatServiceFactory;
import com.phenixrts.room.RoomService;
final RoomService roomService = ...; // previously obtained
final int batchSize = 10;
final RoomChatService chatService = RoomChatServiceFactory.createRoomChatService(roomService, batchSize);
final Observable<ChatMessage[]> chatMessageObservable = chatService.getObservableChatMessages();
final Disposable subscription = chatMessageObservable.subscribe((change) -> {
final ChatMessage[] messages = (ChatMessage[])change;
// Do something with chat messages
});
Send a Message to the Active Room
Send a single message to the active room. If the message is successfully sent the chatMessageObservable will trigger notifications on all subscribers with the new chat message.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.chat.ChatMessage;
import com.phenixrts.chat.RoomChatService;
final RoomChatService chatService = ...; // previously obtained
// Without callback:
chatService.sendMessageToRoom("My First Message");
// With callback
chatService.sendMessageToRoom("My First Message", (RequestStatus status, String message) -> {
if (status == RequestStatus.OK) {
// Successfully sent
} else {
// handle error - send message again
// (`message` may provide more information about what went wrong)
}
});
Parameters
Name |
Type |
Description |
message (required) |
String |
Message to send to the room |
sendMessageCallback (optional) |
RoomChatService.SendMessageCallback |
Callback with the status of the request |
Send Message Callback Arguments
Name |
Type |
Description |
status |
RequestStatus |
The status of the operation |
message |
String |
Optional additional error status in case of a failure |
Send Message Callback Status Codes
Status |
Description |
ok |
Send message succeeded |
<varies> |
Send message failed for other reasons |
Chat Message History
Get messages older than the most recent N buffered in the chatMessageObservable.
Get the 10 most recent messages after a message:
import com.phenixrts.common.RequestStatus;
import com.phenixrts.chat.ChatMessage;
import com.phenixrts.chat.RoomChatService;
final RoomChatService chatService = ...; // previously obtained
final int batchSize = 10;
final String afterMessageId = "MessageId1";
final String beforeMessageId = null;
chatService.getMessages(
batchSize,
afterMessageId,
beforeMessageId,
(RoomChatService chatService, ChatMessage[] messages, RequestStatus status) -> {
if (status == RequestStatus.OK) {
// Successfully got chat messages
} else {
// handle error - request messages again
}
});
Get all messages between two messages:
import com.phenixrts.common.RequestStatus;
import com.phenixrts.chat.ChatMessage;
import com.phenixrts.chat.RoomChatService;
final RoomChatService chatService = ...; // previously obtained
final int batchSize = 100;
final String afterMessageId = "MessageId1";
final String beforeMessageId = "MessageId2";
chatService.getMessages(
batchSize,
afterMessageId,
beforeMessageId,
(RoomChatService chatService, ChatMessage[] messages, RequestStatus status) -> {
if (status == RequestStatus.OK) {
// Successfully got chat messages
} else {
// handle error - request messages again
}
});
Parameters
Name |
Type |
Description |
batchSize (required) |
int |
Limit of messages to receive. |
afterMessageId (optional) |
String |
The messageId to return after. The N messages after message with ID of afterMessageId but before beforeMessageId |
beforeMessageId (optional) |
String |
The messageId to return before. The N messages before message with ID of beforeMessageId |
callback (required) |
RoomChatService.GetMessagesCallback |
Callback with the status of the request |
Get Messages Callback Arguments
Name |
Type |
Description |
roomChatService |
RoomChatService |
The chat service making the callback |
messages |
ChatMessage[] |
Array of chat messages matching search criteria |
status |
RequestStatus |
The status of the operation |
Get Messages Callback Status Codes
Status |
Description |
ok |
Get messages succeeded |
<varies> |
Get messages failed for other reasons |
Observable
Observe value changes. Similar to the ReactiveX Behavior Subject.
Important differences for Phenix SDK observables:
- There is always an initial value (which can be
null
for some objects)
- Only if observed value changes will subscribers get notified
Point 1 means that when subscribing, you will always get notified immediately with the current value. This is guaranteed atomic, meaning that you do not have to worry about missing any observable updates.
Observable
Subscribe to an Observable
Receive a notification every time the observable value changes.
import com.phenixrts.common.Disposable;
import com.phenixrts.common.Observable;
Observable<String> observable = ...; // previously obtained
Disposable subscription = observable.subscribe((change) -> {
String newStringValue = (String)change;
// Do something with newStringValue
});
// Closing subscription will cancel it:
subscription.dispose();
Parameters
Name |
Type |
Description |
onChangedHandler (required) |
Observable.OnChangedHandler |
Handler to call when value changes |
Disposable
Has no methods, but does inherit from JavaObject
. This allows canceling of subscription by calling dispose
.
Get Observable Value
Synchronous method allowing retrieval of currently set value. Thread safe.
import com.phenixrts.common.Observable;
import com.phenixrts.room.Member;
import com.phenixrts.room.MemberRole;
Observable<MemberRole> roleObservable = ...; // previously obtained
MemberRole role = roleObservable.getValue();
Observable<Member[]> membersObservable = ...; // previously obtained
Member[] members = membersObservable.getValue();
Set Observable Value
Change the observable value. This will trigger notifications on all subscribers.
import com.phenixrts.common.Observable;
import com.phenixrts.room.MemberRole;
Observable<MemberRole> roleObservable = ...; // previously obtained
roleObservable.setValue(MemberRole.PRESENTER);