Skip to content

Commit 4fdbefa

Browse files
committed
add value tag selection to notifications
1 parent 1d025f3 commit 4fdbefa

File tree

9 files changed

+197
-19
lines changed

9 files changed

+197
-19
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ interface NotificationTypeInteractor {
77
typesShift: Int = 0,
88
tagsShift: Int = 0,
99
selectedTypeId: Long = 0,
10+
selectedTagId: Long = 0,
11+
autoCancel: Boolean = false,
1012
)
1113

1214
suspend fun checkAndHide(typeId: Long)

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/GetNotificationActivitySwitchControlsInteractor.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class GetNotificationActivitySwitchControlsInteractor @Inject constructor(
3838
tags: List<RecordTag> = emptyList(),
3939
tagsShift: Int = 0,
4040
selectedTypeId: Long? = null,
41+
selectedTagId: Long? = null,
42+
autoCancel: Boolean = false,
4143
goals: Map<Long, List<RecordTypeGoal>>,
4244
allDailyCurrents: Map<Long, GetCurrentRecordsDurationInteractor.Result>,
4345
): NotificationControlsParams {
@@ -136,6 +138,8 @@ class GetNotificationActivitySwitchControlsInteractor @Inject constructor(
136138
controlIconColor = colorMapper.toInactiveColor(isDarkTheme),
137139
filteredTypeColor = colorMapper.toInactiveColor(isDarkTheme),
138140
selectedTypeId = selectedTypeId,
141+
selectedTagId = selectedTagId,
142+
autoCancel = autoCancel,
139143
)
140144
}
141145

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsManager.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import android.graphics.Bitmap
77
import android.view.ContextThemeWrapper
88
import android.view.View
99
import android.widget.RemoteViews
10+
import androidx.core.app.NotificationCompat.Action
11+
import androidx.core.app.RemoteInput
1012
import com.example.util.simpletimetracker.core.extension.allowVmViolations
1113
import com.example.util.simpletimetracker.core.utils.PendingIntents
1214
import com.example.util.simpletimetracker.domain.extension.ifNull
@@ -64,6 +66,40 @@ class NotificationControlsManager @Inject constructor(
6466
}
6567
}
6668

69+
// TODO TAG also add to notif switch.
70+
fun getTagValueInputAction(
71+
from: From,
72+
controls: NotificationControlsParams,
73+
): Action? {
74+
if (controls !is NotificationControlsParams.Enabled) return null
75+
if (controls.selectedTagId.orZero() == 0L) return null
76+
77+
val replyPendingIntent = getPendingSelfIntent(
78+
context = context,
79+
action = ACTION_NOTIFICATION_CONTROLS_TAG_VALUE_INPUT,
80+
requestCode = getRequestCode(from),
81+
from = from,
82+
selectedTypeId = controls.selectedTypeId,
83+
recordTagId = controls.selectedTagId,
84+
recordTypesShift = controls.typesShift,
85+
)
86+
val remoteInput = RemoteInput
87+
.Builder(ARGS_TAG_VALUE_INPUT)
88+
// .setChoices(arrayOf("1", "2.3", "3.45")) // TODO TAG this or show instead of tags?
89+
.setLabel("Enter new value") // TODO TAG
90+
.build()
91+
92+
// Create the reply action and add the remote input.
93+
return Action
94+
.Builder(
95+
R.drawable.icon_category_emoji_emotions, // TODO TAG
96+
"Enter value (steps)", // TODO TAG add suffix
97+
replyPendingIntent,
98+
)
99+
.addRemoteInput(remoteInput)
100+
.build()
101+
}
102+
67103
private fun RemoteViews.addTypeControls(
68104
from: From,
69105
params: NotificationControlsParams.Enabled,
@@ -357,7 +393,7 @@ class NotificationControlsManager @Inject constructor(
357393

358394
sealed interface From {
359395
data class ActivityNotification(val recordTypeId: Long) : From
360-
object ActivitySwitch : From
396+
data object ActivitySwitch : From
361397
}
362398

363399
private data class RequestCode(
@@ -378,6 +414,8 @@ class NotificationControlsManager @Inject constructor(
378414
"com.example.util.simpletimetracker.feature_notification.activitySwitch.onTypeClick"
379415
const val ACTION_NOTIFICATION_CONTROLS_TAG_CLICK =
380416
"com.example.util.simpletimetracker.feature_notification.activitySwitch.onTagClick"
417+
const val ACTION_NOTIFICATION_CONTROLS_TAG_VALUE_INPUT =
418+
"com.example.util.simpletimetracker.feature_notification.activitySwitch.tagValueInput"
381419

382420
const val ACTION_NOTIFICATION_CONTROLS_TYPES_PREV =
383421
"com.example.util.simpletimetracker.feature_notification.activitySwitch.onTypesPrevClick"
@@ -394,6 +432,7 @@ class NotificationControlsManager @Inject constructor(
394432
const val ARGS_TAG_ID = "tagId"
395433
const val ARGS_TYPES_SHIFT = "typesShift"
396434
const val ARGS_TAGS_SHIFT = "tagsShift"
435+
const val ARGS_TAG_VALUE_INPUT = "tagValueInput"
397436

398437
const val TYPES_LIST_SIZE = 6
399438
const val TAGS_LIST_SIZE = 4

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsParams.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ sealed interface NotificationControlsParams {
1818
val controlIconColor: Int,
1919
val filteredTypeColor: Int,
2020
val selectedTypeId: Long?,
21+
val selectedTagId: Long?,
22+
val autoCancel: Boolean,
2123
) : NotificationControlsParams
2224

2325
sealed interface Type {

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recevier/NotificationReceiver.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.app.AlarmManager
44
import android.content.BroadcastReceiver
55
import android.content.Context
66
import android.content.Intent
7+
import androidx.core.app.RemoteInput
78
import com.example.util.simpletimetracker.core.extension.goAsync
89
import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_ADD_RECORD
910
import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_CHANGE_RECORD
@@ -27,6 +28,7 @@ import com.example.util.simpletimetracker.feature_notification.activitySwitch.ma
2728
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TAGS_NEXT
2829
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TAGS_PREV
2930
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TAG_CLICK
31+
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TAG_VALUE_INPUT
3032
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TYPES_NEXT
3133
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TYPES_PREV
3234
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ACTION_NOTIFICATION_CONTROLS_TYPE_CLICK
@@ -40,6 +42,7 @@ import com.example.util.simpletimetracker.feature_notification.recordType.contro
4042
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ARGS_SELECTED_TYPE_ID
4143
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ARGS_TAGS_SHIFT
4244
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ARGS_TAG_ID
45+
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ARGS_TAG_VALUE_INPUT
4346
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ARGS_TYPES_SHIFT
4447
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager.Companion.ARGS_TYPE_ID
4548
import com.example.util.simpletimetracker.feature_notification.external.NotificationExternalBroadcastController
@@ -254,6 +257,24 @@ class NotificationReceiver : BroadcastReceiver() {
254257
typesShift = typesShift,
255258
)
256259
}
260+
ACTION_NOTIFICATION_CONTROLS_TAG_VALUE_INPUT -> {
261+
val from = intent.getIntExtra(ARGS_CONTROLS_FROM, 0)
262+
val typeId = intent.getLongExtra(ARGS_TYPE_ID, 0)
263+
val selectedTypeId = intent.getLongExtra(ARGS_SELECTED_TYPE_ID, 0)
264+
val tagId = intent.getLongExtra(ARGS_TAG_ID, 0)
265+
val tagValue = RemoteInput.getResultsFromIntent(intent)
266+
?.getCharSequence(ARGS_TAG_VALUE_INPUT)
267+
?.toString()?.toDoubleOrNull()
268+
val typesShift = intent.getIntExtra(ARGS_TYPES_SHIFT, 0)
269+
typeController.onActionTagValueSelected(
270+
from = from,
271+
typeId = typeId,
272+
selectedTypeId = selectedTypeId,
273+
tagId = tagId,
274+
tagValue = tagValue,
275+
typesShift = typesShift,
276+
)
277+
}
257278
Intent.ACTION_BOOT_COMPLETED -> {
258279
onBootCompleted()
259280
}

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/controller/NotificationTypeBroadcastController.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,28 @@ class NotificationTypeBroadcastController @Inject constructor(
6767
}
6868
}
6969

70+
fun onActionTagValueSelected(
71+
from: Int,
72+
typeId: Long,
73+
selectedTypeId: Long,
74+
tagId: Long,
75+
tagValue: Double?,
76+
typesShift: Int,
77+
) {
78+
allowDiskRead { MainScope() }.launch {
79+
activityStartStopFromBroadcastInteractor.onActionTagValueSelected(
80+
from = notificationControlsMapper.mapExtraToFrom(
81+
extra = from,
82+
recordTypeId = typeId,
83+
) ?: return@launch,
84+
selectedTypeId = selectedTypeId,
85+
tagId = tagId,
86+
tagValue = tagValue,
87+
typesShift = typesShift,
88+
)
89+
}
90+
}
91+
7092
fun onRequestUpdate(
7193
from: Int,
7294
typeId: Long,

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/interactor/ActivityStartStopFromBroadcastInteractor.kt

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTyp
1111
import com.example.util.simpletimetracker.domain.record.interactor.RemoveRunningRecordMediator
1212
import com.example.util.simpletimetracker.domain.record.interactor.RunningRecordInteractor
1313
import com.example.util.simpletimetracker.domain.record.model.RecordBase
14+
import com.example.util.simpletimetracker.domain.recordTag.interactor.NeedTagValueSelectionInteractor
1415
import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsManager
1516
import kotlinx.coroutines.delay
1617
import javax.inject.Inject
@@ -24,6 +25,7 @@ class ActivityStartStopFromBroadcastInteractor @Inject constructor(
2425
private val notificationActivitySwitchInteractor: NotificationActivitySwitchInteractor,
2526
private val recordRepeatInteractor: RecordRepeatInteractor,
2627
private val completeTypesStateInteractor: CompleteTypesStateInteractor,
28+
private val needTagValueSelectionInteractor: NeedTagValueSelectionInteractor,
2729
) {
2830

2931
suspend fun onActionActivityStop(
@@ -54,7 +56,7 @@ class ActivityStartStopFromBroadcastInteractor @Inject constructor(
5456
// Switch controls are updated separately right from here,
5557
// so no need to update after record change.
5658
updateNotificationSwitch = false,
57-
commentInputAvailable = false, // TODO open activity?
59+
commentInputAvailable = false, // TODO open activity? Or RemoteInput?
5860
) {
5961
// Update to show tag selection.
6062
update(
@@ -84,23 +86,49 @@ class ActivityStartStopFromBroadcastInteractor @Inject constructor(
8486
tagId: Long,
8587
typesShift: Int,
8688
) {
87-
addRunningRecordMediator.startTimer(
88-
typeId = selectedTypeId,
89-
comment = "",
90-
tags = listOfNotNull(tagId.takeUnless { it == 0L }).map {
91-
RecordBase.Tag(
92-
tagId = it,
93-
numericValue = null, // TODO TAG add value selection
94-
)
95-
},
89+
// id = 0 is untagged.
90+
val needValueSelection = tagId != 0L && needTagValueSelectionInteractor.execute(
91+
selectedTagIds = emptyList(),
92+
clickedTagId = tagId,
9693
)
97-
if (from !is NotificationControlsManager.From.ActivitySwitch) {
98-
// Hide tag selection on current notification.
99-
// Switch would be hidden on start timer.
100-
update(from, typesShift)
94+
if (needValueSelection) {
95+
update(
96+
from = from,
97+
typesShift = typesShift,
98+
selectedTypeId = selectedTypeId,
99+
selectedTagId = tagId,
100+
)
101+
} else {
102+
startFromTagSelection(
103+
from = from,
104+
selectedTypeId = selectedTypeId,
105+
tagId = tagId,
106+
tagValue = null,
107+
typesShift = typesShift,
108+
needDelay = false,
109+
autoCancel = false,
110+
)
101111
}
102112
}
103113

114+
suspend fun onActionTagValueSelected(
115+
from: NotificationControlsManager.From,
116+
selectedTypeId: Long,
117+
tagId: Long,
118+
tagValue: Double?,
119+
typesShift: Int,
120+
) {
121+
startFromTagSelection(
122+
from = from,
123+
selectedTypeId = selectedTypeId,
124+
tagId = tagId,
125+
tagValue = tagValue,
126+
typesShift = typesShift,
127+
needDelay = true,
128+
autoCancel = true,
129+
)
130+
}
131+
104132
suspend fun onRequestUpdate(
105133
from: NotificationControlsManager.From,
106134
selectedTypeId: Long,
@@ -115,11 +143,43 @@ class ActivityStartStopFromBroadcastInteractor @Inject constructor(
115143
)
116144
}
117145

146+
private suspend fun startFromTagSelection(
147+
from: NotificationControlsManager.From,
148+
selectedTypeId: Long,
149+
tagId: Long,
150+
tagValue: Double?,
151+
typesShift: Int,
152+
needDelay: Boolean,
153+
autoCancel: Boolean,
154+
) {
155+
delay(1000)
156+
if (from !is NotificationControlsManager.From.ActivitySwitch) {
157+
// Hide tag selection on current notification.
158+
// Switch would be hidden on start timer.
159+
update(from, typesShift, autoCancel = true)
160+
}
161+
// Need delay after sending from RemoteInput before cancelling,
162+
// otherwise it will not cancel.
163+
if (needDelay) delay(1000)
164+
addRunningRecordMediator.startTimer(
165+
typeId = selectedTypeId,
166+
comment = "",
167+
tags = listOfNotNull(tagId.takeUnless { it == 0L }).map {
168+
RecordBase.Tag(
169+
tagId = it,
170+
numericValue = tagValue,
171+
)
172+
},
173+
)
174+
}
175+
118176
private suspend fun update(
119177
from: NotificationControlsManager.From,
120178
typesShift: Int,
121179
tagsShift: Int = 0,
122180
selectedTypeId: Long = 0,
181+
selectedTagId: Long = 0,
182+
autoCancel: Boolean = false,
123183
) {
124184
when (from) {
125185
is NotificationControlsManager.From.ActivityNotification -> {
@@ -130,6 +190,8 @@ class ActivityStartStopFromBroadcastInteractor @Inject constructor(
130190
typesShift = typesShift,
131191
tagsShift = tagsShift,
132192
selectedTypeId = selectedTypeId,
193+
selectedTagId = selectedTagId,
194+
autoCancel = autoCancel,
133195
)
134196
}
135197
is NotificationControlsManager.From.ActivitySwitch -> {

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/interactor/NotificationTypeInteractorImpl.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,20 @@ class NotificationTypeInteractorImpl @Inject constructor(
5656
typesShift: Int,
5757
tagsShift: Int,
5858
selectedTypeId: Long,
59+
selectedTagId: Long,
60+
autoCancel: Boolean,
5961
) {
60-
if (!prefsInteractor.getShowNotifications()) return
62+
if (!prefsInteractor.getShowNotifications()) {
63+
hide(typeId)
64+
return
65+
}
6166

6267
val recordType = recordTypeInteractor.get(typeId)
6368
val runningRecord = runningRecordInteractor.get(typeId)
69+
if (runningRecord == null) {
70+
hide(typeId)
71+
return
72+
}
6473
val recordTags = recordTagInteractor.getAll()
6574
val isDarkTheme = prefsInteractor.getDarkMode()
6675
val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat()
@@ -122,6 +131,8 @@ class NotificationTypeInteractorImpl @Inject constructor(
122131
tags = viewedTags,
123132
tagsShift = tagsShift,
124133
selectedTypeId = selectedTypeId,
134+
selectedTagId = selectedTagId,
135+
autoCancel = autoCancel,
125136
goals = goals,
126137
allDailyCurrents = allDailyCurrents,
127138
)
@@ -132,7 +143,7 @@ class NotificationTypeInteractorImpl @Inject constructor(
132143
show(
133144
recordType = recordType,
134145
goal = goalTime,
135-
runningRecord = runningRecord ?: return,
146+
runningRecord = runningRecord,
136147
recordTags = recordTags.filter { it.id in runningRecord.tagIds },
137148
dailyCurrent = getCurrentRecordsDurationInteractor.getDailyCurrent(runningRecord),
138149
isDarkTheme = isDarkTheme,

0 commit comments

Comments
 (0)