הטמעת ה-Co-Doing API

בדף הזה נסביר איך להשתמש ב-Co-Doing API כדי לתמוך בתרחיש של פעולה משותפת.

הגדרה ראשונית

כדי להכין את הספרייה לשימוש, האפליקציה של השיתוף בזמן אמת צריכה לאתחל אובייקט CoDoingClient שמייצג סשן של פעולה משותפת.

כדי להשתמש ב-SDK של שיתוף בזמן אמת ב-Meet, צריך להפעיל את השיטה AddonClientFactory.getClient. הפעולה הזו תחזיר את האובייקט AddonClient שמשמש כנקודת הכניסה לסשן של פעולה משותפת.

כדי להשתמש בלקוח, קוראים ל-method newSessionBuilder מה-AddonClient כדי להחזיר builder עבור AddonSession חדש. ה-newSessionBuilder מיישם את הממשק של AddonSessionHandler כדי לטפל בקריאות החוזרות שהתוסף מספק בסשן.

כדי להתחיל סשן, מוסיפים את ה-method withCoDoing ל-builder.

דוגמת הקוד הבאה מציגה אתחול בסיסי של אובייקט הלקוח לביצוע פעולה משותפת:

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 שמשתתפים בחוויית השיתוף בזמן אמת. בקטע טיפול בעדכונים נכנסים מוסבר איך מקבלים עדכוני שידור ממשתתפים אחרים.

בתרשים הבא מתואר רצף האירועים אחרי הפעלת פעולת ההשהיה:

הפעלת תרשים של ממשק API לשיתוף בזמן אמת.

ביטול השהיית הסרטון

בדומה להשהיית הווידאו, אם משתמש מבטל את ההשהיה של הסרטון באפליקציה המקומית שלו, 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.

דוגמת הקוד הבאה מראה איך להודיע למשתמשים על כתובת ה-URL המעודכנת של הסרטון:

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);
}

סיום העשייה המשותפת

כשמשתמש בוחר לסיים את הפעילות, היא מתנתקת מאפליקציית Meet באמצעות השיטה endSession, והיא לא גורמת למשתמש לעזוב את הפגישה ב-Meet.

דוגמת הקוד הבאה מראה איך להודיע למשתמשים על הסשן שהופסק:

Java

public void endCoDoing() {
  this.session.endSession();
}

טיפול בעדכונים נכנסים

כשאפליקציית Meet של משתתף אחר מקבלת שידור, מופעלת קריאה חוזרת (callback) ב-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();
    }
  }
}