1616
1717package com.example.wear.snippets.alwayson
1818
19+ import android.Manifest
20+ import android.content.Intent
21+ import android.content.pm.PackageManager
22+ import android.os.Build
23+ import android.os.Bundle
24+ import android.os.SystemClock
25+ import android.util.Log
1926import androidx.activity.ComponentActivity
27+ import androidx.activity.compose.setContent
28+ import androidx.activity.result.contract.ActivityResultContracts
29+ import androidx.compose.foundation.layout.PaddingValues
30+ import androidx.compose.foundation.layout.Spacer
31+ import androidx.compose.foundation.layout.fillMaxSize
32+ import androidx.compose.foundation.layout.height
33+ import androidx.wear.compose.foundation.lazy.AutoCenteringParams
34+ import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
35+ import androidx.wear.compose.foundation.lazy.items
36+ import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
37+ import androidx.compose.runtime.Composable
38+ import androidx.compose.runtime.getValue
39+ import androidx.compose.runtime.mutableStateOf
40+ import androidx.compose.runtime.produceState
41+ import androidx.compose.runtime.saveable.rememberSaveable
42+ import androidx.compose.runtime.setValue
43+ import androidx.compose.ui.Alignment
44+ import androidx.compose.ui.Modifier
45+ import androidx.compose.ui.platform.LocalContext
46+ import androidx.compose.ui.tooling.preview.Preview
47+ import androidx.compose.ui.unit.dp
48+ import androidx.core.content.ContextCompat
49+ import androidx.wear.compose.material3.MaterialTheme
50+ import androidx.wear.compose.material3.SwitchButton
51+ import androidx.wear.compose.material3.Text
52+ import androidx.wear.compose.material3.dynamicColorScheme
53+ import androidx.wear.tooling.preview.devices.WearDevices
54+ import com.google.android.horologist.compose.ambient.AmbientAware
55+ import com.google.android.horologist.compose.ambient.AmbientState
56+ import kotlinx.coroutines.delay
2057
21- class AlwaysOnActivity : ComponentActivity ()
58+ private const val TAG = " AlwaysOnActivity"
59+
60+ class AlwaysOnActivity : ComponentActivity () {
61+ private val requestPermissionLauncher =
62+ registerForActivityResult(ActivityResultContracts .RequestPermission ()) { isGranted ->
63+ if (isGranted) {
64+ Log .d(TAG , " POST_NOTIFICATIONS permission granted" )
65+ } else {
66+ Log .w(TAG , " POST_NOTIFICATIONS permission denied" )
67+ }
68+ }
69+
70+ override fun onCreate (savedInstanceState : Bundle ? ) {
71+ super .onCreate(savedInstanceState)
72+ Log .d(TAG , " onCreate: Activity created" )
73+
74+ setTheme(android.R .style.Theme_DeviceDefault )
75+
76+ // Check and request notification permission
77+ checkAndRequestNotificationPermission()
78+
79+ setContent { WearApp () }
80+ }
81+
82+ private fun checkAndRequestNotificationPermission () {
83+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
84+ when {
85+ ContextCompat .checkSelfPermission(this , Manifest .permission.POST_NOTIFICATIONS ) ==
86+ PackageManager .PERMISSION_GRANTED -> {
87+ Log .d(TAG , " POST_NOTIFICATIONS permission already granted" )
88+ }
89+ shouldShowRequestPermissionRationale(Manifest .permission.POST_NOTIFICATIONS ) -> {
90+ Log .d(TAG , " Should show permission rationale" )
91+ // You could show a dialog here explaining why the permission is needed
92+ requestPermissionLauncher.launch(Manifest .permission.POST_NOTIFICATIONS )
93+ }
94+ else -> {
95+ Log .d(TAG , " Requesting POST_NOTIFICATIONS permission" )
96+ requestPermissionLauncher.launch(Manifest .permission.POST_NOTIFICATIONS )
97+ }
98+ }
99+ }
100+ }
101+ }
102+
103+ @Composable
104+ // [START android_wear_ongoing_activity_elapsedtime]
105+ fun ElapsedTime (ambientState : AmbientState ) {
106+ // [START_EXCLUDE]
107+ val startTimeMs = rememberSaveable { SystemClock .elapsedRealtime() }
108+
109+ val elapsedMs by
110+ produceState(initialValue = 0L , key1 = startTimeMs) {
111+ while (true ) { // time doesn't stop!
112+ value = SystemClock .elapsedRealtime() - startTimeMs
113+ // In ambient mode, update every minute instead of every second
114+ val updateInterval = if (ambientState.isAmbient) 60_000L else 1_000L
115+ delay(updateInterval - (value % updateInterval))
116+ }
117+ }
118+
119+ val totalSeconds = elapsedMs / 1_000L
120+ val minutes = totalSeconds / 60
121+ val seconds = totalSeconds % 60
122+
123+ // [END_EXCLUDE]
124+ val timeText =
125+ if (ambientState.isAmbient) {
126+ // Show "mm:--" format in ambient mode
127+ " %02d:--" .format(minutes)
128+ } else {
129+ // Show full "mm:ss" format in interactive mode
130+ " %02d:%02d" .format(minutes, seconds)
131+ }
132+
133+ Text (text = timeText, style = MaterialTheme .typography.numeralMedium)
134+ }
135+ // [END android_wear_ongoing_activity_elapsedtime]
136+
137+ @Preview(
138+ device = WearDevices .LARGE_ROUND ,
139+ backgroundColor = 0xff000000 ,
140+ showBackground = true ,
141+ group = " Devices - Large Round" ,
142+ showSystemUi = true ,
143+ )
144+ @Composable
145+ fun WearApp () {
146+ val context = LocalContext .current
147+ var runningService by rememberSaveable { mutableStateOf<Class <* >? > (null ) }
148+ val listState = rememberScalingLazyListState()
149+
150+ MaterialTheme (
151+ colorScheme = dynamicColorScheme(LocalContext .current) ? : MaterialTheme .colorScheme
152+ ) {
153+ AmbientAware { ambientState ->
154+ ScalingLazyColumn (
155+ modifier = Modifier .fillMaxSize(),
156+ state = listState,
157+ horizontalAlignment = Alignment .CenterHorizontally ,
158+ autoCentering = AutoCenteringParams (itemIndex = 0 )
159+ ) {
160+ item {
161+ Text (text = " Elapsed Time" , style = MaterialTheme .typography.titleLarge)
162+ }
163+ item {
164+ Spacer (modifier = Modifier .height(8 .dp))
165+ }
166+ item {
167+ ElapsedTime (ambientState = ambientState)
168+ }
169+ item {
170+ Spacer (modifier = Modifier .height(8 .dp))
171+ }
172+
173+ val services = listOf (
174+ AlwaysOnService1 ::class .java,
175+ AlwaysOnService2 ::class .java,
176+ AlwaysOnService3 ::class .java
177+ )
178+
179+ items(services.size) { index ->
180+ val serviceClass = services[index]
181+ val isRunning = runningService == serviceClass
182+ SwitchButton (
183+ checked = isRunning,
184+ onCheckedChange = { newState ->
185+ if (newState) {
186+ if (runningService != null ) {
187+ Log .d(TAG , " Stopping ${runningService?.simpleName} " )
188+ context.stopService(Intent (context, runningService))
189+ }
190+ Log .d(TAG , " Starting ${serviceClass.simpleName} " )
191+ val intent = Intent (context, serviceClass)
192+ context.startForegroundService(intent)
193+ runningService = serviceClass
194+ } else {
195+ Log .d(TAG , " Stopping ${serviceClass.simpleName} " )
196+ context.stopService(Intent (context, serviceClass))
197+ runningService = null
198+ }
199+ },
200+ contentPadding = PaddingValues (horizontal = 8 .dp, vertical = 4 .dp),
201+ ) {
202+ Text (
203+ text = " Ongoing Activity ${index + 1 } " ,
204+ style = MaterialTheme .typography.bodySmall,
205+ )
206+ }
207+ }
208+ }
209+ }
210+ }
211+ }
0 commit comments