Skip to content

Commit 5b4a195

Browse files
committed
Grabbing final versions of updated XDR files because it's easier than trying to figure out the TOC merge conflicts after the big refactor.
1 parent f4b7ed9 commit 5b4a195

6 files changed

Lines changed: 471 additions & 33 deletions

File tree

hub/apps/develop/toc.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,14 @@ items:
462462
href: actions/action-provider-manifest.md
463463
- name: Cross Device People API
464464
href: windows-integration/cross-device-people-api.md
465-
- name: Cross Device Resume
466-
href: windows-integration/cross-device-resume.md
465+
- name: Cross Device Resume (XDR)
466+
items:
467+
- name: Overview
468+
href: develop/windows-integration/cross-device-resume-overview.md
469+
- name: Cross Device Resume with Android Apps
470+
href: develop/windows-integration/cross-device-resume.md
471+
- name: WNS for Cross Device Resume
472+
href: develop/windows-integration/integrate-app-continuity.md
467473
- name: Default apps platform
468474
href: windows-integration/default-apps-platform.md
469475
- name: Pin to taskbar
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
description: This section of the documentation provides developer guidance for integrating with the Windows Cross Device Resume (XDR) feature.
3+
title: Cross Device Resume (XDR)
4+
ms.topic: concept-article
5+
ms.date: 12/10/2025
6+
ms.localizationpriority: medium
7+
# customer intent: As a Windows developer, I want to learn how to integrate my app with Windows so that I can provide a seamless experience for my users.
8+
---
9+
10+
# Cross Device Resume (XDR) Overview
11+
12+
Cross Device Resume (XDR) is a Windows feature that enables developers to add continuity between Windows apps and apps on other platforms. This section of the documentation provides information about the technologies that are currently available to enable XDR scenarios.
13+
14+
## In this section
15+
16+
| Topic | Description |
17+
|-------|-------------|
18+
| [Use the Continuity SDK to implement XDR for Android and Windows Applications](cross-device-resume.md) | Learn how to use the Continuity SDK to implement XDR with Android apps. |
19+
| [Use WNS Notifications to implement XDR](integrate-app-continuity.md) | Learn how to implement Cross Device Resume (XDR) using Windows Push Notification Service (WNS) raw notifications. |
20+
21+

hub/apps/develop/windows-integration/cross-device-resume.md

Lines changed: 195 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
2-
title: Cross Device Resume (XDR) Using Continuity SDK
2+
title: Use the Continuity SDK to implement Cross Device Resume (XDR) for Android and Windows Applications
33
description: Guidelines for first & third party developers to integrate with Windows XDR experiences using the Continuity SDK.
44
ms.date: 08/12/2025
55
ms.topic: how-to
66
# customer intent: As a Windows developer, I want to learn how to integrate my app with Windows XDR experiences so that I can provide a seamless experience for my users.
77
---
88

9-
# Cross Device Resume (XDR) using Continuity SDK (Android and Windows Applications)
9+
# Use the Continuity SDK to implement Cross Device Resume (XDR) for Android and Windows Applications
1010

1111
This article provides comprehensive guidelines for first-party and third-party developers on how to integrate features using the Continuity SDK in your applications. The Continuity SDK enables seamless cross-device experiences, allowing users to resume activities across different platforms, including Android and Windows.
1212

@@ -75,76 +75,236 @@ The App must:
7575
1. After calling the Initialize function, a callback that implements IAppContextEventHandler should be triggered.
7676
1. Send/Delete **AppContext**:
7777
1. After initializing the SDK, if **onContextRequestReceived** is called, it indicates the connection is established. The app can then send (including create and update) **AppContext** to LTW or delete **AppContext** from LTW.
78+
1. If there is no connection between the phone and PC and the app sends **AppContext** to LTW, the app will receive **onContextResponseError** with the message “PC is not connected.”
79+
1. When the connection is re-established, **onContextRequestReceived** is called again. The app can then send the current AppContext to LTW.
7880
1. After **onSyncServiceDisconnected** or deinitializing the SDK, the app should not send an **AppContext**.
7981

8082
Below is a code example. For all the required and optional fields in **AppContext**, please refer to the [AppContext description](#appcontext).
8183

8284
The following Android code snippet demonstrates how to make API requests using the Continuity SDK:
8385

8486
```kotlin MainActivity.kt
85-
class MainActivity : ComponentActivity() {
87+
import android.os.Bundle
88+
import android.util.Log
89+
import android.widget.Button
90+
import android.widget.TextView
91+
import android.widget.Toast
92+
import androidx.activity.enableEdgeToEdge
93+
import androidx.appcompat.app.AppCompatActivity
94+
import androidx.core.view.ViewCompat
95+
import androidx.core.view.WindowInsetsCompat
96+
import androidx.lifecycle.LiveData
97+
import androidx.lifecycle.MutableLiveData
98+
import androidx.lifecycle.Observer
99+
import com.microsoft.crossdevicesdk.continuity.AppContext
100+
import com.microsoft.crossdevicesdk.continuity.AppContextManager
101+
import com.microsoft.crossdevicesdk.continuity.ContextRequestInfo
102+
import com.microsoft.crossdevicesdk.continuity.IAppContextEventHandler
103+
import com.microsoft.crossdevicesdk.continuity.IAppContextResponse
104+
import com.microsoft.crossdevicesdk.continuity.LogUtils
105+
import com.microsoft.crossdevicesdk.continuity.ProtocolConstants
106+
import java.util.UUID
107+
108+
109+
110+
class MainActivity : AppCompatActivity() {
111+
112+
//Make buttons member variables ---
113+
private lateinit var buttonSend: Button
114+
private lateinit var buttonDelete: Button
115+
private lateinit var buttonUpdate: Button
86116

87-
// Required code for Continuity SDK integration
88117
private val appContextResponse = object : IAppContextResponse {
89118
override fun onContextResponseSuccess(response: AppContext) {
90119
Log.d("MainActivity", "onContextResponseSuccess")
120+
runOnUiThread {
121+
Toast.makeText(
122+
this@MainActivity,
123+
"Context response success: ${response.contextId}",
124+
Toast.LENGTH_SHORT
125+
).show()
126+
}
91127
}
92128

93129
override fun onContextResponseError(response: AppContext, throwable: Throwable) {
94-
Log.d("MainActivity", "onContextResponseError")
130+
Log.d("MainActivity", "onContextResponseError: ${throwable.message}")
131+
runOnUiThread {
132+
Toast.makeText(
133+
this@MainActivity,
134+
"Context response error: ${throwable.message}",
135+
Toast.LENGTH_SHORT
136+
).show()
137+
138+
// Check if the error message contains the specific string
139+
if (throwable.message?.contains("PC is not connected") == true) {
140+
//App should stop sending intent once this callback is received
141+
}
142+
}
95143
}
96144
}
97145

98146
private lateinit var appContextEventHandler: IAppContextEventHandler
99147

100-
private val appContext: AppContext = AppContext()
148+
private val _currentAppContext = MutableLiveData<AppContext?>()
149+
150+
private val currentAppContext: LiveData<AppContext?> get() = _currentAppContext
101151

102152
override fun onCreate(savedInstanceState: Bundle?) {
103153
super.onCreate(savedInstanceState)
154+
enableEdgeToEdge()
155+
setContentView(R.layout.activity_main)
156+
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
157+
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
158+
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
159+
insets
160+
}
161+
162+
LogUtils.setDebugMode(true)
104163
var ready = false
105-
// Existing code...
164+
buttonSend = findViewById(R.id.buttonSend)
165+
buttonDelete = findViewById(R.id.buttonDelete)
166+
buttonUpdate = findViewById(R.id.buttonUpdate)
167+
setButtonDisabled(buttonSend)
168+
setButtonDisabled(buttonDelete)
169+
setButtonDisabled(buttonUpdate)
170+
171+
buttonSend.setOnClickListener {
172+
if (ready) {
173+
sendResumeActivity()
174+
}
175+
}
176+
177+
buttonDelete.setOnClickListener {
178+
if (ready) {
179+
deleteResumeActivity()
180+
}
181+
}
182+
183+
buttonUpdate.setOnClickListener {
184+
if (ready) {
185+
updateResumeActivity()
186+
}
187+
}
106188

107-
// Required code for Continuity SDK integration
108189
appContextEventHandler = object : IAppContextEventHandler {
190+
109191
override fun onContextRequestReceived(contextRequestInfo: ContextRequestInfo) {
110-
Log.d("MainActivity", "onContextRequestReceived")
192+
LogUtils.d("MainActivity", "onContextRequestReceived")
111193
ready = true
112-
113-
sendResume()
114-
}
194+
setButtonEnabled(buttonSend)
195+
setButtonEnabled(buttonDelete)
196+
setButtonEnabled(buttonUpdate)
115197

116-
override fun onSyncServiceDisconnected() {
117-
Log.d("MainActivity", "onSyncServiceDisconnected")
118198
}
119199

200+
201+
120202
override fun onInvalidContextRequestReceived(throwable: Throwable) {
121203
Log.d("MainActivity", "onInvalidContextRequestReceived")
204+
205+
}
206+
207+
208+
209+
override fun onSyncServiceDisconnected() {
210+
Log.d("MainActivity", "onSyncServiceDisconnected")
122211
ready = false
123-
deleteResume()
212+
setButtonDisabled(buttonSend)
213+
setButtonDisabled(buttonDelete)
124214
}
125215
}
126216

127-
// Required code for Continuity SDK integration
217+
// Initialize the AppContextManager
128218
AppContextManager.initialize(this.applicationContext, appContextEventHandler)
219+
220+
// Update currentAppContext text view.
221+
val textView = findViewById<TextView>(R.id.appContext)
222+
223+
currentAppContext.observe(this, Observer { appContext ->
224+
appContext?.let {
225+
textView.text =
226+
"Current app context: ${it.contextId}\n App ID: ${it.appId}\n Created: ${it.createTime}\n Updated: ${it.lastUpdatedTime}\n Type: ${it.type}"
227+
Log.d("MainActivity", "Current app context: ${it.contextId}")
228+
} ?: run {
229+
textView.text = "No current app context available"
230+
Log.d("MainActivity", "No current app context available")
231+
}
232+
})
129233
}
130234

131-
// Required code for Continuity SDK integration
132-
private fun sendResume() {
133-
appContext.setContextId("13f53be4-d0d1-448a-8c78-af28820af119")
134-
appContext.setAppId(this.packageName)
135-
appContext.type = ProtocolConstants.TYPE_RESUME_ACTIVITY
136-
137-
// Set context fields. Refer to the AppContext section for detailed field descriptions.
235+
236+
// Send resume activity to LTW
237+
private fun sendResumeActivity() {
238+
val appContext = AppContext().apply {
239+
this.contextId = generateContextId()
240+
this.appId = applicationContext.packageName
241+
this.createTime = System.currentTimeMillis()
242+
this.lastUpdatedTime = System.currentTimeMillis()
243+
this.type = ProtocolConstants.TYPE_RESUME_ACTIVITY
244+
}
245+
246+
_currentAppContext.value = appContext
138247
AppContextManager.sendAppContext(this.applicationContext, appContext, appContextResponse)
139248
}
140249

141-
// Required code for Continuity SDK integration
142-
private fun deleteResume() {
143-
AppContextManager.deleteAppContext(this.applicationContext, "13f53be4-d0d1-448a-8c78-af28820af119", appContextResponse)
250+
// Delete resume activity from LTW
251+
private fun deleteResumeActivity() {
252+
currentAppContext.value?.let {
253+
AppContextManager.deleteAppContext(
254+
this.applicationContext,
255+
it.contextId,
256+
appContextResponse
257+
)
258+
_currentAppContext.value = null
259+
} ?: run {
260+
Toast.makeText(this, "No resume activity to delete", Toast.LENGTH_SHORT).show()
261+
Log.d("MainActivity", "No resume activity to delete")
262+
}
263+
}
264+
265+
private fun updateResumeActivity() {
266+
currentAppContext.value?.let {
267+
it.lastUpdatedTime = System.currentTimeMillis()
268+
AppContextManager.sendAppContext(this.applicationContext, it, appContextResponse)
269+
_currentAppContext.postValue(it)
270+
} ?: run {
271+
Toast.makeText(this, "No resume activity to update", Toast.LENGTH_SHORT).show()
272+
Log.d("MainActivity", "No resume activity to update")
273+
}
274+
}
275+
276+
private fun setButtonDisabled(button: Button) {
277+
button.isEnabled = false
278+
button.alpha = 0.5f
279+
}
280+
281+
private fun setButtonEnabled(button: Button) {
282+
button.isEnabled = true
283+
button.alpha = 1.0f
144284
}
145285

146-
// Existing code
147-
}
286+
override fun onDestroy() {
287+
super.onDestroy()
288+
// Deinitialize the AppContextManager
289+
AppContextManager.deInitialize(this.applicationContext)
290+
}
291+
292+
override fun onStart() {
293+
super.onStart()
294+
// AppContextManager.initialize(this.applicationContext, appContextEventHandler)
295+
}
296+
297+
298+
override fun onStop() {
299+
super.onStop()
300+
// AppContextManager.deInitialize(this.applicationContext)
301+
}
302+
303+
private fun generateContextId(): String {
304+
return "${packageName}.${UUID.randomUUID()}"
305+
}
306+
307+
}
148308
```
149309

150310
## Integration validation steps
@@ -170,7 +330,13 @@ The following steps are required to prepare for the integration validation:
170330
Next, follow these steps to validate the integration:
171331

172332
1. Launch the app and initialize the SDK. Confirm that **onContextRequestReceived** is called.
173-
1. After **onContextRequestReceived** has been called, the app can send the **AppContext** to LTW. If **onContextResponseSuccess** is called after sending **AppContext**, the SDK integration is successful.
333+
1. After **onContextRequestReceived** has been called, the app can send the **AppContext** to LTW. If **onContextResponseSuccess** is called after sending **AppContext**, the SDK integration is successful.
334+
1. If the app sends **AppContext** while the PC is locked or disconnected, verify that **onContextResponseError** is called with “PC is not connected.”
335+
1. When the connection is restored, ensure **onContextRequestReceived** is called again and app can then send the current AppContext to LTW.
336+
337+
The screenshot below shows the log entry when the PC is disconnected with the error message "PC is not connected" and the log entry after reconnection when **onContextRequestReceived** is called again.
338+
339+
:::image type="content" source="images/xdr-not-connected-logs.png" alt-text="A screenshot of Windows log entries showing the PC is not connected error message and the subsequent onContextRequestReceived log entry after reconnection.":::
174340

175341
## AppContext
176342

74.3 KB
Loading

hub/apps/develop/windows-integration/index.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
description: This section of the documentation provides developer guidance for integrating with Windows system components and other Windows features.
33
title: Integrate with Windows
44
ms.topic: concept-article
5-
ms.date: 12/05/2025
5+
ms.date: 04/10/2025
66
ms.localizationpriority: medium
77
# customer intent: As a Windows developer, I want to learn how to integrate my app with Windows so that I can provide a seamless experience for my users.
88
---
@@ -22,13 +22,21 @@ The following table lists the Windows system components that support integration
2222
| [Search providers](../search/search-providers.md) | Learn how to integrate into the Windows Search experience. |
2323
| [Widget providers](../widgets/widget-providers.md) | Learn how to implement a Windows widget service provider to support your app. |
2424

25+
## Windows features using AI
26+
27+
| Feature | Description |
28+
|--|--|
29+
| [Recall](./recall/index.md) | Learn how to use the AI-assisted Recall feature with the User Activity API in Windows. |
30+
| [Click to Do](./click-to-do.md) | Learn how to use the AI-assisted Click to Do feature in Windows. |
31+
2532
## Other Windows integration features
2633

2734
The following table lists other Windows integration features that support 3rd party developers.
2835

2936
| Feature | Description |
3037
|--|--|
31-
| [Cross Device Resume (XDR) using Continuity SDK](cross-device-resume.md) | Guidelines for first & third party developers to integrate with Windows XDR experiences using the Continuity SDK. |
38+
| [Cross Device Resume (XDR)](cross-device-resume-overview.md) | Developer guidance for integrating with the Windows Cross Device Resume (XDR) feature. |
39+
| [Integrate with application continuity and WNS notifications](integrate-app-continuity.md) | A step-by-step guide for first and third parties to integrate application continuity (resume) with Windows Push Notification Service (WNS) raw notifications. |
3240
| [Smart App Control](../smart-app-control/overview.md) | Smart App Control is a new app execution control feature that combines Microsoft’s app intelligence services and Windows' code integrity features to protect users from untrusted or potentially dangerous code. |
3341
| [Windows Share integration](integrate-sharesheet-overview.md) | The Windows Share Sheet is a system-provided UI that enables users to share content from your app with other apps. |
3442
| [Windows People experiences](cross-device-people-api.md) | Learn how third party apps can integrate with Windows People experiences using APIs to store their contacts. |

0 commit comments

Comments
 (0)