本頁面說明如何使用 Co-Doing API 支援共同操作情境。
初始設定
如要準備使用程式庫,即時分享應用程式應初始化代表共同工作工作階段的 CoDoingClient
物件。
如要使用 Meet 即時分享 SDK,請呼叫 AddonClientFactory.getClient
方法。這會傳回 AddonClient
,做為共同執行工作階段的進入點。
如要使用用戶端,請從 AddonClient
呼叫 newSessionBuilder
方法,傳回新 AddonSession
的建構工具。newSessionBuilder
會實作 AddonSessionHandler
介面,以便處理外掛程式提供的回呼。
如要開始工作階段,請在建構工具中新增 withCoDoing
方法。
下列程式碼範例顯示共同執行用戶端物件的基本初始化作業:
Java
class AwesomeVideoAddonSessionHandler implements AddonSessionHandler {}
//For sample implementation, see the "Handle incoming updates" section.
class AwesomeVideoCoDoingHandler implements CoDoingHandler {}
public ListenableFuture<AddonSession> initialSetup() {
AddonClient meetClient = AddonClientFactory.getClient();
return meetClient
.newSessionBuilder(
new AwesomeVideoAddonSessionHandler())
.withCoDoing(new AwesomeVideoCoDoingHandler())
.begin();
}
暫停影片
使用即時分享體驗時,當使用者在他們的本機影片應用程式中暫停播放時,您必須確認即時分享體驗中的所有參與者也會暫停播放影片。
如要這麼做,請編寫顯示暫停播放影片的 CoDoingState
訊息,並指示 Google Meet 使用 setGlobalState
方法向所有其他參與者廣播訊息。在設定新狀態前,共用的全域狀態會成為所有參與者 (現有或新參與者) 的預設狀態。
以下程式碼範例說明如何通知使用者暫停狀態:
Java
public void onVideoPaused(String videoUrl, Instant currentTimestamp) {
// Create an internal state object to share with other participants. Note: It's
// good practice to encode all metadata—even seemingly irrelevant data—into
// ActivityState updates to guard against race conditions and other subtle
// failures.
AwesomeVideoState videoState = AwesomeVideoState
.builder()
.videoUrl(videoUrl)
.videoTimestamp(currentTimestamp)
.isPaused(true)
.build();
// Create the CoDoingState object to wrap the internal state
CoDoingState coDoingState = new CoDoingState();
coDoingState.state = SerializationUtils.serialize(videoState);
// Use Meet to broadcast internal state update to all other participants
this.coDoingClient.setGlobalState(coDoingState);
};
程式碼範例會觸發序列化 videoState
物件,播送到參與即時分享體驗的其他 Meet 執行個體。如要進一步瞭解如何接收其他參與者的廣播更新,請參閱「處理傳入的更新」一節。
下圖說明觸發暫停動作後的事件序列:
取消暫停影片
與暫停視訊功能類似,如果使用者在本機應用程式上取消暫停視訊,Meet 就必須向其他即時分享參與者播送這項作業。
在傳送端 (取消暫停影片的使用者) 上,暫停範例唯一的差別是 isPaused
狀態會更新。
以下程式碼範例說明如何從傳送方端通知使用者已取消暫停的狀態:
Java
public void onVideoUnpaused(String videoUrl, Instant currentTimestamp) {
AwesomeVideoState videoState = AwesomeVideoState
.builder()
.videoUrl(videoUrl)
.videoTimestamp(currentTimestamp)
.isPaused(false)
.build();
CoDoingState coDoingState = new CoDoingState();
coDoingState.state = SerializationUtils.serialize(videoState);
this.coDoingClient.setGlobalState(coDoingState);
}
跳轉影片
就像暫停影片和取消暫停影片一樣,如果使用者將本機應用程式的時間軸拖曳至新的時間戳記,Meet 就必須向所有參與者播送這項作業。
下列程式碼範例說明如何從傳送端通知使用者已更新時間戳記:
Java
public void onVideoSeeked(String videoUrl, Instant currentTimestamp, bool isPaused) {
AwesomeVideoState videoState = AwesomeVideoState
.builder()
.videoUrl(videoUrl)
.videoTimestamp(currentTimestamp)
.isPaused(isPaused)
.build();
CoDoingState coDoingState = new CoDoingState();
coDoingState.state = SerializationUtils.serialize(videoState);
this.coDoingClient.setGlobalState(coDoingState);
}
播放其他影片
假如使用者同時在本機應用程式中選取其他影片來變更正在觀看的影片,則 Meet 必須為所有即時分享參與者播放新影片。變更的影片會儲存在「videoState.videoUrl
」中。
以下程式碼範例說明如何將更新後的影片網址告知使用者:
Java
public void onVideoChanged(String videoUrl, Duration currentTimestamp, bool isPaused) {
AwesomeVideoState videoState = AwesomeVideoState
.builder()
.videoUrl(videoUrl)
.videoTimestamp(currentTimestamp)
.isPaused(isPaused)
.build();
CoDoingState coDoingState = new CoDoingState();
coDoingState.state = SerializationUtils.serialize(videoState);
this.coDoingClient.setGlobalState(coDoingState);
}
結束共同活動
使用者選擇結束活動時,endSession
方法會與 Meet 應用程式中斷連線。這不會強制 Meet 結束會議,也不會讓使用者離開會議。
下列程式碼範例說明如何通知使用者已停止的工作階段:
Java
public void endCoDoing() {
this.session.endSession();
}
處理傳入的更新
當其他參與者的 Meet 應用程式收到廣播時,就會觸發 onGlobalStateChanged()
回呼。通常,一定要有好決定回應傳入更新時應採取的行動,例如只比對傳入的時間戳記,前提是其時間戳記與本機時間戳記有極大差異。
下列程式碼範例說明如何處理不同的傳入更新:
Java
class AwesomeVideoCoDoingHandler implements CoDoingHandler {
public void onGlobalStateChanged(CoDoingState update) {
AwesomeVideoState videoState = SerializationUtils.deserialize(update.state());
// Handle transition to new video.
if (!videoState.videoUrl.equals(this.videoPlayer.videoUrl)) {
this.videoPlayer.loadVideo(videoState.videoUrl);
}
// If the timestamp in the arriving update has sufficiently diverged, adjust
// the local video playout.
if (videoState.videoTimestamp.minus(this.videoPlayer.videoTimestamp).abs() >
Duration.ofSeconds(2)) {
this.videoPlayer.seek(videoState.videoTimestamp);
}
// Update pause state, if necessary.
if (!videoState.isPaused && this.videoPlayer.isPaused) {
this.videoPlayer.unpause();
} else if (videoState.isPaused && !this.videoPlayer.isPaused) {
this.videoPlayer.pause();
}
}
}