RemoteActivityHelper


class RemoteActivityHelper


Support for opening android intents on other devices.

The following example opens play store for the given app on another device:

val remoteActivityHelper = RemoteActivityHelper(context, executor)

val result = remoteActivityHelper.startRemoteActivity(
Intent(Intent.ACTION_VIEW)
.setData(
Uri.parse("http://play.google.com/store/apps/details?id=com.example.myapp"))
.addCategory(Intent.CATEGORY_BROWSABLE),
nodeId)

startRemoteActivity returns a ListenableFuture, which is completed after the intent has been sent or failed if there was an issue with sending the intent.

nodeId is the opaque string that represents a node in the Android Wear network. For the given device, it can obtained by NodeClient.getLocalNode() and the list of nodes to which this device is currently connected can be obtained by NodeClient.getConnectedNodes(). More information about this can be found here.

Summary

Constants

const String
const String

Permission required to use startPhoneActivityWithUnlock.

const Int

Result code passed to ResultReceiver.send when a remote intent failed to send.

const Int

Result code passed to ResultReceiver.send when a remote intent was sent successfully.

const Int

Indicates that remote activity is available.

const Int

Indicates that remote activity is temporarily unavailable.

const Int

The remote auth's availability is unknown.

const Int

The remote activity's availability is unknown.

Public companion functions

Intent?

Returns the android.content.Intent extra specifying remote intent.

String?

Returns the String extra specifying node ID of remote intent.

Public constructors

RemoteActivityHelper(context: Context, executor: Executor)

Public functions

ListenableFuture<Void>
@RequiresApi(value = 37)
@RequiresPermission(value = "com.google.wear.permission.SEND_CONTINUE_ACTIVITY_ON_PHONE")
startPhoneActivityWithUnlock(
    callerPackage: String,
    targetPackage: String,
    targetAction: String,
    targetUri: Uri,
    targetCategories: List<String>
)

Starts an activity on the companion phone device, requesting a remote unlock.

ListenableFuture<Void>
startRemoteActivity(targetIntent: Intent, targetNodeId: String?)

Start an activity on another device.

ListenableFuture<Void>
startRemoteActivityAttemptUnlock(
    targetUri: Uri,
    targetCategories: List<String>
)

Starts an activity on the companion phone device, requesting a remote unlock if supported and PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONE is granted.

Public properties

Flow<Int>

Status of whether RemoteActivityHelper can startRemoteActivity, if known.

Constants

ACTION_REMOTE_INTENT

const val ACTION_REMOTE_INTENTString

PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONE

const val PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONEString

Permission required to use startPhoneActivityWithUnlock.

RESULT_FAILED

const val RESULT_FAILED = 1: Int

Result code passed to ResultReceiver.send when a remote intent failed to send.

RESULT_OK

const val RESULT_OK = 0: Int

Result code passed to ResultReceiver.send when a remote intent was sent successfully.

STATUS_AVAILABLE

const val STATUS_AVAILABLE = 3: Int

Indicates that remote activity is available.

There is a connected device capable to handle the remote interaction.

STATUS_TEMPORARILY_UNAVAILABLE

const val STATUS_TEMPORARILY_UNAVAILABLE = 2: Int

Indicates that remote activity is temporarily unavailable.

There is a known paired device, but it is not currently connected or reachable to handle the remote interaction.

STATUS_UNAVAILABLE

const val STATUS_UNAVAILABLE = 1: Int

The remote auth's availability is unknown.

On older devices, STATUS_UNKNOWN is returned as we can not determine the availability states. To preserve compatibility with existing devices behavior, try startRemoteActivity and handle error codes accordingly.

STATUS_UNKNOWN

const val STATUS_UNKNOWN = 0: Int

The remote activity's availability is unknown.

Public companion functions

getTargetIntent

Added in 1.0.0
fun getTargetIntent(intent: Intent): Intent?

Returns the android.content.Intent extra specifying remote intent.

Parameters
intent: Intent

The intent holding configuration.

Returns
Intent?

The remote intent, or null if none was set.

getTargetNodeId

Added in 1.0.0
fun getTargetNodeId(intent: Intent): String?

Returns the String extra specifying node ID of remote intent.

Parameters
intent: Intent

The intent holding configuration.

Returns
String?

The node id, or null if none was set.

Public constructors

RemoteActivityHelper

Added in 1.0.0
RemoteActivityHelper(
    context: Context,
    executor: Executor = Executors.newSingleThreadExecutor()
)
Parameters
context: Context

The Context of the application for sending the intent.

executor: Executor = Executors.newSingleThreadExecutor()

Executor used for getting data to be passed in remote intent. If not specified, default will be Executors.newSingleThreadExecutor().

Public functions

startPhoneActivityWithUnlock

Added in 1.3.0-alpha01
@RequiresApi(value = 37)
@RequiresPermission(value = "com.google.wear.permission.SEND_CONTINUE_ACTIVITY_ON_PHONE")
fun startPhoneActivityWithUnlock(
    callerPackage: String,
    targetPackage: String,
    targetAction: String,
    targetUri: Uri,
    targetCategories: List<String>
): ListenableFuture<Void>

Starts an activity on the companion phone device, requesting a remote unlock.

This API requests to start an activity on the companion phone. If the phone is locked, it may also request to initiate unlocking the phone before launching the target application if below system conditions allow it.

This API is only available on API 37 and above.

To use this function, the following conditions must be met:

  • Note that this method only works when it's called from the watch.

  • The calling application must be granted PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONE.

  • The targetPackage must be the same package (or a trusted peer application) on the connected phone, and its SHA1 certificate fingerprint must match the source package on the watch. The callerPackage must also accurately match the calling app's package name.

  • The caller application must be running in the foreground when the request is made.

  • The request must be the direct result of an explicit user interaction (for example, tapping a "show on phone" button).

  • User should have enrolled in the feature.

  • An active authentication session must exist between the devices. This requires both devices support the feature, the watch is currently unlocked and near the phone while they are connected via Bluetooth, and the phone has been unlocked by the user at least once after the watch was unlocked and placed on the user's body.

There are no restrictions on targetAction, targetUri or targetCategories as long as they are not empty.

Use this method to launch explicitly targeted intents (specifying target package, action, URI, and categories) instead of public browsable links, and if your flow strictly requires an unlocked phone without automatic fallbacks.

Exception Conditions:

Parameters
callerPackage: String

The package name of the calling app. This is required.

targetPackage: String

The package name of the activity to start on the phone. This is required.

targetAction: String

The action of the activity to start on the phone. This is required.

targetUri: Uri

The data URI of the activity to start on the phone. This is required and cannot be empty.

targetCategories: List<String>

The categories of the activity to start on the phone. This is required.

Returns
ListenableFuture<Void>

A ListenableFuture which resolves if the request was successfully sent, or throws an Exception if the request failed or the parameters were invalid. If there's a problem with starting remote activity, RemoteIntentException will be thrown.

Throws
IllegalStateException

If PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONE has not been granted.

IllegalArgumentException

If any of the required parameters are empty.

UnsupportedOperationException

If this remote unlock API is unsupported on this device.

startRemoteActivity

Added in 1.0.0
fun startRemoteActivity(targetIntent: Intent, targetNodeId: String? = null): ListenableFuture<Void>

Start an activity on another device.

This API currently supports sending intents with action set to android.content.Intent.ACTION_VIEW, a data URI populated using android.content.Intent.setData, and with the category android.content.Intent.CATEGORY_BROWSABLE present.

When targetNodeId is unspecified, if the current device is a watch, the activity will start on the companion phone device. Otherwise, the activity will start on all connected watch devices.

If the intent passed in sets a different action or does not contain the CATEGORY_BROWSABLE category or does not set a data URI, the call will be rejected and a kotlin.IllegalArgumentException thrown.

Besides the mandated action and category, the caller must provide a data URI and an optional set of categories to be delivered to the remote device.

If any additional attributes of the intent are set (for examples, extras, package, component), they will be stripped from the intent. Only an intent with ACTION_VIEW, CATEGORY_BROWSABLE, any other specified categories, and the provided data URI will be delivered to the remote devices.

From Wear 6, the Wear SDK on the Watch will be used for starting remote activities on the connected companion.

Parameters
targetIntent: Intent

The intent to open on the remote device. Action must be set to android.content.Intent.ACTION_VIEW, a data URI must be populated using android.content.Intent.setData, and the category android.content.Intent.CATEGORY_BROWSABLE must be present.

targetNodeId: String? = null

Wear OS node id for the device where the activity should be started. If null, and the current device is a watch, the activity will start on the companion phone device. Otherwise, the activity will start on all connected watch devices.

Returns
ListenableFuture<Void>

The ListenableFuture which resolves if starting activity was successful or throws Exception if any errors happens. If there's a problem with starting remote activity, RemoteIntentException will be thrown.

startRemoteActivityAttemptUnlock

Added in 1.3.0-alpha01
fun startRemoteActivityAttemptUnlock(
    targetUri: Uri,
    targetCategories: List<String> = listOf(Intent.CATEGORY_BROWSABLE)
): ListenableFuture<Void>

Starts an activity on the companion phone device, requesting a remote unlock if supported and PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONE is granted.

The remote unlock aspect of the request is subject to a strict set of security conditions:

  • The remote unlock part is only applicable when the request is initiated from a Wear OS watch.

  • The calling application must be granted PERMISSION_SEND_CONTINUE_ACTIVITY_ON_PHONE.

  • The app must be in the foreground.

  • The request must be the result of explicit user interaction.

  • User should have enrolled in the feature.

  • An active authentication session must exist between the devices. This requires both devices support the feature, the watch is currently unlocked and near the phone while they are connected via Bluetooth, and the phone has been unlocked by the user at least once after the watch was unlocked and placed on the user's body.

To initiate the launch, this method will generate an intent with an android.content.Intent.ACTION_VIEW action, the provided targetUri, and the caller's targetCategories (which must include android.content.Intent.CATEGORY_BROWSABLE category).

If remote unlock is not supported or conditions are not met (for example, missing permission, or if the authentication session ends), this method falls back to startRemoteActivity behavior. In this case, no remote unlock will be requested and the user will have to manually unlock their phone if it is locked before activity is launched.

Use this method for broad compatibility on watches, providing an easy upgrade path for existing users of startRemoteActivity. On API 37 and above, this method attempts unlocking the phone, automatically falling back to a standard remote launch if unavailable. On API below 37, it functions as a standard remote launch. Intents must match the listed limitations.

Parameters
targetUri: Uri

The data URI of the activity to start on the phone. This must not be empty.

targetCategories: List<String> = listOf(Intent.CATEGORY_BROWSABLE)

The categories of the activity to start on the phone. This must contain android.content.Intent.CATEGORY_BROWSABLE. If not specified, by default it will be android.content.Intent.CATEGORY_BROWSABLE.

Returns
ListenableFuture<Void>

A ListenableFuture which resolves if the request was successfully sent, or throws an Exception if the request failed or the parameters were invalid.

Public properties

availabilityStatus

Added in 1.1.0
val availabilityStatusFlow<Int>

Status of whether RemoteActivityHelper can startRemoteActivity, if known.

In scenarios of restricted connection or temporary disconnection with a paired device, startRemoteActivity will not be available. Please check availabilityStatus before calling startRemoteActivity to provide better experience for the user.

Wear devices start to support determining the availability status from Wear Sdk WEAR_TIRAMISU_4. On older wear devices, it will always return STATUS_UNKNOWN. On phone devices, it will always return STATUS_UNKNOWN.

import androidx.wear.remote.interactions.RemoteActivityHelper

remoteActivityHelper.availabilityStatus.collect { status ->
    when (status) {
        RemoteActivityHelper.STATUS_UNAVAILABLE ->
            TODO("Hide or present alternative flow as remote flow is not available.")
        RemoteActivityHelper.STATUS_TEMPORARILY_UNAVAILABLE ->
            TODO("Present education to user to connect devices or bring to proximity.")
        RemoteActivityHelper.STATUS_AVAILABLE,
        RemoteActivityHelper.STATUS_UNKNOWN ->
            // Present normal remote device flow when we don't know (old devices)
            // or when we know it is available.
            remoteActivityHelper.startRemoteActivity(remoteIntent)
    }
}
Returns
Flow<Int>

a Flow with a stream of status updates that could be one of STATUS_UNKNOWN, STATUS_UNAVAILABLE, STATUS_TEMPORARILY_UNAVAILABLE, STATUS_AVAILABLE.