Skip to content

Commit db729cd

Browse files
committed
add wear ongoing activity
1 parent 811cc8b commit db729cd

File tree

17 files changed

+376
-54
lines changed

17 files changed

+376
-54
lines changed

buildSrc/src/main/kotlin/com/example/util/simpletimetracker/Deps.kt

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ object Deps {
9494
object Wear {
9595
const val complications =
9696
"androidx.wear.watchface:watchface-complications-data-source-ktx:${Versions.wear_complications}"
97+
const val wearOngoing =
98+
"androidx.wear:wear-ongoing:${Versions.wear_ongoing}"
9799
}
98100

99101
object Kapt {

buildSrc/src/main/kotlin/com/example/util/simpletimetracker/Versions.kt

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ object Versions {
3939
const val compose_kotlin_compiler = "1.4.0-alpha02"
4040
const val compose_hilt = "1.0.0"
4141
const val wear_complications = "1.2.1"
42+
const val wear_ongoing = "1.0.0"
4243
const val metadata_jvm = "0.5.0"
4344

4445
const val junit = "4.13"

wear/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ dependencies {
6969
implementation(Deps.Google.gson)
7070
implementation(Deps.Google.dagger)
7171
implementation(Deps.Wear.complications)
72+
implementation(Deps.Wear.wearOngoing)
7273
coreLibraryDesugaring(Deps.Google.desugaring)
7374
implementation(Deps.Compose.activity)
7475
implementation(Deps.Compose.ui)

wear/src/main/AndroidManifest.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
<uses-feature android:name="android.hardware.type.watch" />
66

7+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
8+
79
<application
810
android:name=".presentation.WearApp"
911
android:allowBackup="true"
@@ -55,7 +57,7 @@
5557
android:name=".complication.WearComplicationService"
5658
android:exported="true"
5759
android:icon="@drawable/app_ic_launcher_monochrome"
58-
android:label="@string/app_name"
60+
android:label="@string/activity_hint"
5961
android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
6062

6163
<intent-filter>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
package com.example.util.simpletimetracker.complication
7+
8+
import android.content.ComponentName
9+
import android.content.Context
10+
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
11+
import dagger.hilt.android.qualifiers.ApplicationContext
12+
import javax.inject.Inject
13+
14+
class WearComplicationManager @Inject constructor(
15+
@ApplicationContext private val context: Context,
16+
) {
17+
18+
fun updateComplications() {
19+
ComplicationDataSourceUpdateRequester.create(
20+
context = context,
21+
complicationDataSourceComponent = ComponentName(
22+
context,
23+
WearComplicationService::class.java,
24+
),
25+
).requestUpdateAll()
26+
}
27+
}

wear/src/main/java/com/example/util/simpletimetracker/complication/WearComplicationService.kt

+2-18
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
*/
66
package com.example.util.simpletimetracker.complication
77

8-
import android.annotation.SuppressLint
98
import android.app.PendingIntent
10-
import android.content.Intent
119
import android.graphics.Bitmap
1210
import android.graphics.drawable.Icon
1311
import android.util.Log
@@ -26,7 +24,7 @@ import com.example.util.simpletimetracker.R
2624
import com.example.util.simpletimetracker.data.WearDataRepo
2725
import com.example.util.simpletimetracker.data.WearIconMapper
2826
import com.example.util.simpletimetracker.domain.WearActivityIcon
29-
import com.example.util.simpletimetracker.presentation.MainActivity
27+
import com.example.util.simpletimetracker.utils.getMainStartIntent
3028
import dagger.hilt.android.AndroidEntryPoint
3129
import java.time.Instant
3230
import javax.inject.Inject
@@ -93,7 +91,7 @@ class WearComplicationService : SuspendingComplicationDataSourceService() {
9391
startedAt = currentActivity?.startedAt,
9492
activityName = name,
9593
activityIcon = activity?.icon?.let(iconMapper::mapIcon),
96-
onClick = getMainStartIntent(),
94+
onClick = getMainStartIntent(this),
9795
)
9896
}
9997

@@ -144,18 +142,4 @@ class WearComplicationService : SuspendingComplicationDataSourceService() {
144142
}
145143
.getBitmapFromView()
146144
}
147-
148-
@SuppressLint("WearRecents")
149-
private fun getMainStartIntent(): PendingIntent {
150-
val startIntent = Intent(this, MainActivity::class.java).apply {
151-
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
152-
}
153-
154-
return PendingIntent.getActivity(
155-
this,
156-
0,
157-
startIntent,
158-
getPendingIntentFlags(),
159-
)
160-
}
161145
}

wear/src/main/java/com/example/util/simpletimetracker/complication/WearComplicationUtils.kt

-10
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
*/
66
package com.example.util.simpletimetracker.complication
77

8-
import android.app.PendingIntent
98
import android.content.Context
109
import android.graphics.Bitmap
1110
import android.graphics.Canvas
12-
import android.os.Build
1311
import android.util.TypedValue
1412
import android.view.View
1513
import android.view.View.MeasureSpec
@@ -41,12 +39,4 @@ fun Int.dpToPx(context: Context): Int {
4139
this.toFloat(),
4240
context.resources.displayMetrics,
4341
).roundToInt()
44-
}
45-
46-
fun getPendingIntentFlags(): Int {
47-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
48-
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
49-
} else {
50-
PendingIntent.FLAG_UPDATE_CURRENT
51-
}
5242
}

wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt

+7-17
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
*/
66
package com.example.util.simpletimetracker.data
77

8-
import android.content.ComponentName
9-
import android.content.Context
10-
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
11-
import com.example.util.simpletimetracker.complication.WearComplicationService
8+
import com.example.util.simpletimetracker.complication.WearComplicationManager
9+
import com.example.util.simpletimetracker.notification.WearNotificationManager
1210
import com.example.util.simpletimetracker.wear_api.WearActivity
1311
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
1412
import com.example.util.simpletimetracker.wear_api.WearSettings
1513
import com.example.util.simpletimetracker.wear_api.WearTag
16-
import dagger.hilt.android.qualifiers.ApplicationContext
14+
import dagger.Lazy
1715
import kotlinx.coroutines.Deferred
1816
import kotlinx.coroutines.async
1917
import kotlinx.coroutines.awaitAll
@@ -29,8 +27,9 @@ import javax.inject.Singleton
2927

3028
@Singleton
3129
class WearDataRepo @Inject constructor(
32-
@ApplicationContext private val context: Context,
3330
private val wearRPCClient: WearRPCClient,
31+
private val wearComplicationManager: WearComplicationManager,
32+
private val wearNotificationManager: Lazy<WearNotificationManager>,
3433
) {
3534

3635
val dataUpdated: SharedFlow<Unit> get() = _dataUpdated.asSharedFlow()
@@ -51,7 +50,8 @@ class WearDataRepo @Inject constructor(
5150
deferred += async { loadActivities(forceReload = true) }
5251
deferred += async { loadCurrentActivities(forceReload = true) }
5352
deferred.awaitAll()
54-
updateComplications()
53+
wearComplicationManager.updateComplications()
54+
wearNotificationManager.get().updateNotifications()
5555
_dataUpdated.emit(Unit)
5656
}
5757
}
@@ -92,14 +92,4 @@ class WearDataRepo @Inject constructor(
9292
suspend fun openAppPhone(): Result<Unit> = mutex.withLock {
9393
return runCatching { wearRPCClient.openPhoneApp() }
9494
}
95-
96-
fun updateComplications() {
97-
ComplicationDataSourceUpdateRequester.create(
98-
context = context,
99-
complicationDataSourceComponent = ComponentName(
100-
context,
101-
WearComplicationService::class.java,
102-
),
103-
).requestUpdateAll()
104-
}
10595
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
package com.example.util.simpletimetracker.data
7+
8+
import android.Manifest
9+
import android.content.Context
10+
import android.content.pm.PackageManager
11+
import android.os.Build
12+
import androidx.core.app.ActivityCompat
13+
import androidx.core.app.NotificationManagerCompat
14+
import dagger.hilt.android.qualifiers.ApplicationContext
15+
import javax.inject.Inject
16+
17+
class WearPermissionRepo @Inject constructor(
18+
@ApplicationContext private val context: Context,
19+
) {
20+
21+
fun checkPostNotificationsPermission(): Boolean {
22+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
23+
checkPermission(Manifest.permission.POST_NOTIFICATIONS)
24+
} else {
25+
true
26+
}
27+
}
28+
29+
fun areNotificationsEnabled(): Boolean {
30+
return NotificationManagerCompat.from(context).areNotificationsEnabled()
31+
}
32+
33+
@Suppress("SameParameterValue")
34+
private fun checkPermission(permission: String): Boolean {
35+
return ActivityCompat.checkSelfPermission(context, permission) ==
36+
PackageManager.PERMISSION_GRANTED
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
package com.example.util.simpletimetracker.domain
7+
8+
import android.Manifest
9+
import android.os.Build
10+
import androidx.annotation.RequiresApi
11+
import com.example.util.simpletimetracker.data.WearPermissionRepo
12+
import com.example.util.simpletimetracker.navigation.WearActionResolver
13+
import javax.inject.Inject
14+
15+
class WearCheckNotificationsPermissionInteractor @Inject constructor(
16+
private val wearActionResolver: WearActionResolver,
17+
private val wearPermissionRepo: WearPermissionRepo,
18+
) {
19+
20+
fun execute(
21+
onEnabled: () -> Unit,
22+
onDisabled: () -> Unit = {},
23+
) {
24+
when {
25+
wearPermissionRepo.areNotificationsEnabled() -> onEnabled()
26+
27+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> requestPermission(
28+
onEnabled = onEnabled,
29+
onDisabled = onDisabled,
30+
)
31+
32+
else -> onDisabled()
33+
}
34+
}
35+
36+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
37+
private fun requestPermission(
38+
onEnabled: () -> Unit,
39+
onDisabled: () -> Unit,
40+
) {
41+
wearActionResolver.setResultListener {
42+
val isGranted = it as? Boolean == true
43+
if (isGranted) {
44+
onEnabled()
45+
} else {
46+
onDisabled()
47+
}
48+
}
49+
wearActionResolver.requestPermission(Manifest.permission.POST_NOTIFICATIONS)
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
package com.example.util.simpletimetracker.navigation
7+
8+
import androidx.activity.ComponentActivity
9+
import androidx.activity.result.ActivityResultLauncher
10+
import androidx.activity.result.contract.ActivityResultContracts
11+
import javax.inject.Inject
12+
import javax.inject.Singleton
13+
14+
@Singleton
15+
class WearActionResolver @Inject constructor(
16+
private val resultContainer: WearResultContainer,
17+
) {
18+
19+
private var requestPermissionLauncher: ActivityResultLauncher<String>? = null
20+
21+
fun registerResultListeners(activity: ComponentActivity) {
22+
requestPermissionLauncher = activity.registerForRequestPermission(REQUEST_PERMISSION)
23+
}
24+
25+
fun setResultListener(listener: WearResultContainer.ResultListener) {
26+
resultContainer.setResultListener(REQUEST_PERMISSION, listener)
27+
}
28+
29+
fun requestPermission(permissionId: String) {
30+
requestPermissionLauncher?.launch(permissionId)
31+
}
32+
33+
private fun ComponentActivity.registerForRequestPermission(key: String): ActivityResultLauncher<String> {
34+
return registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
35+
resultContainer.sendResult(key, result)
36+
}
37+
}
38+
39+
companion object {
40+
private const val REQUEST_PERMISSION = "REQUEST_PERMISSION"
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
package com.example.util.simpletimetracker.navigation
7+
8+
import javax.inject.Inject
9+
import javax.inject.Singleton
10+
11+
@Singleton
12+
class WearResultContainer @Inject constructor() {
13+
private val listeners: MutableMap<String, ResultListener> = mutableMapOf()
14+
15+
fun setResultListener(key: String, listener: ResultListener) {
16+
listeners[key] = listener
17+
}
18+
19+
fun sendResult(key: String, data: Any?) {
20+
listeners.remove(key)?.onResult(data)
21+
}
22+
23+
fun interface ResultListener {
24+
fun onResult(data: Any?)
25+
}
26+
}

0 commit comments

Comments
 (0)