Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo fornisce linee guida complete per sviluppatori proprietari e di terze parti su come integrare le funzionalità usando Continuity SDK nelle applicazioni. Continuity SDK offre esperienze multi-dispositivo senza interruzioni, consentendo agli utenti di riprendere le attività in diverse piattaforme, tra cui Android e Windows.
Seguendo queste indicazioni, è possibile creare un'esperienza utente uniforme e integrata tra più dispositivi sfruttando XDR usando Continuity SDK.
Importante
Onboarding in Riprendi in Windows
Resume è una funzionalità di accesso limitato (LAF). Per ottenere l'accesso a questa API, è necessario ottenere l'approvazione da Microsoft per interagire con il pacchetto "Collega a Windows" nei dispositivi mobili Android.
Per richiedere l'accesso, inviare un messaggio di posta elettronica wincrossdeviceapi@microsoft.com con le informazioni elencate di seguito:
- Descrizione dell'esperienza utente
- Screenshot dell'applicazione in cui un utente accede in modo nativo al Web o ai documenti
- PackageId dell'applicazione
- URL di Google Play Store per l'applicazione
Se la richiesta viene approvata, si riceveranno istruzioni su come sbloccare la funzionalità. Le approvazioni saranno basate sulla comunicazione, purché lo scenario soddisfi i requisiti dello scenario descritti.
Prerequisiti
Per le applicazioni Android, verificare che i requisiti seguenti siano soddisfatti prima dell'integrazione di Continuity SDK:
- Versione minima dell'SDK: 24
- Versione Kotlin: 1.9.x
- Collegamento a Windows (LTW): 1.241101.XX
Per le applicazioni Windows, verificare che siano soddisfatti i requisiti seguenti:
- Versione minima di Windows: Windows 11
- Ambiente di sviluppo: Visual Studio 2019 o versione successiva
Annotazioni
Le applicazioni iOS non sono attualmente supportate per l'integrazione con Continuity SDK.
Configurare l'ambiente di sviluppo
Le sezioni seguenti forniscono istruzioni dettagliate per configurare l'ambiente di sviluppo per le applicazioni Android e Windows.
Configurazione di Android
Per configurare l'ambiente di sviluppo per Android, seguire questa procedura:
Per configurare il bundle, scaricare e usare il file con estensione aar tramite le librerie fornite nelle versioni seguenti: Windows Cross-Device SDK.
Aggiungere i meta tag nel file AndroidManifest.xml dell'applicazione Android. Il frammento di codice seguente illustra come aggiungere i meta tag necessari:
<meta-data android:name="com.microsoft.crossdevice.resumeActivityProvider" android:value="true" /> <meta-data android:name="com.microsoft.crossdevice.trigger.PartnerApp" android:value="4" />
Passaggi di integrazione dell'API
Dopo le dichiarazioni del manifesto, gli sviluppatori di app possono inviare facilmente il contesto dell'app seguendo un semplice esempio di codice.
L'app deve:
- Inizializzare/DeInitializzare continuity SDK:
- L'app deve determinare il tempo appropriato per chiamare le funzioni Initialize e DeInitialize.
- Dopo aver chiamato la funzione Initialize, deve essere attivato un callback che implementa IAppContextEventHandler.
- Invia/Elimina AppContext:
- Dopo l'inizializzazione dell'SDK, se viene chiamato onContextRequestReceived , indica che la connessione viene stabilita. L'app può quindi inviare (inclusa la creazione e l'aggiornamento) AppContext a LTW o eliminare AppContext da LTW.
- Se non è presente alcuna connessione tra il telefono e il PC e l'app invia AppContext a LTW, l'app riceverà onContextResponseError con il messaggio "PC non è connesso".
- Quando la connessione viene ristabilita, viene chiamato di nuovo onContextRequestReceived . L'app può quindi inviare l'oggetto AppContext corrente a LTW.
- Dopo che onSyncServiceDisconnected o la deinitializing dell'SDK, l'app non deve inviare un Oggetto AppContext.
Di seguito è riportato un esempio di codice. Per tutti i campi obbligatori e facoltativi in AppContext, vedere la descrizione di AppContext.
Il frammento di codice Android seguente illustra come effettuare richieste API usando Continuity 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()}"
}
}
Passaggi di convalida dell'integrazione
Per convalidare l'integrazione di Continuity SDK nell'applicazione, seguire questa procedura:
Preparazione
Per preparare la convalida dell'integrazione sono necessari i passaggi seguenti:
Verificare che sia installato LTW privato.
Connettere LTW al PC:
Per istruzioni, vedere Come gestire il dispositivo mobile nel PC .
Annotazioni
Se dopo aver scansionato il codice a matrice non viene reindirizzato a LTW, aprire prima LTW ed eseguire la scansione del codice a matrice all'interno dell'app.
Verificare che l'app partner abbia integrato Continuity SDK.
Validation
Seguire quindi questa procedura per convalidare l'integrazione:
- Avviare l'app e inizializzare l'SDK. Verificare che onContextRequestReceived sia chiamato.
- Dopo aver chiamato onContextRequestReceived , l'app può inviare AppContext a LTW. Se onContextResponseSuccess viene chiamato dopo l'invio di AppContext, l'integrazione dell'SDK ha esito positivo.
- Se l'app invia AppContext mentre il PC è bloccato o disconnesso, verificare che onContextResponseError venga chiamato con "PC non è connesso".
- Quando la connessione viene ripristinata, verificare che onContextRequestReceived venga chiamato di nuovo e l'app possa quindi inviare l'attuale contesto app a LTW.
Lo screenshot seguente mostra la voce di log quando il PC è disconnesso con il messaggio di errore "PC non è connesso" e la voce di log dopo la riconnessione quando viene chiamato di nuovo onContextRequestReceived .
AppContext
XDR definisce AppContext come metadati tramite cui XDR può comprendere quale app riprendere, insieme al contesto con cui deve essere ripresa l'applicazione. Le app possono usare le attività per consentire agli utenti di tornare a ciò che stavano facendo nell'app, in più dispositivi. Le attività create da qualsiasi app per dispositivi mobili vengono visualizzate nei dispositivi Windows degli utenti, purché tali dispositivi siano stati sottoposti a provisioning di Cross Device Experience Host (CDEH).
Ogni applicazione è diversa e spetta a Windows comprendere l'applicazione di destinazione per riprendere e fino a applicazioni specifiche in Windows per comprendere il contesto. XDR propone uno schema generico che può soddisfare i requisiti per tutti gli scenari di ripresa delle app di terze parti e di terze parti.
contextId
- Obbligatorio: Sì
- Descrizione: identificatore univoco usato per distinguere un Oggetto AppContext da un altro. Garantisce che ogni AppContext sia identificabile in modo univoco.
- Utilizzo: assicurarsi di generare un contextId univoco per ogni AppContext per evitare conflitti.
type
- Obbligatorio: Sì
- Descrizione: flag binario che indica il tipo di AppContext inviato a Collega a Windows (LTW). Il valore deve essere coerente con requestedContextType.
- Utilizzo: impostare questo flag in base al tipo di contesto che si sta inviando. Ad esempio:
ProtocolConstants.TYPE_RESUME_ACTIVITY.
createTime
- Obbligatorio: Sì
- Descrizione: questo timestamp rappresenta l'ora di creazione di AppContext.
- Utilizzo: registrare l'ora esatta di creazione di AppContext .
intentUri
- Obbligatorio: No, se viene fornito il collegamento Web
- Descrizione: questo URI indica quale app può continuare l'appContext passata dal dispositivo di origine.
- Utilizzo: specificare questa opzione se si vuole specificare una particolare app per gestire il contesto.
weblink
- Obbligatorio: No, se viene specificato intentUri
- Descrizione: questo URI viene usato per avviare l'endpoint Web dell'applicazione se sceglie di non usare le app dello Store. Questo parametro viene usato solo quando non viene specificato intentUri . Se vengono specificati entrambi, intentUri verrà usato per riprendere l'applicazione in Windows.
- Utilizzo: da usare solo se l'applicazione vuole riprendere negli endpoint Web e non nelle applicazioni dello Store.
appId
- Obbligatorio: Sì
- Descrizione: nome del pacchetto dell'applicazione per cui si trova il contesto.
- Utilizzo: impostare questa opzione sul nome del pacchetto dell'applicazione.
title
- Obbligatorio: Sì
- Descrizione: si tratta del titolo di AppContext, ad esempio un nome di documento o un titolo della pagina Web.
- Utilizzo: specificare un titolo significativo che rappresenta AppContext.
Anteprima
- Obbligatorio: No
- Descrizione: byte dell'immagine di anteprima che può rappresentare AppContext.
- Utilizzo: fornire un'immagine di anteprima, se disponibile, per offrire agli utenti una rappresentazione visiva di AppContext.
Vita
- Obbligatorio: No
- Descrizione: durata in
AppContextmillisecondi. Viene usato solo per scenari in corso. Se non è impostato, il valore predefinito è 5 minuti. - Utilizzo: impostare questa opzione per definire per quanto tempo deve
AppContextessere valido. È possibile impostare un valore fino a un massimo di 5 minuti. Qualsiasi valore maggiore verrà abbreviato automaticamente a 5 minuti.
URI finalità
Gli URI consentono di avviare un'altra app per eseguire un'attività specifica, abilitando scenari utili da app a app. Per altre informazioni sull'avvio di app con URI, vedere Avviare l'app di Windows predefinita per un URI e Creare collegamenti diretti al contenuto dell'app | Sviluppatori Android.
Gestione delle risposte api in Windows
Questa sezione descrive come gestire le risposte api nelle applicazioni Windows. Continuity SDK consente di gestire le risposte api per le app Win32, UWP e Windows App SDK.
Esempio di app Win32
Per consentire alle app Win32 di gestire l'avvio dell'URI del protocollo, sono necessari i passaggi seguenti:
Prima di tutto, è necessario creare una voce nel Registro di sistema come indicato di seguito:
[HKEY_CLASSES_ROOT\partnerapp] @="URL:PartnerApp Protocol" "URL Protocol"="" [HKEY_CLASSES_ROOT\partnerapp\shell\open\command] @="\"C:\\path\\to\\PartnerAppExecutable.exe\" \"%1\""L'avvio deve essere gestito nella funzione principale dell'app 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; }
App UWP
Per le app UWP, l'URI del protocollo può essere registrato nel manifesto dell'app del progetto. I passaggi seguenti illustrano come gestire l'attivazione del protocollo in un'app UWP.
In primo luogo, l'URI del
Package.appxmanifestprotocollo viene registrato nel file come segue:<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>Quindi, nel file eseguire l'override
App.xaml.csdelOnActivatedmetodo come indicato di seguito:public partial class App { protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs; // TODO: Handle URI activation // The received URI is eventArgs.Uri.AbsoluteUri } } }
Per altre informazioni sulla gestione dell'avvio dell'URI nelle app UWP, vedi il passaggio 3 in Gestire l'attivazione URI.
Esempio di WinUI 3
Il frammento di codice seguente illustra come gestire l'attivazione del protocollo in un'app WinUI C++ con 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
}
}
Collegamento Web
L'uso di un collegamento Web avvierà l'endpoint Web dell'applicazione. Gli sviluppatori di app devono assicurarsi che il collegamento Web fornito dall'applicazione Android sia valido perché XDR userà il browser predefinito del sistema per reindirizzare al weblink fornito.
Gestione degli argomenti ottenuti da Cross Device Resume
È responsabilità di ogni app deserializzare e decrittografare l'argomento ricevuto ed elaborare le informazioni di conseguenza per trasferire il contesto in corso dal telefono al PC. Ad esempio, se una chiamata deve essere trasferita, l'app deve essere in grado di comunicare il contesto dal telefono e l'app desktop deve comprendere il contesto in modo appropriato e continuare il caricamento.