Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье приведены комплексные рекомендации для разработчиков первых и третьих сторон по интеграции возможностей с использованием пакета SDK Continuity в ваших приложениях. Пакет SDK для обеспечения непрерывности обеспечивает простое взаимодействие между устройствами, позволяя пользователям возобновлять действия на разных платформах, включая Android и Windows.
Следуя этому руководству, вы можете создать плавный и интегрированный пользовательский опыт на нескольких устройствах, используя XDR через Continuity SDK.
Это важно
Подключение к возобновлению работы в Windows
Резюме — это функция с ограниченным доступом (LAF). Чтобы получить доступ к этому API, необходимо получить одобрение от Microsoft для взаимодействия с пакетом "Link to Windows" на мобильных устройствах Android.
Чтобы запросить доступ, отправьте сообщение электронной почты wincrossdeviceapi@microsoft.com с информацией, перечисленной ниже:
- Описание пользовательского интерфейса
- Снимок экрана приложения, в котором пользователь изначально обращается к веб-сайту или документам
- PackageId вашего приложения
- URL-адрес магазина Google Play для приложения
Если запрос утвержден, вы получите инструкции по разблокировке функции. Утверждения будут основаны на ваших сообщениях, если ваш сценарий соответствует указанным требованиям сценария.
Предпосылки
Для приложений Android убедитесь, что перед интеграцией пакета SDK непрерывности выполняются следующие требования:
- Минимальная версия пакета SDK: 24
- Версия Kotlin: 1.9.x
- Ссылка на Windows (LTW): 1.241101.XX
Для приложений Windows убедитесь, что выполнены следующие требования:
- Минимальная версия Windows: Windows 11
- Среда разработки: Visual Studio 2019 или более поздней версии
Замечание
В настоящее время приложения iOS не поддерживаются для интеграции с пакетом SDK для непрерывности.
Настройка среды разработки
В следующих разделах приведены пошаговые инструкции по настройке среды разработки для приложений Android и Windows.
Настройка Android
Чтобы настроить среду разработки для Android, выполните следующие действия.
Чтобы настроить пакет, скачайте и используйте .aar файл через библиотеки, предоставляемые в следующих релизах: Windows Cross-Device SDK.
Добавьте метатеги в файл AndroidManifest.xml приложения Android. В следующем фрагменте кода показано, как добавить необходимые метатеги:
<meta-data android:name="com.microsoft.crossdevice.resumeActivityProvider" android:value="true" /> <meta-data android:name="com.microsoft.crossdevice.trigger.PartnerApp" android:value="4" />
Шаги интеграции API
После объявления манифеста разработчики приложений могут легко отправлять контекст приложения, следуя простому примеру кода.
Приложение должно:
- Инициализация/деинициализация SDK континуальности:
- Приложение должно определить подходящее время для вызова функций Initialize и DeInitialize.
- После вызова функции Initialize следует активировать обратный вызов, реализующий IAppContextEventHandler.
- Send/Delete AppContext:
- После инициализации пакета SDK, если вызывается onContextRequestReceived , оно указывает, что подключение установлено. Затем приложение может отправить (включая создание и обновление) AppContext в LTW или удалить AppContext из LTW.
- Если между телефоном и компьютером нет подключения, и приложение отправляет AppContext в LTW, приложение получит onContextResponseError с сообщением "Компьютер не подключен."
- При повторной установке подключения снова вызывается onContextRequestReceived . Приложение может затем отправить текущий AppContext в LTW.
- После отключения onSyncServiceDisconnected или деинициализации SDK приложение не должно отправлять AppContext.
Ниже приведен пример кода. Все обязательные и необязательные поля в AppContext см. в описании AppContext.
В следующем фрагменте кода Android показано, как выполнять запросы API с помощью пакета SDK для непрерывности:
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.microsoft.crossdevicesdk.continuity.AppContext
import com.microsoft.crossdevicesdk.continuity.AppContextManager
import com.microsoft.crossdevicesdk.continuity.ContextRequestInfo
import com.microsoft.crossdevicesdk.continuity.IAppContextEventHandler
import com.microsoft.crossdevicesdk.continuity.IAppContextResponse
import com.microsoft.crossdevicesdk.continuity.LogUtils
import com.microsoft.crossdevicesdk.continuity.ProtocolConstants
import java.util.UUID
class MainActivity : AppCompatActivity() {
//Make buttons member variables ---
private lateinit var buttonSend: Button
private lateinit var buttonDelete: Button
private lateinit var buttonUpdate: Button
private val appContextResponse = object : IAppContextResponse {
override fun onContextResponseSuccess(response: AppContext) {
Log.d("MainActivity", "onContextResponseSuccess")
runOnUiThread {
Toast.makeText(
this@MainActivity,
"Context response success: ${response.contextId}",
Toast.LENGTH_SHORT
).show()
}
}
override fun onContextResponseError(response: AppContext, throwable: Throwable) {
Log.d("MainActivity", "onContextResponseError: ${throwable.message}")
runOnUiThread {
Toast.makeText(
this@MainActivity,
"Context response error: ${throwable.message}",
Toast.LENGTH_SHORT
).show()
// Check if the error message contains the specific string
if (throwable.message?.contains("PC is not connected") == true) {
//App should stop sending intent once this callback is received
}
}
}
}
private lateinit var appContextEventHandler: IAppContextEventHandler
private val _currentAppContext = MutableLiveData<AppContext?>()
private val currentAppContext: LiveData<AppContext?> get() = _currentAppContext
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
LogUtils.setDebugMode(true)
var ready = false
buttonSend = findViewById(R.id.buttonSend)
buttonDelete = findViewById(R.id.buttonDelete)
buttonUpdate = findViewById(R.id.buttonUpdate)
setButtonDisabled(buttonSend)
setButtonDisabled(buttonDelete)
setButtonDisabled(buttonUpdate)
buttonSend.setOnClickListener {
if (ready) {
sendResumeActivity()
}
}
buttonDelete.setOnClickListener {
if (ready) {
deleteResumeActivity()
}
}
buttonUpdate.setOnClickListener {
if (ready) {
updateResumeActivity()
}
}
appContextEventHandler = object : IAppContextEventHandler {
override fun onContextRequestReceived(contextRequestInfo: ContextRequestInfo) {
LogUtils.d("MainActivity", "onContextRequestReceived")
ready = true
setButtonEnabled(buttonSend)
setButtonEnabled(buttonDelete)
setButtonEnabled(buttonUpdate)
}
override fun onInvalidContextRequestReceived(throwable: Throwable) {
Log.d("MainActivity", "onInvalidContextRequestReceived")
}
override fun onSyncServiceDisconnected() {
Log.d("MainActivity", "onSyncServiceDisconnected")
ready = false
setButtonDisabled(buttonSend)
setButtonDisabled(buttonDelete)
}
}
// Initialize the AppContextManager
AppContextManager.initialize(this.applicationContext, appContextEventHandler)
// Update currentAppContext text view.
val textView = findViewById<TextView>(R.id.appContext)
currentAppContext.observe(this, Observer { appContext ->
appContext?.let {
textView.text =
"Current app context: ${it.contextId}\n App ID: ${it.appId}\n Created: ${it.createTime}\n Updated: ${it.lastUpdatedTime}\n Type: ${it.type}"
Log.d("MainActivity", "Current app context: ${it.contextId}")
} ?: run {
textView.text = "No current app context available"
Log.d("MainActivity", "No current app context available")
}
})
}
// Send resume activity to LTW
private fun sendResumeActivity() {
val appContext = AppContext().apply {
this.contextId = generateContextId()
this.appId = applicationContext.packageName
this.createTime = System.currentTimeMillis()
this.lastUpdatedTime = System.currentTimeMillis()
this.type = ProtocolConstants.TYPE_RESUME_ACTIVITY
}
_currentAppContext.value = appContext
AppContextManager.sendAppContext(this.applicationContext, appContext, appContextResponse)
}
// Delete resume activity from LTW
private fun deleteResumeActivity() {
currentAppContext.value?.let {
AppContextManager.deleteAppContext(
this.applicationContext,
it.contextId,
appContextResponse
)
_currentAppContext.value = null
} ?: run {
Toast.makeText(this, "No resume activity to delete", Toast.LENGTH_SHORT).show()
Log.d("MainActivity", "No resume activity to delete")
}
}
private fun updateResumeActivity() {
currentAppContext.value?.let {
it.lastUpdatedTime = System.currentTimeMillis()
AppContextManager.sendAppContext(this.applicationContext, it, appContextResponse)
_currentAppContext.postValue(it)
} ?: run {
Toast.makeText(this, "No resume activity to update", Toast.LENGTH_SHORT).show()
Log.d("MainActivity", "No resume activity to update")
}
}
private fun setButtonDisabled(button: Button) {
button.isEnabled = false
button.alpha = 0.5f
}
private fun setButtonEnabled(button: Button) {
button.isEnabled = true
button.alpha = 1.0f
}
override fun onDestroy() {
super.onDestroy()
// Deinitialize the AppContextManager
AppContextManager.deInitialize(this.applicationContext)
}
override fun onStart() {
super.onStart()
// AppContextManager.initialize(this.applicationContext, appContextEventHandler)
}
override fun onStop() {
super.onStop()
// AppContextManager.deInitialize(this.applicationContext)
}
private fun generateContextId(): String {
return "${packageName}.${UUID.randomUUID()}"
}
}
Шаги проверки интеграции
Чтобы проверить интеграцию пакета SDK непрерывности в приложении, выполните следующие действия.
Подготовка
Чтобы подготовиться к проверке интеграции, необходимо выполнить следующие действия.
Убедитесь, что установлена приватная LTW.
Подключите LTW к компьютеру:
Инструкции см. в статье "Управление мобильным устройством на компьютере ".
Замечание
Если после сканирования QR-кода вы не перенаправляетесь в LTW, сначала откройте LTW и проверьте QR-код в приложении.
Убедитесь, что партнерское приложение интегрировано в пакет SDK Continuity.
Validation
Затем выполните следующие действия, чтобы проверить интеграцию:
- Запустите приложение и инициализируйте пакет SDK. Убедитесь, что вызывается onContextRequestReceived.
- После вызова onContextRequestReceived приложение может отправить AppContext в LTW. Если onContextResponseSuccess вызывается после отправки AppContext, интеграция пакета SDK выполнена успешно.
- Если приложение отправляет AppContext , пока компьютер заблокирован или отключен, убедитесь, что onContextResponseError вызывается с параметром "КОМПЬЮТЕР не подключен".
- При восстановлении подключения убедитесь, что вызывается onContextRequestReceived, и затем приложение может отправить текущий AppContext в LTW.
На снимке экрана ниже показана запись журнала при отключении компьютера с сообщением об ошибке "Компьютер не подключен" и запись журнала после повторного подключения при вызове onContextRequestReceived .
Снимок экрана записей журнала Windows, показывающий сообщение об ошибке "компьютер не подключен" и последующую запись журнала onContextRequestReceived после повторного подключения.
AppContext
XDR определяет AppContext как метаданные, с помощью которых XDR может понять, какое приложение нужно возобновить, а также контекст, с которым приложение должно быть возобновлено. Приложения могут использовать действия, чтобы пользователи могли вернуться к тому, что они делали в своем приложении, на нескольких устройствах. Действия, созданные любым мобильным приложением, отображаются на устройствах Windows пользователей до тех пор, пока эти устройства настроены для использования с узлом взаимодействия между устройствами (CDEH).
Каждое приложение отличается, и Windows отвечает за понимание целевой программы для восстановления, а конкретные приложения в Windows должны понимать контекст. XDR предлагает универсальную схему, которая может удовлетворять требованиям для всех приложений от первого лица, а также для сценариев возобновления сторонних приложений.
contextId
- Обязательный: Да
- Описание. Это уникальный идентификатор, используемый для различения одного AppContext от другого. Это гарантирует, что каждый AppContext является уникально идентифицируемым.
- Использование: обязательно создайте уникальный contextId для каждого AppContext , чтобы избежать конфликтов.
type
- Обязательный: Да
- Описание. Это двоичный флаг, указывающий тип AppContext , отправляемый в Link to Windows (LTW). Значение должно быть согласовано с запрошеннымContextType.
- Использование: задайте этот флаг в соответствии с типом отправленного контекста. Например:
ProtocolConstants.TYPE_RESUME_ACTIVITY.
createTime
- Обязательный: Да
- Описание. Эта метка времени представляет время создания AppContext.
- Использование: запишите точное время создания AppContext .
intentUri
- Обязательный: нет, если веб-ссылка предоставлена
- Описание. Этот универсальный код ресурса (URI) указывает, какое приложение может продолжить передачу AppContext с исходного устройства.
- Использование: укажите это, если вы хотите указать конкретное приложение для обработки контекста.
веб-ссылка
- Обязательно: Нет, если предоставлен intentUri
- Описание: Этот URI используется для запуска веб-адреса конечной точки приложения, если они выбирают не использовать магазинные приложения. Этот параметр используется только в том случае, если не указан параметр intentUri . Если оба указаны, функция intentUri будет использоваться для возобновления работы приложения в Windows.
- Использование: только если приложение хочет возобновить работу в веб-конечных точках, а не в приложениях магазина.
идентификатор приложения (appId)
- Обязательный: Да
- Описание: Это имя пакета приложения, для которого предоставлен контекст.
- Использование. Задайте для этого имя пакета приложения.
title
- Обязательный: Да
- Описание. Это название AppContext, например имя документа или название веб-страницы.
- Использование: укажите понятное название, представляющее AppContext.
Предварительный просмотр
- Обязательный: Нет
- Описание. Это байты изображения предварительного просмотра, которые могут представлять AppContext.
- Использование: предоставьте предварительный просмотр изображения, если оно доступно для предоставления пользователям визуального представления AppContext.
Продолжительность жизни
- Обязательный: Нет
- Описание: это время существования
AppContextв миллисекундах. Он используется только для текущих сценариев. Если значение не задано, значение по умолчанию — 5 минут. - Использование. Задайте для этого значение, чтобы определить, сколько времени
AppContextдолжно быть допустимым. Можно задать значение не более 5 минут. Любое большее значение будет автоматически сокращено до 5 минут.
URI для намерений
URI позволяет запускать другое приложение для выполнения определенной задачи, что дает возможность использования полезных сценариев взаимодействия между приложениями. Дополнительные сведения о запуске приложений с помощью URI см. в статье Запуск приложения Windows по умолчанию для URI и Create Deep Links to App Content | Разработчики Android.
Обработка ответов API в Windows
В этом разделе описывается обработка ответов API в приложениях Windows. Пакет SDK непрерывности предоставляет способ обработки ответов API для приложений Win32 и WinUI.
Пример приложения Win32
Чтобы приложения Win32 обрабатывали запуск URI протокола, необходимо выполнить следующие действия.
Во-первых, запись должна быть сделана в реестр следующим образом:
[HKEY_CLASSES_ROOT\partnerapp] @="URL:PartnerApp Protocol" "URL Protocol"="" [HKEY_CLASSES_ROOT\partnerapp\shell\open\command] @="\"C:\\path\\to\\PartnerAppExecutable.exe\" \"%1\""Запуск должен обрабатываться в главной функции приложения Win32:
#include <windows.h> #include <shellapi.h> #include <string> #include <iostream> int CALLBACK wWinMain(HINSTANCE, HINSTANCE, PWSTR lpCmdLine, int) { // Check if there's an argument passed via lpCmdLine std::wstring cmdLine(lpCmdLine); std::wstring arguments; if (!cmdLine.empty()) { // Check if the command-line argument starts with "partnerapp://", indicating a URI launch if (cmdLine.find(L"partnerapp://") == 0) { // This is a URI protocol launch // Process the URI as needed // Example: Extract action and parameters from the URI arguments = cmdLine; // or further parse as required } else { // Launched by command line or activation APIs } } else { // Handle cases where no arguments were passed } return 0; }
Приложения WinUI
Для упакованных приложений WinUI URI протокола можно зарегистрировать в манифесте приложения проекта. Ниже показано, как обрабатывать активацию протокола в приложении WinUI.
Во-первых, URI протокола регистрируется в
Package.appxmanifestфайле следующим образом:<Applications> <Application Id= ... > <Extensions> <uap:Extension Category="windows.protocol"> <uap:Protocol Name="alsdk"> <uap:Logo>images\icon.png</uap:Logo> <uap:DisplayName>SDK Sample URI Scheme</uap:DisplayName> </uap:Protocol> </uap:Extension> </Extensions> ... </Application> <Applications>
Пример WinUI 3
В следующем фрагменте кода показано, как обрабатывать активацию протокола в приложении WinUI C++ с помощью Windows App SDK:
void App::OnActivated(winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args)
{
if (args.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::Protocol)
{
auto protocolArgs = args.as<winrt::Windows::ApplicationModel::Activation::ProtocolActivatedEventArgs>();
auto uri = protocolArgs.Uri();
std::wstring uriString = uri.AbsoluteUri().c_str();
//Process the URI as per argument scheme
}
}
Веб-ссылка
С помощью веб-ссылки запустится веб-конечная точка приложения. Разработчикам приложений необходимо убедиться, что веб-ссылка, предоставленная из приложения Android, действительна, так как XDR будет использовать браузер по умолчанию системы для перенаправления на веб-ссылку, предоставленную.
Обработка аргументов, полученных из возобновления работы между устройствами
Каждое приложение несет ответственность за десериализацию и расшифровку полученного аргумента и обработки информации соответствующим образом для передачи текущего контекста с телефона на компьютер. Например, если необходимо передать звонок, приложение должно иметь возможность передавать этот контекст с телефона, и настольное приложение должно правильно понимать этот контекст и продолжать загрузку.
Связанный контент
Windows developer