本頁說明如何使用 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
訊息來顯示影片已暫停,並使用 setGlobalState
方法要求 Google Meet 向所有其他參與者廣播。在設定新狀態前,共用的全域狀態會成為所有參與者 (現有或新參與者) 的預設狀態。
以下程式碼範例說明如何通知使用者已暫停狀態:
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();
}
}
}