Skip to content

Commit 368b8e9

Browse files
committed
add wear data update on phone data change
1 parent 20b3d95 commit 368b8e9

File tree

15 files changed

+223
-64
lines changed

15 files changed

+223
-64
lines changed

domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/AddRunningRecordMediator.kt

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class AddRunningRecordMediator @Inject constructor(
1414
private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor,
1515
private val notificationGoalCountInteractor: NotificationGoalCountInteractor,
1616
private val widgetInteractor: WidgetInteractor,
17+
private val wearInteractor: WearInteractor,
1718
private val activityStartedStoppedBroadcastInteractor: ActivityStartedStoppedBroadcastInteractor,
1819
) {
1920

@@ -97,6 +98,7 @@ class AddRunningRecordMediator @Inject constructor(
9798
if (runningRecordInteractor.getAll().size == 1) notificationActivityInteractor.checkAndSchedule()
9899
notificationGoalTimeInteractor.checkAndReschedule(listOf(typeId))
99100
widgetInteractor.updateWidgets()
101+
wearInteractor.updateCurrentActivities()
100102
}
101103
}
102104
}

domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RemoveRunningRecordMediator.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class RemoveRunningRecordMediator @Inject constructor(
1212
private val notificationActivityInteractor: NotificationActivityInteractor,
1313
private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor,
1414
private val widgetInteractor: WidgetInteractor,
15+
private val wearInteractor: WearInteractor,
1516
private val prefsInteractor: PrefsInteractor,
1617
private val activityStartedStoppedBroadcastInteractor: ActivityStartedStoppedBroadcastInteractor,
1718
) {
@@ -52,6 +53,9 @@ class RemoveRunningRecordMediator @Inject constructor(
5253
val runningRecordIds = runningRecordInteractor.getAll().map { it.id }
5354
if (runningRecordIds.isEmpty()) notificationActivityInteractor.cancel()
5455
notificationGoalTimeInteractor.checkAndReschedule(runningRecordIds + typeId)
55-
if (updateWidgets) widgetInteractor.updateWidgets()
56+
if (updateWidgets) {
57+
widgetInteractor.updateWidgets()
58+
wearInteractor.updateCurrentActivities()
59+
}
5660
}
5761
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.example.util.simpletimetracker.domain.interactor
2+
3+
interface WearInteractor {
4+
5+
suspend fun updateActivities()
6+
7+
suspend fun updateCurrentActivities()
8+
}

features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearCommunicationInteractor.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@ import com.example.util.simpletimetracker.domain.model.AppColor
1717
import com.example.util.simpletimetracker.domain.model.RecordTag
1818
import com.example.util.simpletimetracker.domain.model.RunningRecord
1919
import com.example.util.simpletimetracker.wear_api.WearActivity
20+
import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI
2021
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
2122
import com.example.util.simpletimetracker.wear_api.WearSettings
22-
import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI
2323
import com.example.util.simpletimetracker.wear_api.WearTag
24+
import dagger.Lazy
2425
import javax.inject.Inject
2526

2627
class WearCommunicationInteractor @Inject constructor(
2728
private val prefsInteractor: PrefsInteractor,
2829
private val recordTypeInteractor: RecordTypeInteractor,
2930
private val recordTagInteractor: RecordTagInteractor,
3031
private val runningRecordInteractor: RunningRecordInteractor,
31-
private val removeRunningRecordMediator: RemoveRunningRecordMediator,
32-
private val addRunningRecordMediator: AddRunningRecordMediator,
32+
private val removeRunningRecordMediator: Lazy<RemoveRunningRecordMediator>,
33+
private val addRunningRecordMediator: Lazy<AddRunningRecordMediator>,
3334
private val appColorMapper: AppColorMapper,
3435
) : WearCommunicationAPI {
3536

@@ -67,10 +68,10 @@ class WearCommunicationInteractor @Inject constructor(
6768
val started = starting.filter { it.id !in currentsIds }
6869

6970
stopped.forEach {
70-
removeRunningRecordMediator.removeWithRecordAdd(it)
71+
removeRunningRecordMediator.get().removeWithRecordAdd(it)
7172
}
7273
started.forEach { record ->
73-
addRunningRecordMediator.add(
74+
addRunningRecordMediator.get().add(
7475
typeId = record.id,
7576
timeStarted = record.startedAt,
7677
tagIds = record.tags.map(WearTag::id),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.example.util.simpletimetracker.feature_wear
2+
3+
import com.example.util.simpletimetracker.domain.interactor.WearInteractor
4+
import javax.inject.Inject
5+
6+
class WearInteractorImpl @Inject constructor(
7+
private val wearRPCServer: WearRPCServer,
8+
) : WearInteractor {
9+
10+
override suspend fun updateActivities() {
11+
12+
}
13+
14+
override suspend fun updateCurrentActivities() {
15+
wearRPCServer.updateCurrentActivities()
16+
}
17+
}

features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearModule.kt

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package com.example.util.simpletimetracker.feature_wear
77

8+
import com.example.util.simpletimetracker.domain.interactor.WearInteractor
89
import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI
910
import dagger.Binds
1011
import dagger.Module
@@ -17,4 +18,8 @@ interface WearModule {
1718

1819
@Binds
1920
fun WearCommunicationInteractor.bindWearCommunicationInteractor(): WearCommunicationAPI
21+
22+
// TODO add base flavor noop
23+
@Binds
24+
fun WearInteractorImpl.bindWearInteractor(): WearInteractor
2025
}

features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt

+23
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,22 @@
55
*/
66
package com.example.util.simpletimetracker.feature_wear
77

8+
import android.content.Context
89
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
910
import com.example.util.simpletimetracker.wear_api.WearRequests
1011
import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI
12+
import com.google.android.gms.tasks.Tasks
13+
import com.google.android.gms.wearable.Wearable
1114
import com.google.gson.Gson
1215
import com.google.gson.reflect.TypeToken
16+
import dagger.hilt.android.qualifiers.ApplicationContext
17+
import kotlinx.coroutines.Dispatchers
18+
import kotlinx.coroutines.withContext
1319
import timber.log.Timber
1420
import javax.inject.Inject
1521

1622
class WearRPCServer @Inject constructor(
23+
@ApplicationContext private val context: Context,
1724
private val api: WearCommunicationAPI,
1825
) {
1926

@@ -38,6 +45,22 @@ class WearRPCServer @Inject constructor(
3845
}
3946
}
4047

48+
suspend fun updateCurrentActivities() = withContext(Dispatchers.IO) {
49+
// TODO handle errors?
50+
val nodesListTask = Wearable
51+
.getNodeClient(context)
52+
.connectedNodes
53+
val nodesList = Tasks.await(nodesListTask)
54+
55+
nodesList.forEach { node ->
56+
Wearable.getMessageClient(context).sendMessage(
57+
node.id,
58+
WearRequests.DATA_UPDATED,
59+
mapToBytes(WearRequests.DATA_UPDATED_CURRENT_ACTIVITIES),
60+
)
61+
}
62+
}
63+
4164
private suspend fun onQueryTagsForActivity(request: ByteArray): ByteArray? {
4265
val activityId: Long = mapFromBytes(request) ?: return null
4366
return mapToBytes(api.queryTagsForActivity(activityId))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.example.util.simpletimetracker.data
2+
3+
import com.example.util.simpletimetracker.wear_api.WearActivity
4+
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
5+
import com.example.util.simpletimetracker.wear_api.WearSettings
6+
import com.example.util.simpletimetracker.wear_api.WearTag
7+
import kotlinx.coroutines.channels.BufferOverflow
8+
import kotlinx.coroutines.flow.MutableSharedFlow
9+
import kotlinx.coroutines.flow.SharedFlow
10+
import kotlinx.coroutines.flow.asSharedFlow
11+
import javax.inject.Inject
12+
import javax.inject.Singleton
13+
14+
@Singleton
15+
class WearDataRepo @Inject constructor(
16+
private val wearRPCClient: WearRPCClient,
17+
) {
18+
19+
val activitiesUpdated: SharedFlow<Unit> get() = _activitiesUpdated.asSharedFlow()
20+
val currentActivitiesUpdated: SharedFlow<Unit> get() = _currentActivitiesUpdated.asSharedFlow()
21+
22+
private var _activitiesUpdated: MutableSharedFlow<Unit> = MutableSharedFlow(
23+
extraBufferCapacity = 1,
24+
onBufferOverflow = BufferOverflow.DROP_OLDEST,
25+
)
26+
private var _currentActivitiesUpdated: MutableSharedFlow<Unit> = MutableSharedFlow(
27+
extraBufferCapacity = 1,
28+
onBufferOverflow = BufferOverflow.DROP_OLDEST,
29+
)
30+
31+
fun addListener() {
32+
wearRPCClient.addListener {
33+
_currentActivitiesUpdated.tryEmit(Unit)
34+
}
35+
}
36+
37+
fun removeListener() {
38+
wearRPCClient.removeListener()
39+
}
40+
41+
suspend fun loadActivities(): List<WearActivity> {
42+
return wearRPCClient.queryActivities()
43+
}
44+
45+
suspend fun loadCurrentActivities(): List<WearCurrentActivity> {
46+
return wearRPCClient.queryCurrentActivities()
47+
}
48+
49+
suspend fun setCurrentActivities(starting: List<WearCurrentActivity>) {
50+
wearRPCClient.setCurrentActivities(starting)
51+
}
52+
53+
suspend fun loadTagsForActivity(activityId: Long): List<WearTag> {
54+
return wearRPCClient.queryTagsForActivity(activityId)
55+
}
56+
57+
suspend fun loadSettings(): WearSettings {
58+
return wearRPCClient.querySettings()
59+
}
60+
}

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

+31-1
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,29 @@
55
*/
66
package com.example.util.simpletimetracker.data
77

8+
import android.content.Context
89
import com.example.util.simpletimetracker.wear_api.WearActivity
10+
import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI
911
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
1012
import com.example.util.simpletimetracker.wear_api.WearRequests
1113
import com.example.util.simpletimetracker.wear_api.WearSettings
1214
import com.example.util.simpletimetracker.wear_api.WearTag
13-
import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI
15+
import com.google.android.gms.wearable.MessageClient
16+
import com.google.android.gms.wearable.Wearable
1417
import com.google.gson.Gson
1518
import com.google.gson.reflect.TypeToken
19+
import dagger.hilt.android.qualifiers.ApplicationContext
1620
import javax.inject.Inject
21+
import javax.inject.Singleton
1722

23+
@Singleton
1824
class WearRPCClient @Inject constructor(
25+
@ApplicationContext private val context: Context,
1926
private val messenger: Messenger,
2027
) : WearCommunicationAPI {
2128

2229
private val gson = Gson()
30+
private var listener: MessageClient.OnMessageReceivedListener? = null
2331

2432
override suspend fun ping(message: String): String {
2533
val response: String? = messenger
@@ -65,6 +73,28 @@ class WearRPCClient @Inject constructor(
6573
return response ?: throw WearRPCException("No response")
6674
}
6775

76+
fun addListener(
77+
onDataChanged: () -> Unit,
78+
) {
79+
listener = MessageClient.OnMessageReceivedListener {
80+
if (it.path == WearRequests.DATA_UPDATED) {
81+
val response: String? = it.data.let(::mapFromBytes)
82+
if (response == WearRequests.DATA_UPDATED_CURRENT_ACTIVITIES) {
83+
onDataChanged()
84+
}
85+
}
86+
}
87+
listener?.let {
88+
Wearable.getMessageClient(context).addListener(it)
89+
}
90+
}
91+
92+
fun removeListener() {
93+
listener?.let {
94+
Wearable.getMessageClient(context).removeListener(it)
95+
}
96+
}
97+
6898
private fun <T> mapToBytes(data: T): ByteArray {
6999
return gson.toJson(data).toByteArray()
70100
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.example.util.simpletimetracker.domain
22

3-
import com.example.util.simpletimetracker.data.WearRPCClient
3+
import com.example.util.simpletimetracker.data.WearDataRepo
44
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
5-
import com.example.util.simpletimetracker.wear_api.WearSettings
65
import com.example.util.simpletimetracker.wear_api.WearTag
76
import javax.inject.Inject
87

98
class CurrentActivitiesMediator @Inject constructor(
10-
private val rpc: WearRPCClient,
9+
private val wearDataRepo: WearDataRepo,
1110
) {
1211

1312
suspend fun start(
@@ -19,21 +18,19 @@ class CurrentActivitiesMediator @Inject constructor(
1918
startedAt = System.currentTimeMillis(),
2019
tags = tags,
2120
)
22-
if (settings().allowMultitasking) {
23-
val currents = rpc.queryCurrentActivities()
24-
this.rpc.setCurrentActivities(currents.plus(newCurrent))
21+
val settings = wearDataRepo.loadSettings()
22+
23+
if (settings.allowMultitasking) {
24+
val currents = wearDataRepo.loadCurrentActivities()
25+
this.wearDataRepo.setCurrentActivities(currents.plus(newCurrent))
2526
} else {
26-
this.rpc.setCurrentActivities(listOf(newCurrent))
27+
this.wearDataRepo.setCurrentActivities(listOf(newCurrent))
2728
}
2829
}
2930

3031
suspend fun stop(currentId: Long) {
31-
val currents = rpc.queryCurrentActivities()
32+
val currents = wearDataRepo.loadCurrentActivities()
3233
val remaining = currents.filter { it.id != currentId }
33-
this.rpc.setCurrentActivities(remaining)
34-
}
35-
36-
private suspend fun settings(): WearSettings {
37-
return this.rpc.querySettings()
34+
this.wearDataRepo.setCurrentActivities(remaining)
3835
}
3936
}

wear/src/main/java/com/example/util/simpletimetracker/domain/StartActivityMediator.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55
*/
66
package com.example.util.simpletimetracker.domain
77

8-
import com.example.util.simpletimetracker.data.WearRPCClient
8+
import com.example.util.simpletimetracker.data.WearDataRepo
99
import com.example.util.simpletimetracker.wear_api.WearActivity
1010
import com.example.util.simpletimetracker.wear_api.WearSettings
1111
import javax.inject.Inject
1212

1313
class StartActivityMediator @Inject constructor(
14-
private val api: WearRPCClient,
14+
private val wearDataRepo: WearDataRepo,
1515
) {
1616

1717
suspend fun requestStart(
1818
activity: WearActivity,
1919
onRequestStartActivity: suspend (activity: WearActivity) -> Unit,
2020
onRequestTagSelection: suspend (activity: WearActivity) -> Unit,
2121
) {
22-
val settings = api.querySettings()
22+
val settings = wearDataRepo.loadSettings()
2323
if (settings.showRecordTagSelection) {
2424
requestTagSelectionIfNeeded(
2525
activity = activity,
@@ -38,11 +38,11 @@ class StartActivityMediator @Inject constructor(
3838
onRequestStartActivity: suspend (activity: WearActivity) -> Unit,
3939
onRequestTagSelection: suspend (activity: WearActivity) -> Unit,
4040
) {
41-
val tags = api.queryTagsForActivity(activity.id)
41+
val tags = wearDataRepo.loadTagsForActivity(activity.id)
4242
val generalTags = tags.filter { it.isGeneral }
4343
val nonGeneralTags = tags.filter { !it.isGeneral }
44-
val tagSelectionNeeded =
45-
nonGeneralTags.isNotEmpty() || generalTags.isNotEmpty() && settings.recordTagSelectionEvenForGeneralTags
44+
val tagSelectionNeeded = nonGeneralTags.isNotEmpty() ||
45+
generalTags.isNotEmpty() && settings.recordTagSelectionEvenForGeneralTags
4646
if (tagSelectionNeeded) {
4747
onRequestTagSelection(activity)
4848
} else {

0 commit comments

Comments
 (0)