diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e4701a474..d02cdf75e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -76,7 +76,7 @@ wear = "1.3.0"
wearComposeFoundation = "1.5.0-rc02"
wearComposeMaterial = "1.5.0-rc02"
wearComposeMaterial3 = "1.5.0-rc02"
-wearOngoing = "1.0.0"
+wearOngoing = "1.1.0"
wearToolingPreview = "1.0.0"
webkit = "1.14.0"
diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml
index c2a0bb52f..a70ca62db 100644
--- a/wear/src/main/AndroidManifest.xml
+++ b/wear/src/main/AndroidManifest.xml
@@ -224,7 +224,21 @@
+
+
+
+
+
+
?>(null) }
+ val listState = rememberScalingLazyListState()
+
MaterialTheme(
colorScheme = dynamicColorScheme(LocalContext.current) ?: MaterialTheme.colorScheme
) {
- // [START android_wear_ongoing_activity_ambientaware]
AmbientAware { ambientState ->
- // [START_EXCLUDE]
- Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ ScalingLazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ state = listState,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ autoCentering = AutoCenteringParams(itemIndex = 0)
+ ) {
+ item {
Text(text = "Elapsed Time", style = MaterialTheme.typography.titleLarge)
+ }
+ item {
Spacer(modifier = Modifier.height(8.dp))
- // [END_EXCLUDE]
+ }
+ item {
ElapsedTime(ambientState = ambientState)
- // [START_EXCLUDE]
+ }
+ item {
Spacer(modifier = Modifier.height(8.dp))
+ }
+
+ val services = listOf(
+ AlwaysOnService1::class.java,
+ AlwaysOnService2::class.java,
+ AlwaysOnService3::class.java
+ )
+
+ items(services.size) { index ->
+ val serviceClass = services[index]
+ val isRunning = runningService == serviceClass
SwitchButton(
- checked = isOngoingActivity,
+ checked = isRunning,
onCheckedChange = { newState ->
- Log.d(TAG, "Switch button changed: $newState")
- isOngoingActivity = newState
-
if (newState) {
- Log.d(TAG, "Starting AlwaysOnService")
- AlwaysOnService.startService(context)
+ if (runningService != null) {
+ Log.d(TAG, "Stopping ${runningService?.simpleName}")
+ context.stopService(Intent(context, runningService))
+ }
+ Log.d(TAG, "Starting ${serviceClass.simpleName}")
+ val intent = Intent(context, serviceClass)
+ context.startForegroundService(intent)
+ runningService = serviceClass
} else {
- Log.d(TAG, "Stopping AlwaysOnService")
- AlwaysOnService.stopService(context)
+ Log.d(TAG, "Stopping ${serviceClass.simpleName}")
+ context.stopService(Intent(context, serviceClass))
+ runningService = null
}
},
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
- text = "Ongoing Activity",
- style = MaterialTheme.typography.bodyExtraSmall,
+ text = "Ongoing Activity ${index + 1}",
+ style = MaterialTheme.typography.bodySmall,
)
}
}
}
- // [END_EXCLUDE]
}
- // [END android_wear_ongoing_activity_ambientaware]
}
}
diff --git a/wear/src/main/java/com/example/wear/snippets/alwayson/AlwaysOnService.kt b/wear/src/main/java/com/example/wear/snippets/alwayson/AlwaysOnService.kt
index 59ed0f8af..d52db8fd8 100644
--- a/wear/src/main/java/com/example/wear/snippets/alwayson/AlwaysOnService.kt
+++ b/wear/src/main/java/com/example/wear/snippets/alwayson/AlwaysOnService.kt
@@ -16,12 +16,11 @@
package com.example.wear.snippets.alwayson
-import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
-import android.content.Context
import android.content.Intent
+import android.os.SystemClock
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
@@ -30,29 +29,19 @@ import androidx.wear.ongoing.OngoingActivity
import androidx.wear.ongoing.Status
import com.example.wear.R
-class AlwaysOnService : LifecycleService() {
+abstract class AlwaysOnServiceBase : LifecycleService() {
private val notificationManager by lazy { getSystemService() }
companion object {
private const val TAG = "AlwaysOnService"
- private const val NOTIFICATION_ID = 1001
- private const val CHANNEL_ID = "always_on_service_channel"
+ const val NOTIFICATION_ID = 1001
+ const val CHANNEL_ID = "always_on_service_channel"
private const val CHANNEL_NAME = "Always On Service"
+
@Volatile
var isRunning = false
private set
-
- fun startService(context: Context) {
- Log.d(TAG, "Starting AlwaysOnService")
- val intent = Intent(context, AlwaysOnService::class.java)
- context.startForegroundService(intent)
- }
-
- fun stopService(context: Context) {
- Log.d(TAG, "Stopping AlwaysOnService")
- context.stopService(Intent(context, AlwaysOnService::class.java))
- }
}
override fun onCreate() {
@@ -66,9 +55,7 @@ class AlwaysOnService : LifecycleService() {
super.onStartCommand(intent, flags, startId)
Log.d(TAG, "onStartCommand: Service started with startId: $startId")
- // Create and start foreground notification
- val notification = createNotification()
- startForeground(NOTIFICATION_ID, notification)
+ createNotification()
Log.d(TAG, "onStartCommand: Service is now running as foreground service")
@@ -93,8 +80,13 @@ class AlwaysOnService : LifecycleService() {
Log.d(TAG, "createNotificationChannel: Notification channel created")
}
- // [START android_wear_ongoing_activity_create_notification]
- private fun createNotification(): Notification {
+ abstract fun createNotification()
+}
+
+class AlwaysOnService1 : AlwaysOnServiceBase() {
+ override fun createNotification() {
+ // Creates an ongoing activity that demonstrates how to link the touch intent to the always-on activity.
+ // [START android_wear_ongoing_activity_create_notification]
val activityIntent =
Intent(this, AlwaysOnActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
@@ -122,7 +114,6 @@ class AlwaysOnService : LifecycleService() {
.setOngoing(true)
// [START_EXCLUDE]
- // Create an Ongoing Activity
val ongoingActivityStatus = Status.Builder().addTemplate("Stopwatch running").build()
// [END_EXCLUDE]
@@ -139,7 +130,120 @@ class AlwaysOnService : LifecycleService() {
ongoingActivity.apply(applicationContext)
- return notificationBuilder.build()
+ val notification = notificationBuilder.build()
+ // [END android_wear_ongoing_activity_create_notification]
+
+ startForeground(NOTIFICATION_ID, notification)
+ }
+}
+
+class AlwaysOnService2 : AlwaysOnServiceBase() {
+ override fun createNotification() {
+ // Creates an ongoing activity with a static status text
+
+ // [START android_wear_ongoing_activity_notification_builder]
+ // Create a PendingIntent to pass to the notification builder
+ val pendingIntent =
+ PendingIntent.getActivity(
+ this,
+ 0,
+ Intent(this, AlwaysOnActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
+ },
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+ )
+
+ val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("Always On Service")
+ .setContentText("Service is running in background")
+ .setSmallIcon(R.drawable.animated_walk)
+ // Category helps the system prioritize the ongoing activity
+ .setCategory(NotificationCompat.CATEGORY_WORKOUT)
+ .setContentIntent(pendingIntent)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setOngoing(true) // Important!
+ // [END android_wear_ongoing_activity_notification_builder]
+
+ // [START android_wear_ongoing_activity_builder]
+ val ongoingActivity =
+ OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
+ // Sets the icon that appears on the watch face in active mode.
+ .setAnimatedIcon(R.drawable.animated_walk)
+ // Sets the icon that appears on the watch face in ambient mode.
+ .setStaticIcon(R.drawable.ic_walk)
+ // Sets the tap target to bring the user back to the app.
+ .setTouchIntent(pendingIntent)
+ .build()
+ // [END android_wear_ongoing_activity_builder]
+
+ // [START android_wear_ongoing_activity_post_notification]
+ // This call modifies notificationBuilder to include the ongoing activity data.
+ ongoingActivity.apply(applicationContext)
+
+ // Post the notification.
+ startForeground(NOTIFICATION_ID, notificationBuilder.build())
+ // [END android_wear_ongoing_activity_post_notification]
+ }
+}
+
+class AlwaysOnService3 : AlwaysOnServiceBase() {
+ override fun createNotification() {
+ // Creates an ongoing activity that demonstrates dynamic status text (a timer)
+ val activityIntent =
+ Intent(this, AlwaysOnActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+
+ val pendingIntent =
+ PendingIntent.getActivity(
+ this,
+ 0,
+ activityIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+ )
+
+ val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("Always On Service")
+ .setContentText("Service is running in background")
+ .setSmallIcon(R.drawable.animated_walk)
+ // Category helps the system prioritize the ongoing activity
+ .setCategory(NotificationCompat.CATEGORY_WORKOUT)
+ .setContentIntent(pendingIntent)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setOngoing(true) // Important!
+
+ // [START android_wear_ongoing_activity_create_status]
+ // Define a template with placeholders for the activity type and the timer.
+ val statusTemplate = "#type# for #time#"
+
+ // Set the start time for a stopwatch.
+ // Use SystemClock.elapsedRealtime() for time-based parts.
+ val runStartTime = SystemClock.elapsedRealtime()
+
+ val ongoingActivityStatus = Status.Builder()
+ // Sets the template string.
+ .addTemplate(statusTemplate)
+ // Fills the #type# placeholder with a static text part.
+ .addPart("type", Status.TextPart("Run"))
+ // Fills the #time# placeholder with a stopwatch part.
+ .addPart("time", Status.StopwatchPart(runStartTime))
+ .build()
+ // [END android_wear_ongoing_activity_create_status]
+
+ // [START android_wear_ongoing_activity_set_status]
+ val ongoingActivity =
+ OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
+ // [START_EXCLUDE]
+ .setAnimatedIcon(R.drawable.animated_walk)
+ .setStaticIcon(R.drawable.ic_walk)
+ .setTouchIntent(pendingIntent)
+ // [END_EXCLUDE]
+ // Add the status to the OngoingActivity.
+ .setStatus(ongoingActivityStatus)
+ .build()
+ // [END android_wear_ongoing_activity_set_status]
+
+ ongoingActivity.apply(applicationContext)
+ startForeground(NOTIFICATION_ID, notificationBuilder.build())
}
- // [END android_wear_ongoing_activity_create_notification]
}