تنفيذ واجهة برمجة التطبيقات Co-Doing API

تصف هذه الصفحة كيفية استخدام Co-Doing API لإتاحة سيناريو مشترك.

عملية الإعداد الأوّلية

لإعداد المكتبة للاستخدام، يجب أن يضبط تطبيق المشاركة المباشرة عنصر CoDoingClient الذي يمثّل جلسة مشتركة.

لاستخدام حزمة تطوير البرامج (SDK) لميزة "المشاركة المباشرة في Meet"، عليك طلب الطريقة AddonClientFactory.getClient. يؤدي ذلك إلى عرض رمز AddonClient يعمل كنقطة دخول لجلسة العمل المشترَك.

لاستخدام البرنامج، يمكنك استدعاء الطريقة newSessionBuilder من AddonClient لعرض أداة إنشاء 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".

يعرض نموذج الرمز البرمجي التالي كيفية إشعار المستخدمين بعنوان 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);
}

إنهاء المشاركة في اللعب

عندما يختار المستخدم إنهاء النشاط، يتم إلغاء ربط الطريقة 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();
    }
  }
}