Get started

Stay organized with collections Save and categorize content based on your preferences.

You can start integrating the Google Meet Live Sharing SDK into your Android app using Java.

You can find Android documentation and additional Android libraries to expand the functionality and features of your app through the official Android Developers Documentation.

Using the SDK

The first step to begin Google Meet Live Sharing SDK is to call LiveSharingClientFactory.getClient(). This returns a LiveSharingClient that can be used to start a co-watching activity, a co-doing activity, or both.

The Co-Watching API and Co-Doing API are independent and can be used in parallel with each other.

Java

/** Provides a {@link LiveSharingClient}. */
public final class LiveSharingClientFactory {
  /** Returns the singleton instance of {@link LiveSharingClient}. */
  public static LiveSharingClient getClient();
}

/**
 * Primary interface for starting and stopping live sharing with Meet as the video provider.
 *
 * <p><b>Note:</b> Methods must be called in the documented order, else the returned {@link
 * ListenableFuture} resolves with an {@link IllegalStateException}. For example, {@link
 * #disconnectMeeting} should not be called before {@link #connectMeeting}. However, as long as
 * these methods are called in the correct order, it's not necessary (although recommended) to wait
 * for the {@link ListenableFuture} of the first method to resolve before calling the second method.
 * Additionally, it's <i>not recommended</i> to call {@link ListenableFuture#cancel} on these
 * futures, but instead to permit them to run to completion.
 */
public interface LiveSharingClient {
  /**
   * Returns the status of ongoing Meet sessions, if any. This is a single, one-time check.
   *
   * <p>Utilized to detect any ongoing Meet standalone or live sharing sessions. In the event of
   * detected active sessions, will return {@link LiveSharingMeetingInfo} with the {@link
   * MeetingStatus} member set as {@link MeetingStatus#CONNECTED} or {@link
   * MeetingStatus#CONNECTED_WITH_LIVE_SHARING}. No active session returns {@link
   * MeetingStatus#NOT_CONNECTED}
   *
   * <p>Accepts {@link Optional} of {@link Handler} as parameter for application controlled thread
   * management. In the absence of {@link Handler}, one will be spawned during execution. Only allow
   * this if you do not care to have control over the {@link Handler} instance.
   *
   * <p>This method can be used to determine whether to prompt the user to begin a new live sharing
   * session or join an existing one. Typically a call to {@link #connectMeeting} is made when the
   * user responds affirmatively to such a prompt.
   *
   * @param appContext the {@link Context#getApplicationContext()} value of the application that is
   *     using the Google Meet Live Sharing SDK.
   * @param handler an {@link Optional} of {@link Handler} for asynchronous execution
   * @return A {@link ListenableFuture} of {@link LiveSharingMeetingInfo} describing the status of
   *     the current meeting, if any.
   */
  ListenableFuture<LiveSharingMeetingInfo> queryMeeting(
      Context appContext, Optional<Handler> handler);

  /**
   * Connects to a meeting, either by creating a meeting or by connecting to an ongoing meeting.
   *
   * <p>If a meeting is created, the current user will be the only participant initially.
   *
   * <p>The returned URL is intended to be exposed to the user to be manually shared with their
   * intended live sharing group.
   *
   * <p>If this method is called before calling {@link #disconnectMeeting} for an existing meeting,
   * the future will fail with an {@link IllegalStateException}.
   *
   * <p>The method should only be called once the user has confirmed that they want to participate
   * in a live sharing session and shouldn't be used as a mechanism to track the status of any
   * ongoing meetings. Refer to {@link #queryMeeting} method instead to track the status of the
   * current meeting.
   *
   * @param appContext the {@link Context#getApplicationContext()} value of the application that is
   *     using the Google Meet Live Sharing SDK.
   * @param liveSharingApplicationName the globally-unique canonical name for the Live Sharing App
   *     connecting to Meet, e.g. {@code youtube}.
   * @param meetingDisconnectHandler a {@link MeetingDisconnectHandler} to receive notification when
   *     the Meet client has disconnected.
   * @return A {@link ListenableFuture} which will resolve with a {@link LiveSharingMeetingInfo}
   *     object if the connection was successful; or to an {@link IllegalStateException} if already
   *     connected to a meeting (i.e. {@link #disconnectMeeting} was not called); or to a {@link
   *     LiveSharingException} if there was an unexpected error.
   * @throws NullPointerException if any of the provided arguments are null.
   * @throws IllegalStateException if {@code liveSharingApplicationName} is an empty string or isn't
   *     part of the onboarded list of Apps.
   */
  ListenableFuture<LiveSharingMeetingInfo> connectMeeting(
      Context appContext,
      String liveSharingApplicationName,
      MeetingDisconnectHandler meetingDisconnectHandler);

  /**
   * Stores or updates the encoded metadata for this participant.
   *
   * <p>The encoded metadata is capped at {@link
   * ParticipantMetadataDelegate#MAX_INDIVIDUAL_PARTICIPANT_METADATA_SIZE_BYTES} bytes per
   * participant.
   *
   * @param metadata an encoded blob of metadata that describes relevant metadata for the local
   *     participant.
   * @param delegate a {@link ParticipantMetadataDelegate} to receive the latest set of participant
   *     metadata every time it is updated.
   * @throws IllegalStateException if not already connected to a meeting via {@link
   *     #connectMeeting}.
   * @throws IllegalArgumentException if the provided metadata exceeds {@link
   *     ParticipantMetadataDelegate#MAX_INDIVIDUAL_PARTICIPANT_METADATA_SIZE_BYTES} bytes.
   */
  void setParticipantMetadata(Byte[] metadata, ParticipantMetadataDelegate delegate);

  /**
   * Disconnects the user from the connected meeting, but does not force Meet to end the meeting.
   *
   * @return A {@link ListenableFuture} which evaluates to success if the user successfully left the
   *     meeting; or to an {@link IllegalStateException} if not connected to a meeting (i.e. {@link
   *     #connectMeeting} was not called); or to a {@link LiveSharingException} if there was an
   *     unexpected error.
   */
  ListenableFuture<Void> disconnectMeeting();

  /**
   * Begins a {@link CoDoingSession}.
   *
   * <p><b>Note:</b> Only one co-doing session can be active at one time.
   *
   * @return A {@link ListenableFuture} which evaluates to a {@link CoDoingSession} instance if a
   *     co-doing session was successfully started; or to an {@link IllegalStateException} if not
   *     connected to a meeting or if another co-doing session is still running (i.e. {@link
   *     #endCoDoing} was not called); or to a {@link LiveSharingException} if there was an
   *     unexpected error.
   * @throws NullPointerException if {@code delegate} is null.
   */
  ListenableFuture<CoDoingSession> beginCoDoing(CoDoingSessionDelegate delegate);

  /**
   * Ends the current {@link CoDoingSession}.
   *
   * @return A {@link ListenableFuture} which evaluates to success if the co-doing session
   *     successfully ended; or to an {@link IllegalStateException} if not connected to a meeting or
   *     if there is no ongoing co-doing session (i.e. {@link #beginCoDoing} was not called); or to
   *     a {@link LiveSharingException} if there was an unexpected error.
   */
  ListenableFuture<Void> endCoDoing();

  /**
   * Begins a {@link CoWatchingSession}.
   *
   * <p><b>Note:</b> Only one co-watching session can be active at one time.
   *
   * @return A {@link ListenableFuture} which evaluates to a {@link CoWatchingSession} instance if a
   *     co-watching session was successfully started; or to an {@link IllegalStateException} if not
   *     connected to a meeting or if another co-watching session is still running (i.e. {@link
   *     #endCoWatching} was not called); or to a {@link LiveSharingException} if there was an
   *     unexpected error.
   * @throws NullPointerException if {@code delegate} is null.
   */
  ListenableFuture<CoWatchingSession> beginCoWatching(CoWatchingSessionDelegate delegate);

  /**
   * Ends the current {@link CoWatchingSession}.
   *
   * @return A {@link ListenableFuture} which evaluates to success if the co-watching session
   *     successfully ended; or to an {@link IllegalStateException} if not connected to a meeting or
   *     if there is no ongoing co-watching session (i.e. {@link #beginCoWatching} was not called);
   *     or to a {@link LiveSharingException} if there was an unexpected error.
   */
  ListenableFuture<Void> endCoWatching();
}

interface CoWatchingSessionDelegate {
  /**
   * Apply the supplied state to media playout, up to and including switching to a new
   * media stream if the mediaId changes.
   *
   * @param state the new state to be applied to the player.
   */
  void applyCoWatchingState(CoWatchingState state);

  /**
   * Return the current state of the local media playout. This is called regularly, so it
   * should be written to be performant.
   *
   * @return a {@link CoWatchingState} describing the current state.
   */
  Optional<CoWatchingState> queryCoWatchingState();
}

interface CoDoingSessionDelegate {
  /**
   * Callback for state updates broadcast by other participants in the meeting.
   *
   * @param update the {@link CoDoingState} update to be applied.
   */
  void applyUpdate(CoDoingState update);

  /**
   * Callback to retrieve the state of live sharing. This is called regularly, so it
   * should be written to be performant.
   *
   * @return the current {@link CoDoingState} of the live sharing app.
   */
  CoDoingState queryCoDoingState();
}

Co-Watching API

Java

/**
 * Represents the state of the co-watching activity.
 *
 * <p>Used when receiving state updates from other participants and as the return value of {@link
 * CoWatchingSessionDelegate#onCoWatchingStateQuery()}.
 */
abstract class CoWatchingState {
  /**
   * Returns the identifier for the media being played.
   *
   * <p><b>Note:</b> the actual format only matters to co-watching app.
   */
  public abstract String mediaId();

  /** Returns the current position of the media playout. */
  public abstract Duration mediaPlayoutPosition();

  /** Returns the current playout rate, where {@code 1.0} is normal speed. */
  public abstract double mediaPlayoutRate();

  /** Represents the current state of media playback. */
  public enum PlaybackState {
    /** The media is currently buffering and will begin playback when ready. */
    BUFFERING,
    /** The media is playing. */
    PLAY,
    /** The media is paused. */
    PAUSE,
    /** The media player has reached the end of the current media. */
    ENDED
  }

  /** Returns the current state of media playback. */
  public abstract PlaybackState playbackState();

  /** Returns a new {@link Builder} from the current object. */
  public abstract Builder toBuilder();
}

/**
 * Represents a co-watching session.
 *
 * <p>Informs Meet of recent user actions (e.g. play/pause/seek) and environmental factors like
 * delays due to buffering media.
 */
public interface CoWatchingSession {
  /** Maximum allowed playout rate. */
  public static final double MAX_PLAYOUT_RATE = 2.0;

  /** Maximum number of seeks allowed per second. */
  public static final double MAX_SEEKS_PER_SECOND = 0.9;

  /**
   * Notify Meet that the user has switched media, so Meet can pass that along to other users.
   *
   * @param mediaTitle the title of the media switched to. This title will be reflected in the Meet
   *     UI when other users are considering connecting to the co-watching session.
   * @param mediaId the string URI of the media switched to.
   * @param mediaPlayoutPosition the position at which the media began playout.
   * @throws NullPointerException if {@code mediaId} or {@code mediaPlayoutPosition} are null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   */
  void notifySwitchedToMedia(String mediaTitle, String mediaId, Duration mediaPlayoutPosition);

  /**
   * Notify Meet that the user has paused or unpaused the playback of media, so Meet can mirror that
   * action for other users.
   *
   * @param paused {@code true} if paused or {@code false} if playback resumed.
   * @param mediaPlayoutPosition the position at which the media was paused or unpaused.
   * @throws NullPointerException if {@code mediaPlayoutPosition} is null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   */
  void notifyPauseState(boolean paused, Duration mediaPlayoutPosition);

  /**
   * Notify Meet that the user has seeked the playback point of the media, so Meet can mirror that
   * action for other users.
   *
   * @param mediaPlayoutPosition the timestamp to which the user seeked.
   * @throws NullPointerException if {@code mediaPlayoutPosition} is null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   * @throws IllegalStateException if the number of seeks per second is greater than {@link
   *     #MAX_SEEKS_PER_SECOND}.
   */
  void notifySeekToTimestamp(Duration mediaPlayoutPosition);

  /**
   * Notifies Meet that the user has updated the playout rate of the media (eg. 1.25x) to a new value.
   *
   * @param rate the rate at which media is now being played out.
   * @param mediaPlayoutPosition the current position of the media player.
   * @throws IllegalStateException if {@code rate} is not a positive number.
   * @throws IllegalStateException if {@code rate} is greater than the {@link #MAX_PLAYOUT_RATE}.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   */
  void notifyPlayoutRate(double rate, Duration mediaPlayoutPosition);

  /**
   * Notifies Meet that the media is not ready to be played out due to buffering, due to a prior
   * media switch, media seek, or normal network congestion.
   *
   * @param mediaPlayoutPosition the position at which the media is paused, waiting for buffering to
   *     complete.
   * @throws NullPointerException if {@code mediaPlayoutPosition} is null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   */
  void notifyBuffering(Duration mediaPlayoutPosition);

  /**
   * Notifies Meet that the buffering is complete and the media is now ready to be played out,
   * starting at the supplied timestamp.
   *
   * @param mediaPlayoutPosition the position at which the media is buffered and ready to play.
   * @throws NullPointerException if {@code mediaPlayoutPosition} is null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   */
  void notifyReady(Duration mediaPlayoutPosition);

  /**
   * Notifies Meet that the media player has reached the end of the current media.
   *
   * <p><b>Note:</b> calling this method is not required (though not harmful) if {@link
   * #notifySwitchedToMedia} is called via an auto-play mechanism as soon as a media ends.
   *
   * @param mediaPlayoutPosition the final position of the player.
   * @throws NullPointerException if {@code mediaPlayoutPosition} is null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-watching session has ended.
   */
  void notifyEnded(Duration mediaPlayoutPosition);
}

Co-Doing API

The co-doing version is semantically much simpler than co-watching as it passes around arbitrary state blobs between participants:

Java

abstract class CoDoingState {
  /** Returns the opaque binary-encoded state update for the co-doing activity. */
  public abstract byte[] state();
}

public interface CoDoingSession {
  /** Maximum allowed blob size in bytes. */
  int MAX_CODOING_BLOB_BYTES = 1650;

  /** Preferred maximum allowed blob size (1kb) in bytes. */
  int PREFERRED_MAX_CODOING_BLOB_BYTES = 1 * BYTES_IN_KB;

  /**
   * Broadcasts state to all other current participants, and becomes the default state for any
   * participants until some other state is broadcast.
   *
   * <p><b>Note:</b> This shared state is eventually consistent across participants. For predictable
   * behavior, this binary state should be complete state, not partial, as the Google Meet Live Sharing SDK does
   * not provide guarantees around the delivery of individual messages -- only eventual consistency.
   *
   * <p><b>Note:</b> In a race condition where two participants call this method simultaneously, the
   * Google Meet Live Sharing SDK will select a canonical winning update. The losing update may or may not be
   * applied to participants, but the winning update will always be applied later.
   *
   * @param updatedCoDoingState the updated activity state that will be broadcast to other
   *     participants. Preferred blob size is {@link #PREFERRED_MAX_CODOING_BLOB_BYTES}.
   * @throws NullPointerException if {@code updatedCoDoingState} is null.
   * @throws LiveSharingException if there was an unexpected error.
   * @throws IllegalStateException if called after the co-doing session has ended.
   * @throws IllegalStateException if blob size > {@link #MAX_CODOING_BLOB_BYTES}.
   */
  void broadcastStateUpdate(CoDoingState updatedCoDoingState);
}