-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathBluetoothManager.kt
More file actions
181 lines (155 loc) · 5.95 KB
/
BluetoothManager.kt
File metadata and controls
181 lines (155 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package org.covidwatch.android.ble
import android.app.*
import android.content.ComponentName
import android.content.Context
import android.content.Context.BIND_AUTO_CREATE
import android.content.Intent
import android.content.ServiceConnection
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat.getSystemService
import org.covidwatch.android.ui.MainActivity
import org.covidwatch.android.R
import org.covidwatch.android.data.ContactEvent
import org.covidwatch.android.data.ContactEventDAO
import org.covidwatch.android.data.CovidWatchDatabase
import org.tcncoalition.tcnclient.BluetoothService
import org.tcncoalition.tcnclient.BluetoothService.LocalBinder
import org.tcncoalition.tcnclient.cen.*
import org.tcncoalition.tcnclient.toBytes
import org.tcncoalition.tcnclient.toUUID
import java.util.*
import java.util.concurrent.TimeUnit
interface BluetoothManager {
fun startAdvertiser(cen: Cen)
fun stopAdvertiser()
fun startService(cen: Cen)
fun stopService()
fun changeAdvertisedValue(cen: Cen)
}
class BluetoothManagerImpl(
private val app: Application
) : BluetoothManager {
private val intent get() = Intent(app, BluetoothService::class.java)
private var service: BluetoothService? = null
private val cenGenerator = DefaultCenGenerator()
private val cenVisitor = DefaultCenVisitor(app)
private var timer: Timer? = null
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
this@BluetoothManagerImpl.service = (service as LocalBinder).service.apply {
configure(
BluetoothService.ServiceConfiguration(
cenGenerator,
cenVisitor,
foregroundNotification()
)
)
start()
}
runTimer()
}
override fun onServiceDisconnected(name: ComponentName?) = Unit
}
private fun runTimer() {
// scheduler a new timer to start changing the contact event numbers
timer?.cancel()
timer = Timer()
timer?.scheduleAtFixedRate(
object : TimerTask() {
override fun run() {
service?.updateCen()
}
},
TimeUnit.MINUTES.toMillis(CEN_CHANGE_INTERVAL_MIN),
TimeUnit.MINUTES.toMillis(CEN_CHANGE_INTERVAL_MIN)
)
}
private fun foregroundNotification(): Notification {
createNotificationChannelIfNeeded()
val notificationIntent = Intent(app, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT
)
return NotificationCompat.Builder(app, CHANNEL_ID)
.setContentTitle(app.getString(R.string.foreground_notification_title))
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
}
override fun changeAdvertisedValue(cen: Cen) {
cenGenerator.cen = cen
service?.updateCen()
}
override fun startAdvertiser(cen: Cen) {
cenGenerator.cen = cen
service?.startAdvertiser()
}
override fun startService(cen: Cen) {
cenGenerator.cen = cen
app.bindService(intent, serviceConnection, BIND_AUTO_CREATE)
app.startService(intent)
}
override fun stopAdvertiser() {
service?.stopAdvertiser()
}
override fun stopService() {
service?.stopAdvertiser()
app.stopService(intent)
}
/**
* This notification channel is only required for android versions above
* android O. This creates the necessary notification channel for the foregroundService
* to function.
*/
private fun createNotificationChannelIfNeeded() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(
app, NotificationManager::class.java
)
manager?.createNotificationChannel(serviceChannel)
}
}
inner class DefaultCenVisitor(private val ctx: Context) : CenVisitor {
private fun handleCEN(cen: Cen) {
Log.i(TAG, "Running handleCEN")
CovidWatchDatabase.databaseWriteExecutor.execute {
val dao: ContactEventDAO = CovidWatchDatabase.getInstance(ctx).contactEventDAO()
val contactEvent = ContactEvent(cen.data.toUUID().toString())
val isCurrentUserSick = ctx.getSharedPreferences(
ctx.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
).getBoolean(ctx.getString(R.string.preference_is_current_user_sick), false)
contactEvent.wasPotentiallyInfectious = isCurrentUserSick
dao.insert(contactEvent)
}
}
override fun visit(cen: GeneratedCen) {
Log.i(TAG, "Handling generated CEN")
this.handleCEN(cen)
}
override fun visit(cen: ObservedCen) {
Log.i(TAG, "Handling observed CEN")
this.handleCEN(cen)
}
}
class DefaultCenGenerator(var cen: Cen? = null) : CenGenerator {
override fun generate(): GeneratedCen {
return GeneratedCen(UUID.randomUUID().toBytes())
}
}
companion object {
// CONSTANTS
private const val CEN_CHANGE_INTERVAL_MIN = 15L
private const val CHANNEL_ID = "CovidBluetoothContactChannel"
private const val TAG = "BluetoothManagers"
}
}