Интегрируйте виджеты Android с Google Assistant

1. Обзор

В первой лаборатории кода App Actions вы узнали, как расширить Google Assistant до примера фитнес-приложения, реализовав встроенные намерения (BII) из категории BII «Здоровье и фитнес».

Действия приложения позволяют пользователям запускать определенные функции приложения напрямую из Ассистента, спрашивая что-то вроде: « Эй, Google, начни запуск в exampleApp». Помимо запуска приложений, Ассистент может отображать пользователю интерактивный виджет Android для выполнения запросов на соответствующие BII.

Экран, на котором Ассистент возвращает виджет в ответ на запрос пользователя, который активировал функцию GET_EXERCISE_OBSERVATION BII приложения.

Что ты построишь

В этой лаборатории кода вы узнаете, как возвращать виджеты Android для выполнения запросов пользователей Ассистента. Вы также научитесь:

  • Параметры пользовательского BII для персонализации виджетов.
  • Обеспечьте преобразование текста в речь (TTS) в Ассистенте для своих виджетов.
  • Используйте встроенную ссылку на намерение, чтобы определить, какие BII поддерживают выполнение виджета.

Предварительные условия

Прежде чем продолжить, убедитесь, что ваша среда разработки готова к разработке App Actions. Он должен иметь:

  • Терминал для запуска команд оболочки с установленным git.
  • Последняя стабильная версия Android Studio .
  • Физическое или виртуальное устройство Android с доступом в Интернет.
  • Учетная запись Google, в которой выполнен вход в Android Studio, приложение Google и приложение Google Assistant.

Если вы используете физическое устройство, подключите его к локальному компьютеру разработки.

2. Поймите, как это работает

Google Ассистент использует понимание естественного языка (NLU), чтобы прочитать запрос пользователя и сопоставить его со встроенным намерением Ассистента (BII). Затем Ассистент сопоставляет намерение с возможностью (реализующей BII), которую вы регистрируете для этого намерения в своем приложении. Наконец, Ассистент выполняет запрос пользователя, отображая виджет Android, который создает ваше приложение, используя сведения, найденные в этой возможности.

В этой лаборатории кода вы определяете возможность, которая регистрирует поддержку GET_EXERCISE_OBSERVATION BII. Используя эту возможность, вы поручаете Ассистенту сгенерировать намерение Android для класса виджета FitActions для выполнения запросов для этого BII. Вы обновляете этот класс, чтобы создать персонализированный виджет, который Помощник будет отображать пользователю, и введение TTS, которое будет объявлять Помощник.

Следующая диаграмма демонстрирует этот поток:

Блок-схема, демонстрирующая работу виджета Assistant.

Виджет FitActions

Пример приложения FitActions содержит виджет с информацией о тренировках, который пользователи могут добавить на свой главный экран. Этот виджет является отличным кандидатом для выполнения пользовательских запросов, которые запускают GET_EXERCISE_OBSERVATION BII.

Как работает виджет

Когда пользователь добавляет виджет на главный экран, виджет отправляет сигнал широковещательному приемнику устройства. Эта служба получает информацию о виджете из определения получателя виджета в ресурсе AndroidManifest.xml приложения. Он использует эту информацию для создания объекта RemoteViews представляющего виджет.

Пример приложения определяет получатель widgets.StatsWidgetProvider , который соответствует классу StatsWidgetProvider :

<!-- app/src/main/AndroidManifest.xml -->

<receiver
  android:name=".widgets.StatsWidgetProvider"
  android:exported="false">
  <intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
  <meta-data
    android:name="android.appwidget.provider"
    android:resource="@xml/stats_widget" />
</receiver>

Класс StatsWidgetProvider , StatsWidgetProvider.kt , управляет потоками создания объектов StatsWidget . Он выполняет следующие обязанности:

  • Создание экземпляров виджетов и заполнение их данными упражнений из базы данных приложения.
  • Форматирование данных тренировки для удобства чтения с помощью formatDataAndSetWidget() .
  • Предоставление значений по умолчанию, если данные тренировки недоступны, с помощью setNoActivityDataWidget() .

Добавить поддержку Ассистента

В этой лаборатории кода вы обновите пример приложения для поддержки функций App Actions. Эти изменения включают в себя:

  1. Настройка возможности GET_EXERCISE_OBSERVATION BII для возврата экземпляра объекта StatsWidget .
  2. Обновление класса StatsWidget для использования таких функций App Actions, как:
    • Использование параметров BII, позволяющих пользователям просматривать конкретную статистику тренировок, задавая такие вопросы, как «Эй, Google, покажи мою статистику бега в exampleApp».
    • Предоставление вводных строк TTS.
    • Управление особыми случаями, например, когда пользовательский запрос не включает параметр типа тренировки.

3. Подготовьте среду разработки

Загрузите базовые файлы

Запустите эту команду, чтобы клонировать репозиторий GitHub примера приложения:

git clone --branch start-widget-codelab https://github.com/actions-on-google/appactions-fitness-kotlin.git

После клонирования репозитория выполните следующие действия, чтобы открыть его в Android Studio:

  1. В диалоговом окне «Добро пожаловать в Android Studio» нажмите «Импортировать проект» .
  2. Найдите и выберите папку, в которую вы клонировали репозиторий.

Чтобы увидеть версию приложения, представляющую завершенную кодовую работу, клонируйте репозиторий примера приложения, используя --branch master .

Обновите идентификатор приложения Android

Обновление идентификатора приложения однозначно идентифицирует приложение на тестовом устройстве и позволяет избежать ошибки «Дублируется имя пакета», если приложение загружено в Play Console. Чтобы обновить идентификатор приложения, откройте app/build.gradle :

android {
...
  defaultConfig {
    applicationId "com.MYUNIQUENAME.android.fitactions"
    ...
  }
}

Замените «MYUNIQUENAME» в поле applicationId на что-то уникальное для вас.

Установите тестовый плагин

Плагин Google Assistant позволяет тестировать действия приложения на тестовом устройстве. Он работает, отправляя информацию Ассистенту через приложение Google на вашем устройстве Android. Если у вас еще нет плагина, установите его, выполнив следующие действия:

  1. Перейдите в «Файл» > «Настройки» ( Android Studio > «Настройки» в MacOS).
  2. В разделе «Плагины» перейдите на Marketplace и найдите «Google Assistant». Вы также можете вручную загрузить и установить инструмент тестирования.
  3. Установите инструмент и перезапустите Android Studio.

Проверьте приложение на своем устройстве

Прежде чем вносить дополнительные изменения в приложение, полезно получить представление о том, на что способен пример приложения.

Запустите приложение на тестовом устройстве:

  1. В Android Studio выберите физическое или виртуальное устройство и выберите «Выполнить» > «Выполнить приложение» или нажмите «Выполнить». Запустите значок приложения в Android Studio. на панели инструментов.
  2. Нажмите и удерживайте кнопку «Домой», чтобы настроить Ассистента и убедиться, что он работает. Вам нужно будет войти в Ассистент на своем устройстве, если вы еще этого не сделали.

Дополнительную информацию о виртуальных устройствах Android см. в разделе Создание виртуальных устройств и управление ими .

Кратко изучите приложение, чтобы узнать, на что оно способно. Приложение предварительно заполняет 10 упражнений и отображает эту информацию в первом представлении.

Попробуйте существующий виджет

  1. Нажмите кнопку «Домой» , чтобы перейти на главный экран тестового устройства.
  2. Нажмите и удерживайте пустое место на главном экране и выберите «Виджеты» .
  3. Прокрутите список виджетов до FitActions .
  4. Нажмите и удерживайте значок FitActions и поместите его виджет на главный экран.

Снимок экрана, показывающий виджет FitActions на главном экране устройства.

4. Добавьте действие приложения.

На этом этапе вы добавляете возможность GET_EXERCISE_OBSERVATION BII. Это можно сделать, добавив новый элемент capability в shortcuts.xml . Эта возможность определяет, как эта возможность активируется, как используются параметры BII и какой Android намерен вызвать для выполнения запроса.

  1. Добавьте новый элемент capability в пример ресурса shortcuts.xml проекта.xml со следующей конфигурацией:
    <!-- fitnessactions/app/src/main/res/xml/shortcuts.xml -->
    
    <capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
      <app-widget
        android:identifier="GET_EXERCISE_OBSERVATION"
        android:targetClass="com.devrel.android.fitactions.widgets.StatsWidgetProvider"
        android:targetPackage="PUT_YOUR_APPLICATION_ID_HERE">
        <parameter
          android:name="exerciseObservation.aboutExercise.name"
          android:key="aboutExerciseName"
          android:required="true">
        </parameter>
        <extra android:name="hasTts" android:value="true"/>
      </app-widget>
      <!-- Add Fallback Intent-->
    </capability>
    
    Замените значение android:targetPackage PUT_YOUR_APPLICATION_ID_HERE своим уникальным applicationId .

Эта возможность сопоставляет GET_EXERCISE_OBSERVATION BII с намерением app-widget так что при срабатывании BII виджет создается и отображается пользователю.

Прежде чем активировать виджет, Ассистент извлекает поддерживаемые параметры BII из запроса пользователя. Для этой лаборатории кода требуется параметр BII exerciseObservation.aboutExercise.name , который представляет запрошенный пользователем тип упражнения. Приложение поддерживает три типа упражнений: «бег», «ходьба» и «езда на велосипеде». Вы предоставляете встроенную инвентаризацию , чтобы информировать Ассистента об этих поддерживаемых значениях.

  1. Определите эти элементы инвентаря, добавив эту конфигурацию над возможностью GET_EXERCISE_OBSERVATION в shortcuts.xml :
    <!-- shortcuts.xml -->
    
    <!-- shortcuts are bound to the GET_EXERCISE_OBSERVATION capability and
         represent the types of exercises supported by the app. -->
    
    <shortcut
      android:shortcutId="running"
      android:shortcutShortLabel="@string/activity_running">
      <capability-binding android:key="actions.intent.GET_EXERCISE_OBSERVATION">
        <parameter-binding
          android:key="exerciseObservation.aboutExercise.name"
          android:value="@array/runningSynonyms"/>
      </capability-binding>
    </shortcut>
    
    <shortcut
      android:shortcutId="walking"
      android:shortcutShortLabel="@string/activity_walking">
      <capability-binding android:key="actions.intent.GET_EXERCISE_OBSERVATION">
        <parameter-binding
          android:key="exerciseObservation.aboutExercise.name"
          android:value="@array/walkingSynonyms"/>
      </capability-binding>
    </shortcut>
    
    <shortcut
      android:shortcutId="cycling"
      android:shortcutShortLabel="@string/activity_cycling">
      <capability-binding android:key="actions.intent.GET_EXERCISE_OBSERVATION">
        <parameter-binding
          android:key="exerciseObservation.aboutExercise.name"
          android:value="@array/cyclingSynonyms"/>
      </capability-binding>
    </shortcut>
    
    <capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
      <!-- ... -->
    </capability>
    

Добавить резервное намерение

Резервные намерения обрабатывают ситуации, когда пользовательский запрос не может быть выполнен, поскольку в запросе отсутствуют параметры, необходимые для этой возможности. Для возможности GET_EXERCISE_OBSERVATION требуется параметр exerciseObservation.aboutExercise.name , указанный атрибутом android:required="true" . В таких ситуациях Ассистент требует, чтобы вы определили резервное намерение, чтобы обеспечить успешное выполнение запроса, даже если в запросе не указаны параметры.

  1. В shortcuts.xml добавьте резервное намерение к возможности GET_EXERCISE_OBSERVATION , используя следующую конфигурацию:
    <!-- shortcuts.xml -->
    
    <capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
    
      <app-widget>
        <!-- ... -->
      </app-widget>
    
      <!-- Fallback intent with no parameters needed to successfully execute.-->
      <intent
        android:identifier="GET_EXERCISE_OBSERVATION_FALLBACK"
        android:action="android.intent.action.VIEW"
        android:targetClass="com.devrel.android.fitactions.widgets.StatsWidgetProvider">
      </intent>
    </capability>
    

В этом примере конфигурации резервное выполнение — это намерение Android без параметров в Extra данных.

5. Включите виджет для Ассистента.

Установив возможность GET_EXERCISE_OBSERVATION , обновите класс виджета, чтобы он поддерживал голосовой вызов действий приложения.

Добавьте библиотеку расширений виджетов

Библиотека расширений виджетов действий приложений расширяет возможности виджетов для голосового помощника. В частности, он позволяет вам предоставить собственное представление TTS для ваших виджетов.

  1. Добавьте зависимость библиотеки расширения виджетов к ресурсу примера приложения /app/build.gradle :
    // app/build.gradle
    
    dependencies {
      //...
      implementation "com.google.assistant.appactions:widgets:0.0.1"
    }
    
    Нажмите «Синхронизировать сейчас» в окне предупреждения, которое появляется в Android Studio. Синхронизация после каждого изменения build.gradle помогает избежать ошибок при сборке приложения.

Добавьте сервис виджетов

Служба — это компонент приложения, который может выполнять длительные операции в фоновом режиме. Ваше приложение должно предоставлять сервис для обработки запросов виджетов.

  1. Добавьте службу в ресурс AndroidManifest.xml примера приложения со следующей конфигурацией:
    <!-- AndroidManifest.xml -->
    <service
       android:name=".widgets.StatsWidgetProvider"
       android:enabled="true"
       android:exported="true">
       <intent-filter>
           <action
               android:name="com.google.assistant.appactions.widgets.PIN_APP_WIDGET" />
       </intent-filter>
    </service>
    
    

Во время голосового запроса, который запускает выполнение виджета, Ассистент использует эту службу для отправки запросов в приложение. Служба получает запрос вместе с данными BII. Служба использует эти данные для создания объекта виджета RemoteView для отображения в Assistant.

Обновите класс виджета

Теперь ваше приложение настроено на маршрутизацию запросов возможностей GET_EXERCISE_OBSERVATION в класс вашего виджета. Затем обновите класс StatsWidget.kt , чтобы сгенерировать экземпляр виджета, персонализированный в соответствии с запросом пользователя, используя значения параметров BII.

  1. Откройте класс StatsWidget.kt и импортируйте библиотеку расширений виджета действий приложения:
    // StatsWidget.kt
    
    // ... Other import statements
    import com.google.assistant.appactions.widgets.AppActionsWidgetExtension
    
    
  2. Добавьте эти частные переменные, которые вы используете при определении информации, которая должна заполнить виджет:
    // StatsWidget.kt
    
    private val hasBii: Boolean
    private val isFallbackIntent: Boolean
    private val aboutExerciseName: String
    private val exerciseType: FitActivity.Type
    
  3. Добавьте функцию init , чтобы позволить классу использовать данные параметров виджета, переданные из Ассистента:
    // StatsWidget.kt
    
    init {
      val optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId)
      val bii = optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII)
      hasBii = !bii.isNullOrBlank()
      val params = optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS)
    
      if (params != null) {
        isFallbackIntent = params.isEmpty
        if (isFallbackIntent) {
          aboutExerciseName = context.resources.getString(R.string.activity_unknown)
        } else {
            aboutExerciseName = params.get("aboutExerciseName") as String
          }
      } else {
          isFallbackIntent = false
          aboutExerciseName = context.resources.getString(R.string.activity_unknown)
      }
      exerciseType = FitActivity.Type.find(aboutExerciseName)
    }
    
    

Давайте рассмотрим, как эти обновления позволяют классу StatsWidget.kt реагировать на намерения Android, генерируемые возможностью GET_EXERCISE_OBSERVATION :

  • optionsBundle = Пакет
    • Пакеты — это объекты, которые предназначены для использования за пределами границ процесса, между действиями с намерениями и для хранения переходного состояния при изменениях конфигурации. Ассистент использует объекты Bundle для передачи данных конфигурации в виджет.
  • bii = actions.intent.GET_EXERCISE_OBSERVATION
    • Имя BII можно получить из пакета с помощью AppActionsWidgetExtension .
  • hasBii = true
    • Проверяет наличие BII.
  • params = Bundle[{aboutExerciseName=running}]
    • Специальный Bundle, созданный App Actions, вложен в параметры виджета Bundle . Он содержит пары ключ/значение BII. В этом случае значение running было извлечено из примера запроса «Эй, Google, покажи мою статистику бега в exampleApp».
  • isFallbackIntent = false
    • Проверяет наличие необходимых параметров BII в намерении Extras .
  • aboutExerciseName = running
    • Получает значение намерения Extras для aboutExerciseName .
  • exerciseType = RUNNING
    • Использует aboutExerciseName для поиска соответствующего объекта типа базы данных.

Теперь, когда класс StatsWidget может обрабатывать входящие данные о намерениях действий приложения Android, обновите логику процесса создания виджета, чтобы проверить, был ли виджет запущен действием приложения.

  1. В StatsWidget.kt замените функцию updateAppWidget() этим кодом:
    // StatsWidget.kt
    
    fun updateAppWidget() {
       /**
        * Checks for App Actions BII invocation and if BII parameter data is present.
        * If parameter data is missing, use data from last exercise recorded to the
        *  fitness tracking database.
        */
       if (hasBii && !isFallbackIntent) {
           observeAndUpdateRequestedExercise()
       } else observeAndUpdateLastExercise()
    }
    
    

Приведенный выше код ссылается на новую функцию observeAndUpdateRequestedExercise . Эта функция генерирует данные виджета, используя данные параметра exerciseType , передаваемые намерением App Actions Android.

  1. Добавьте функцию observeAndUpdateRequestedExercise с помощью этого кода:
    // StatsWidget.kt
    
    /**
    * Create and observe the last exerciseType activity LiveData.
    */
    private fun observeAndUpdateRequestedExercise() {
      val activityData = repository.getLastActivities(1, exerciseType)
    
       activityData.observeOnce { activitiesStat ->
           if (activitiesStat.isNotEmpty()) {
               formatDataAndSetWidget(activitiesStat[0])
               updateWidget()
           } else {
               setNoActivityDataWidget()
               updateWidget()
           }
       }
    }
    
    

В приведенном выше коде используйте существующий класс репозитория , найденный в приложении, для получения данных о фитнесе из локальной базы данных приложения. Этот класс предоставляет API, упрощающий доступ к базе данных. Репозиторий работает, предоставляя объект LiveData при выполнении запросов к базе данных. В своем коде вы наблюдаете за этими LiveData , чтобы получать последние данные о фитнес-тренировках.

Включить TTS

Вы можете предоставить строку TTS, чтобы Помощник мог объявлять ее при отображении виджета. Мы рекомендуем включить это, чтобы обеспечить звуковой контекст для ваших виджетов. Эта функциональность обеспечивается библиотекой расширений виджетов действий приложения, которая позволяет вам устанавливать текст и TTS-знакомства, сопровождающие ваши виджеты в Ассистенте.

Хорошим местом для ознакомления с TTS является функция formatDataAndSetWidget , которая форматирует данные о действиях, возвращаемые из базы данных приложения.

  1. В StatsWidget.kt добавьте этот код в функцию formatDataAndSetWidget :
    // StatsWidget.kt
    
    private fun formatDataAndSetWidget(
      activityStat: FitActivity,
    ) {
          // ...
    
          // Add conditional for hasBii for widget with data
          if (hasBii) {
             // Formats TTS speech and display text for Assistant
             val speechText = context.getString(
                 R.string.widget_activity_speech,
                 activityExerciseTypeFormatted,
                 formattedDate,
                 durationInMin,
                 distanceInKm
             )
             val displayText = context.getString(
                 R.string.widget_activity_text,
                 activityExerciseTypeFormatted,
                 formattedDate
             )
             setTts(speechText, displayText)
          }
    }
    
    

Приведенный выше код ссылается на два строковых ресурса: один для речи, а другой для текста. Ознакомьтесь с рекомендациями по стилю преобразования текста в речь в нашем видеоролике о виджетах, чтобы получить рекомендации TTS. В примере также упоминается setTts — новая функция, которая предоставляет информацию TTS экземпляру виджета.

  1. Добавьте эту новую функцию setTts в StatsWidget.kt используя этот код:
    // StatsWidget.kt
    
    /**
     * Sets TTS to widget
     */
    private fun setTts(
      speechText: String,
      displayText: String,
    ) {
      val appActionsWidgetExtension: AppActionsWidgetExtension =
          AppActionsWidgetExtension.newBuilder(appWidgetManager)
            .setResponseSpeech(speechText)  // TTS to be played back to the user
            .setResponseText(displayText)  // Response text to be displayed in Assistant
            .build()
    
      // Update widget with TTS
      appActionsWidgetExtension.updateWidget(appWidgetId)
    }
    

Наконец, завершите логику TTS, установив информацию TTS, когда база данных упражнений возвращает пустые данные для запрошенного типа тренировки.

  1. Обновите функцию setNoActivityDataWidget() в StatsWidget.kt с помощью этого кода:
    // StatsWidget.kt
    
    private fun setNoActivityDataWidget() {
      // ...
      // Add conditional for hasBii for widget without data
      if (hasBii) {
        // formats speech and display text for Assistant
        // https://developers.google.com/assistant/app/widgets#library
        val speechText =
          context.getString(R.string.widget_no_activity_speech, aboutExerciseName)
        val displayText =
          context.getString(R.string.widget_no_activity_text)
    
        setTts(speechText, displayText)
      }
    }
    

6. Проверьте действие приложения.

Во время разработки используйте плагин Google Assistant для предварительного просмотра действий приложения Assistant на тестовом устройстве. Вы можете настроить параметры намерения для действия приложения с помощью этого инструмента, чтобы проверить, как ваше действие обрабатывает различные способы, которыми пользователь может попросить Ассистента запустить его.

Создать предварительный просмотр

Чтобы протестировать действие приложения с помощью плагина:

  1. Откройте «Инструменты» > «Google Ассистент» > «Инструмент проверки действий приложения» . Вас могут попросить войти в Android Studio, используя свою учетную запись Google.
  2. Нажмите «Создать предварительный просмотр» . Если потребуется, просмотрите и примите политику и условия обслуживания App Actions.

Проверьте ожидаемый тип упражнения

Верните виджет, показывающий информацию о последнем завершенном запуске в приложении, выполнив следующие действия в инструменте тестирования:

  1. На первом этапе, когда инструмент попросит вас выбрать и настроить BII, выберите actions.intent.GET_EXERCISE_OBSERVATION .
  2. В поле «УпражнениеНаблюдение» измените имя упражнения по умолчанию с climbing на run .
  3. Нажмите «Запустить действие приложения» .

Экран, показывающий виджет, возвращенный с помощью плагина Google Assistant.

Протестируйте неожиданный тип упражнения

Чтобы протестировать неожиданный тип упражнения с помощью инструмента тестирования:

  1. В поле «УпражнениеНаблюдение» обновите значение name с Run на Climbing .
  2. Нажмите «Запустить действие приложения» .

Ассистент должен вернуть виджет, отображающий информацию «Действия не найдены».

Экран с виджетом без информации об упражнениях, полученный с помощью плагина Google Assistant.

Проверьте резервное намерение

Запросы, запускающие резервное намерение, должны возвращать виджет, отображающий информацию о последней зарегистрированной активности любого типа упражнений.

Чтобы проверить намерение отката:

  1. В поле «УпражнениеНаблюдение» удалите объект aboutExercise .
  2. Нажмите «Запустить действие приложения» .

Ассистент должен вернуть виджет, отображающий информацию о последнем выполненном упражнении.

Экран с виджетом, отображающим последнюю записанную активность с использованием плагина Google Assistant.

7. Следующие шаги

Поздравляем!

Теперь у вас есть возможность отвечать на запросы пользователей с помощью виджета Android с помощником.

Что мы рассмотрели

В этой лаборатории вы узнали, как:

  • Добавьте виджет приложения в BII.
  • Измените виджет, чтобы получить доступ к параметрам из Android Extras.

Что дальше

Отсюда вы можете попробовать внести дополнительные улучшения в свое фитнес-приложение. Чтобы получить ссылку на готовый проект, см. основной репозиторий на GitHub.

Вот несколько советов по дальнейшему изучению расширения этого приложения с помощью действий приложения:

Чтобы продолжить свое путешествие по Actions on Google, изучите эти ресурсы:

Следуйте за нами в Твиттере @ActionsOnGoogle, чтобы быть в курсе наших последних объявлений, и пишите в Твиттере #appactions , чтобы поделиться тем, что вы создали!

Опрос обратной связи

Наконец, пожалуйста, заполните этот опрос , чтобы оставить отзыв о своем опыте работы с этой лабораторией кода.