From 40a56e6e498cb1ca7b055837294e576e704ac1eb Mon Sep 17 00:00:00 2001 From: sangjun park Date: Wed, 6 Nov 2024 20:39:32 +0900 Subject: [PATCH] WIP: AmbientLightCollector Initialization Error --- .../src/main/ic_launcher-playstore.png | Bin 18148 -> 27186 bytes .../kaist/iclab/field_tracker/DeviceInfo.kt | 9 - .../kaist/iclab/field_tracker/KoinModule.kt | 83 ++-- .../iclab/field_tracker/MainApplication.kt | 33 +- .../database/DatabaseInterface.kt | 7 +- .../field_tracker/ui/AbstractMainViewModel.kt | 25 +- .../iclab/field_tracker/ui/MainScreen.kt | 427 ++++++++++++------ .../field_tracker/ui/MainViewModelFakeImpl.kt | 110 ++--- .../field_tracker/ui/MainViewModelImpl.kt | 78 +--- .../ui/MainViewModelInterface.kt | 27 ++ .../src/main/res/drawable-hdpi/ic_notf.png | Bin 432 -> 899 bytes .../src/main/res/drawable-mdpi/ic_notf.png | Bin 270 -> 564 bytes .../src/main/res/drawable-xhdpi/ic_notf.png | Bin 603 -> 1240 bytes .../src/main/res/drawable-xxhdpi/ic_notf.png | Bin 987 -> 2211 bytes .../src/main/res/drawable-xxxhdpi/ic_notf.png | Bin 1437 -> 3063 bytes .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1218 -> 1740 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 918 -> 1350 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 3122 -> 3524 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 774 -> 1048 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 568 -> 810 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1970 -> 2306 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1662 -> 2064 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 1226 -> 1864 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 4324 -> 4546 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2480 -> 3424 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 2080 -> 3130 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 6772 -> 7784 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3432 -> 4382 bytes .../ic_launcher_foreground.webp | Bin 3036 -> 4428 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 9564 -> 10248 bytes .../data/WearableDataCollector.kt | 2 +- .../main/java/kaist/iclab/tracker/Tracker.kt | 30 +- .../java/kaist/iclab/tracker/TrackerUtil.kt | 35 ++ .../tracker/collectors/AbstractCollector.kt | 56 --- .../collectors/ActivityTransitionCollector.kt | 212 ++++----- .../collectors/AmbientLightCollector.kt | 92 ++-- .../collectors/AppUsageLogCollector.kt | 159 +++---- .../tracker/collectors/BatteryCollector.kt | 100 ++-- .../tracker/collectors/CallLogCollector.kt | 172 +++---- .../collectors/DataTrafficStatCollector.kt | 120 ++--- .../tracker/collectors/LocationCollector.kt | 266 +++++------ .../tracker/collectors/MessageLogCollector.kt | 266 +++++------ .../collectors/NotificationCollector.kt | 224 ++++----- .../tracker/collectors/ScreenCollector.kt | 92 ++-- .../collectors/UserInteractionCollector.kt | 146 +++--- .../tracker/collectors/WiFiScanCollector.kt | 134 +++--- .../tracker/controller/AbstractCollector.kt | 87 ++++ .../iclab/tracker/controller/Availability.kt | 6 + .../tracker/controller/CollectorConfig.kt | 3 + .../controller/CollectorControllerImpl.kt | 105 ++--- .../CollectorControllerInterface.kt | 22 +- .../tracker/controller/CollectorInterface.kt | 30 ++ .../tracker/controller/CollectorState.kt | 14 + .../iclab/tracker/controller/DataEntity.kt | 5 + .../permission/PermissionManagerImpl.kt | 4 + .../permission/PermissionManagerInterface.kt | 1 + .../data/collector/WearableSensorCollector.kt | 2 +- 57 files changed, 1726 insertions(+), 1458 deletions(-) delete mode 100644 field-smartphone/src/main/java/kaist/iclab/field_tracker/DeviceInfo.kt create mode 100644 field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelInterface.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/TrackerUtil.kt delete mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/collectors/AbstractCollector.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/controller/AbstractCollector.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/controller/Availability.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorConfig.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorInterface.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorState.kt create mode 100644 tracker-library/src/main/java/kaist/iclab/tracker/controller/DataEntity.kt diff --git a/field-smartphone/src/main/ic_launcher-playstore.png b/field-smartphone/src/main/ic_launcher-playstore.png index 91cd7059b8abb3237cc9936ec854f14978f4a324..5ba317c6409c0a886e71d059202ad1e7f78015c8 100644 GIT binary patch literal 27186 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 OPeoA;TdH6d_ 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 { + mapOf( +// "ActivityTransitionCollector" to get(), + "AmbientLight" to get(), +// "AppUsageLogCollector" to get(), +// "BatteryCollector" to get(), +// "CallLogCollector" to get(), +// "DataTrafficStatCollector" to get(), +// "LocationCollector" to get(), +// "MessageLogCollector" to get(), +// "NotificationCollector" to get(), +// "ScreenCollector" to get(), +// "UserInteractionCollector" to get(), +// "WiFiScanCollector" to get() + ) + } + single { Tracker.getCollectorController() } - single { - Tracker.getPermissionManager() - } single { Tracker.getNotfManager() } - single { - TempDBImpl(androidContext()) + single { + Tracker.getPermissionManager() } - singleOf(::ActivityTransitionCollector) - singleOf(::AmbientLightCollector) - singleOf(::AppUsageLogCollector) - singleOf(::BatteryCollector) - singleOf(::CallLogCollector) - singleOf(::DataTrafficStatCollector) - singleOf(::LocationCollector) - singleOf(::MessageLogCollector) - singleOf(::NotificationCollector) - singleOf(::ScreenCollector) - singleOf(::UserInteractionCollector) - singleOf(::WiFiScanCollector) + single { + TrackerUtil(androidContext()) + } - viewModel { - val collectors = mapOf( - "ActivityTransitionCollector" to get(), - "AmbientLightCollector" to get(), - "AppUsageLogCollector" to get(), - "BatteryCollector" to get(), - "CallLogCollector" to get(), - "DataTrafficStatCollector" to get(), - "LocationCollector" to get(), - "MessageLogCollector" to get(), - "NotificationCollector" to get(), - "ScreenCollector" to get(), - "UserInteractionCollector" to get(), - "WiFiScanCollector" to get() - ) - MainViewModelImpl( - get(), - get(), - get(), - collectors - ) + single{ + MainViewModelImpl(get(), get(), get>().keys.toTypedArray()) } } diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/MainApplication.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/MainApplication.kt index 63bd201..89b1d76 100644 --- a/field-smartphone/src/main/java/kaist/iclab/field_tracker/MainApplication.kt +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/MainApplication.kt @@ -3,7 +3,8 @@ package kaist.iclab.field_tracker import android.app.Application import android.util.Log import kaist.iclab.tracker.Tracker -import kaist.iclab.tracker.controller.CollectorControllerInterface +import kaist.iclab.tracker.controller.AbstractCollector +import kaist.iclab.tracker.controller.CollectorInterface import kaist.iclab.tracker.notf.NotfManagerInterface import org.koin.android.ext.android.get import org.koin.android.ext.koin.androidContext @@ -13,27 +14,27 @@ import org.koin.core.context.startKoin class MainApplication: Application(){ override fun onCreate() { super.onCreate() - Tracker.initialize(this@MainApplication) startKoin { androidLogger() androidContext(this@MainApplication) modules(appModule) } - initConfiguration() +// Tracker.initialize(this@MainApplication, get(), get(), get()) +// initConfiguration() } - fun initConfiguration() { - val collectorController = get() - collectorController.collectors.forEach { collector -> - collector.listener = { data -> - Log.d(collector.NAME, "Data: $data") - } - } - - val notfManager = get() - notfManager.setServiceNotfDescription( - icon = R.drawable.ic_notf - ) - } +// fun initConfiguration() { +// val collectorMap = get>() +// collectorMap.forEach { (name, collector) -> +// collector.listener = { data -> +// Log.d(collector.NAME, "Data: $data") +// } +// } +// +// val notfManager = get() +// notfManager.setServiceNotfDescription( +// icon = R.drawable.ic_notf +// ) +// } } \ No newline at end of file diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/database/DatabaseInterface.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/database/DatabaseInterface.kt index 3c1c69b..1bcae69 100644 --- a/field-smartphone/src/main/java/kaist/iclab/field_tracker/database/DatabaseInterface.kt +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/database/DatabaseInterface.kt @@ -1,7 +1,5 @@ package kaist.iclab.tracker.database -import android.net.Uri -import kaist.iclab.tracker.collectors.AbstractCollector import kotlinx.coroutines.flow.Flow interface DatabaseInterface { @@ -21,9 +19,10 @@ interface DatabaseInterface { // /*TODO: maybe UI required*/ // fun export() // -// /*Update config including their enabled/disabled*/ + /*Update config including their enabled/disabled*/ // fun updateConfig(name: String, config: AbstractCollector.Config) - + fun updateConfig(name: String, value: Boolean) + fun getConfigFlow(): Flow> } \ No newline at end of file diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/AbstractMainViewModel.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/AbstractMainViewModel.kt index 31e8baf..0fbb837 100644 --- a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/AbstractMainViewModel.kt +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/AbstractMainViewModel.kt @@ -1,26 +1,9 @@ package kaist.iclab.field_tracker.ui import androidx.lifecycle.ViewModel -import kaist.iclab.tracker.collectors.AbstractCollector -import kotlinx.coroutines.flow.MutableStateFlow +import kaist.iclab.tracker.controller.CollectorConfig +import kaist.iclab.tracker.controller.CollectorInterface +import kaist.iclab.tracker.controller.CollectorState import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -abstract class AbstractMainViewModel: ViewModel() { - abstract val _isRunningState: MutableStateFlow - val isRunningState: StateFlow - get() = _isRunningState.asStateFlow() - - abstract val collectorMap: Map - abstract val _enabledCollectors: MutableStateFlow> - val enabledCollectors: StateFlow> - get() = _enabledCollectors.asStateFlow() - - abstract fun start() - abstract fun stop() - abstract fun enable(name: String) - abstract fun disable(name: String) - - abstract fun sync() - abstract fun delete() -} \ No newline at end of file +abstract class AbstractMainViewModel: ViewModel(), MainViewModelInterface diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainScreen.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainScreen.kt index 12471cd..7168dca 100644 --- a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainScreen.kt +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainScreen.kt @@ -1,6 +1,5 @@ package kaist.iclab.field_tracker.ui -import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -8,205 +7,333 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.DirectionsRun -import androidx.compose.material.icons.filled.Pause -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Sync -import androidx.compose.material.icons.rounded.Delete -import androidx.compose.material.icons.rounded.ExpandLess -import androidx.compose.material.icons.rounded.ExpandMore -import androidx.compose.material.icons.rounded.PlayArrow -import androidx.compose.material.icons.rounded.Stop -import androidx.compose.material.icons.rounded.Upload -import androidx.compose.material3.Button +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import kaist.iclab.field_tracker.ui.theme.TrackerTheme +import kaist.iclab.tracker.controller.CollectorConfig +import kaist.iclab.tracker.controller.CollectorState import org.koin.androidx.compose.koinViewModel @Composable fun MainScreen(viewModel: AbstractMainViewModel = koinViewModel()) { - val isRunning = viewModel.isRunningState.collectAsStateWithLifecycle() + val isRunning = viewModel.controllerStateFlow.collectAsStateWithLifecycle() + val collectorStates = viewModel.collectorStateFlow.collectAsStateWithLifecycle( + viewModel.collectors.map{ it to CollectorState(CollectorState.FLAG.UNAVAILABLE, "Not initialized") }.toMap() + ) + val collectorConfigs = viewModel.configFlow.collectAsStateWithLifecycle( + viewModel.collectors.map{ it to CollectorConfig() }.toMap() + ) - Column { - CollectorControllerUI(isRunning.value, viewModel) - LazyColumn( - modifier = Modifier.fillMaxWidth() - ) { - items( - viewModel.collectorMap.keys.toList() - ) { item -> - Collector(item, viewModel) - } - } - } -} + Column( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(14.dp) + ) { + Header() + Section( + contents = listOf( + { + SettingRow( + title = "Run Tracker", + displayStatus = false, + displayToggle = true, + toggleStatus = isRunning.value, + onClick = {if(isRunning.value) viewModel.stop() else viewModel.start()} + ) + } + ) + ) +// Section( +// contents = listOf( +// { +// SettingRow( +// title = "Users", +// status = "testing@ic.kaist.ac.kr", +// displayStatus = true, +// displayToggle = false, +// onClick = {} +// ) +// } +// ) +// ) -@Composable -fun Collector(name: String, viewModel: AbstractMainViewModel) { - val enabledCollectors = viewModel.enabledCollectors.collectAsStateWithLifecycle() - val (expanded, setExpand) = remember { mutableStateOf(true) } - Column { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .padding(16.dp) - .fillMaxWidth() - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - IconButton(onClick = { setExpand(!expanded) }) { - Icon( - - imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, - contentDescription = "expand", - tint = Color.Gray, - modifier = Modifier - .width(32.dp) - .height(32.dp) + Section( + contents = viewModel.collectors.map{ name -> + { + SettingRow( + title = name, + displayStatus = false, + displayToggle = true, + toggleStatus = collectorStates.value[name]?.flag == CollectorState.FLAG.ENABLED, + onClick = {if(collectorStates.value[name]?.flag == CollectorState.FLAG.ENABLED) + viewModel.disableCollector(name) else viewModel.enableCollector(name)} ) } - Icon( - imageVector = Icons.Default.DirectionsRun, - contentDescription = name, - tint = Color.Black, - modifier = Modifier.size(24.dp) - ) - Text(text = name, fontSize = 16.sp) } - Switch( - checked = enabledCollectors.value[name] ?: false, - onCheckedChange = { - if (it) { - viewModel.enable(name) - } else { - viewModel.disable(name) - } + ) + +// Section( +//// viewModel.collectorMap.keys.toList(). +// contents = listOf( +// { +// SettingRow( +// title = "External Devices", +// status = "Galaxy Watch, Polar H10", +// displayStatus = true, +// displayToggle = true, +// onClick = {} +// ) +// } +// ) +// ) + +// Section( +// contents = listOf( +// { +// SettingRow( +// title = "Conduct Experiment", +// status = "beta-testing", +// displayStatus = true, +// displayToggle = true, +// onClick = {} +// ) +// }, +// { +// SettingRow( +// title = "Server URL", +// status= "abc.kaist.ac.kr", +// displayStatus = true, +// displayToggle = false, +// onClick = {} +// ) +// }, +// { +// SettingRow( +// title = "Sync", +// status= "3 hours", +// displayStatus = true, +// displayToggle = true, +// onClick = {} +// ) +// }, +// { +// SettingRow( +// title = "Delete", +// displayStatus = false, +// displayToggle = false, +// onClick = {} +// ) +// } +// ) +// ) + + Section( + contents = listOf( + { + SettingRow( + title = "Version", + status = viewModel.getAppVersion(), + displayStatus = true, + displayToggle = false, + onClick = {} + ) }, - colors = SwitchDefaults.colors( - checkedThumbColor = Color(0xFF0B57D0), // Blue thumb when checked - checkedTrackColor = Color(0xFFD3E3FD), // Blue track when checked - uncheckedThumbColor = Color.White, // White thumb when unchecked - uncheckedTrackColor = Color(0xFFC0C0C0), // Light blue track when unchecked - uncheckedBorderColor = Color.Transparent, // No border for unchecked state - checkedBorderColor = Color.Transparent // No border for checked state - ), - thumbContent = { - Box( - modifier = Modifier - .size(16.dp) // Increase thumb size - .background(Color.Transparent, CircleShape) // Ensure thumb is circular + { + SettingRow( + title = "Device", + status= viewModel.getDeviceInfo(), + displayStatus = true, + displayToggle = false, + onClick = {} ) - } + }, + { + SettingRow( + title = "License", + displayStatus = false, + displayToggle = false, + onClick = {} + ) + }, ) - } - - if (expanded) { - Row( - modifier = Modifier - .padding(16.dp) - .fillMaxWidth(), - ) { - Text(text = "Hello") - } + ) - } } +} +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Header() { + TopAppBar( + title = { + Text(text = "Tracker Configuration", + fontWeight = FontWeight.SemiBold) + }, + actions = { + IconButton(onClick = { /* Handle menu click */ }) { + Icon( + imageVector = Icons.Default.Menu, // This is the standard menu icon + contentDescription = "Menu" + ) + } + }, + colors = TopAppBarColors( + containerColor = Color.White, + titleContentColor = Color.Black, + actionIconContentColor = Color.Black, + scrolledContainerColor = Color.White, + navigationIconContentColor = Color.Black + ) + ) +} +@Composable +fun CustomDivider( + modifier: Modifier, + isHorizontal: Boolean = true +) { + if (isHorizontal) { + HorizontalDivider( + modifier = modifier, + thickness = 1.dp, + color = Color(0xFFEAEAEA) + ) + } else { + VerticalDivider( + modifier = modifier, + thickness = 1.dp, + color = Color(0xFFEAEAEA) + ) + } } @Composable -fun CollectorControllerUI(isRunning: Boolean, viewModel: AbstractMainViewModel) { - Row( - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, +fun Section( + contents: List<@Composable () -> Unit> +) { + Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp) + .clip(RoundedCornerShape(18.dp)) + .background(Color.White) + .padding(horizontal = 18.dp) + ) { - // Sync Button (left of Play/Pause) - IconButton( - onClick = {}, - modifier = Modifier.size(40.dp) - ) { - Icon( - imageVector = Icons.Default.Sync, - contentDescription = "Sync", - tint = Color.Gray, - modifier = Modifier.size(24.dp) - ) + contents.forEachIndexed { index, item -> + if (index > 0) { + CustomDivider(modifier = Modifier + .fillMaxWidth()) +// .padding(horizontal = 18.dp)) + } + item() } + } +} - // Play/Pause Button (centered as main action) - IconButton( - onClick = { - if (isRunning) { - viewModel.stop() - } else { - viewModel.start() - } - }, - modifier = Modifier - .size(40.dp) - .background(Color(0xFF0B57D0), CircleShape) - ) { - Icon( - imageVector = if (isRunning) Icons.Default.Pause else Icons.Default.PlayArrow, - contentDescription = if (isRunning) "Pause" else "Start", - tint = Color.White, - modifier = Modifier.size(28.dp) - ) - } - // Flush Button - IconButton( - onClick = {}, - modifier = Modifier.size(40.dp) - ) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = "Flush", - tint = Color.Gray, - modifier = Modifier.size(24.dp) +@Composable +fun CustomToggle( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, +) { + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + colors = SwitchDefaults.colors( + checkedThumbColor = Color.White, // Blue thumb when checked + checkedTrackColor = Color(0xFF3579FF), // Blue track when checked + uncheckedThumbColor = Color.White, // White thumb when unchecked + uncheckedTrackColor = Color(0xFF9A999E), // Light blue track when unchecked + uncheckedBorderColor = Color.Transparent, // No border for unchecked state + checkedBorderColor = Color.Transparent // No border for checked state + ), + thumbContent = { + Box( + modifier = Modifier + .size(16.dp) // Increase thumb size + .background(Color.Transparent, CircleShape) // Ensure thumb is circular ) } - } + ) } -@Preview(showBackground = true) @Composable -fun Preview() { - MainScreen(MainViewModelFakeImpl()) +fun SettingRow( + title: String, + status: String = "", + onClick: () -> Unit, + toggleStatus: Boolean = false, + displayStatus: Boolean, + displayToggle: Boolean, +) { + Column { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 14.dp) + .clickable { onClick() }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text( + text = title, + fontSize = 18.sp, + ) + if (displayStatus) { + Text( + text = status, + fontSize = 12.sp, + color = Color(0xFF8E8D92) + ) + } + } + if (displayToggle) { + Row(verticalAlignment = Alignment.CenterVertically) { + CustomDivider(modifier = Modifier.height(36.dp), isHorizontal = false) + Spacer(modifier = Modifier.width(8.dp)) + CustomToggle(toggleStatus) { onClick() } + } + } + } + } } + +//@Preview(showBackground = true) +//@Composable +//fun Preview() { +// MainScreen(MainViewModelFakeImpl()) +//} diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelFakeImpl.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelFakeImpl.kt index 4de74de..a471c6d 100644 --- a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelFakeImpl.kt +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelFakeImpl.kt @@ -1,52 +1,58 @@ -package kaist.iclab.field_tracker.ui - -import android.util.Log -import kaist.iclab.tracker.collectors.AbstractCollector -import kaist.iclab.tracker.collectors.BatteryCollector -import kotlinx.coroutines.flow.MutableStateFlow - -class MainViewModelFakeImpl( - -) : AbstractMainViewModel() { - companion object{ - const val TAG = "MainViewModelFakeImpl" - } - - override val collectorMap: Map = mapOf() - override val _enabledCollectors: MutableStateFlow> - = MutableStateFlow( - mapOf("Battery" to false) - ) - - override val _isRunningState = MutableStateFlow(false) - - - override fun start() { - _isRunningState.value = true - } - - override fun stop() { - _isRunningState.value = false - } - - override fun enable(name: String) { - _enabledCollectors.value = _enabledCollectors.value.toMutableMap().apply { - this[name] = true - } - - } - - override fun disable(name: String) { - _enabledCollectors.value = _enabledCollectors.value.toMutableMap().apply { - this[name] = false - } - } - - override fun sync() { - Log.d(TAG, "SYNC") - } - - override fun delete() { - Log.d(TAG, "delete") - } -} \ No newline at end of file +//package kaist.iclab.field_tracker.ui +// +//import kaist.iclab.tracker.controller.AbstractCollector +//import kotlinx.coroutines.flow.MutableStateFlow +// +//class MainViewModelFakeImpl( +// +//) : AbstractMainViewModel() { +// companion object{ +// const val TAG = "MainViewModelFakeImpl" +// } +// +// override val collectorMap: Map = mapOf() +// override val _enabledCollectors: MutableStateFlow> +// = MutableStateFlow( +// mapOf("Battery" to false) +// ) +// +// override val _isRunningState = MutableStateFlow(false) +// +// +// override fun start() { +// _isRunningState.value = true +// } +// +// override fun stop() { +// _isRunningState.value = false +// } +// +// override fun enable(name: String) { +// _enabledCollectors.value = _enabledCollectors.value.toMutableMap().apply { +// this[name] = true +// } +// +// } +// +// override fun disable(name: String) { +// _enabledCollectors.value = _enabledCollectors.value.toMutableMap().apply { +// this[name] = false +// } +// } +// +//// override fun sync() { +//// Log.d(TAG, "SYNC") +//// } +//// +//// override fun delete() { +//// Log.d(TAG, "delete") +//// } +// +// override fun getDeviceInfo(): String { +// return "Device Info" +// } +// +// override fun getAppVersion(): String { +// return "App Version" +// } +//} \ No newline at end of file diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelImpl.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelImpl.kt index 575c7c8..026313f 100644 --- a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelImpl.kt +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelImpl.kt @@ -1,48 +1,22 @@ package kaist.iclab.field_tracker.ui -import android.util.Log -import androidx.lifecycle.viewModelScope -import kaist.iclab.tracker.collectors.AbstractCollector +import kaist.iclab.tracker.TrackerUtil import kaist.iclab.tracker.controller.CollectorControllerInterface -import kaist.iclab.tracker.database.DatabaseInterface -import kaist.iclab.tracker.permission.PermissionManagerInterface -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch +import kaist.iclab.tracker.controller.CollectorInterface class MainViewModelImpl( private val collectorController: CollectorControllerInterface, - private val permissionManager: PermissionManagerInterface, - private val database: DatabaseInterface, - override val collectorMap: Map -) : AbstractMainViewModel() { - + private val trackerUtil: TrackerUtil, + override val collectors: Array +): AbstractMainViewModel() { companion object { const val TAG = "MainViewModelImpl" } - override val _isRunningState: MutableStateFlow = MutableStateFlow(false) - override val _enabledCollectors: MutableStateFlow> = MutableStateFlow( - collectorMap.map { it.key to false }.toMap() - ) - - - init { - Log.d(TAG, "isRunning: ${_isRunningState.value}") - viewModelScope.launch { - Log.d(TAG, "INITIALIZED") - collectorController.isRunningFlow().collect { - Log.d(TAG, "isRunningState: $it") - _isRunningState.value = it - } - } - viewModelScope.launch { - database.getConfigFlow().collect { - Log.d(TAG, "enabledCollectors: $it") - _enabledCollectors.value = it - } - } - } + override val controllerStateFlow = collectorController.stateFlow + override val collectorStateFlow = collectorController.collectorStateFlow + override val configFlow = collectorController.configFlow override fun start() { collectorController.start() @@ -52,31 +26,27 @@ class MainViewModelImpl( collectorController.stop() } - override fun enable(name: String) { - collectorMap[name]?.let { collector-> - collectorController.add(collector) - collector.enable(permissionManager){ - if(it){ - _enabledCollectors.value = _enabledCollectors.value.toMutableMap().apply { - this[name] = true - } - } - } - - } + override fun enableCollector(name: String) { + collectorController.enableCollector(name) } - override fun disable(name: String) { - collectorMap[name]?.let { collector-> - collectorController.remove(collector) - } + override fun disableCollector(name: String) { + collectorController.disableCollector(name) } - override fun sync() { - throw NotImplementedError("Not implemented") + override fun getDeviceInfo(): String { + return trackerUtil.getDeviceModel() } - override fun delete() { - throw NotImplementedError("Not implemented") + override fun getAppVersion(): String { + return trackerUtil.getAppVersion() } + + // override fun sync() { +// throw NotImplementedError("Not implemented") +// } +// +// override fun delete() { +// throw NotImplementedError("Not implemented") +// } } \ No newline at end of file diff --git a/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelInterface.kt b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelInterface.kt new file mode 100644 index 0000000..98f7a36 --- /dev/null +++ b/field-smartphone/src/main/java/kaist/iclab/field_tracker/ui/MainViewModelInterface.kt @@ -0,0 +1,27 @@ +package kaist.iclab.field_tracker.ui + +import kaist.iclab.tracker.controller.CollectorConfig +import kaist.iclab.tracker.controller.CollectorInterface +import kaist.iclab.tracker.controller.CollectorState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +interface MainViewModelInterface { + val collectors: Array + val controllerStateFlow: StateFlow + + val collectorStateFlow: Flow> + val configFlow: Flow> + + fun start() + fun stop() + fun enableCollector(name: String) + fun disableCollector(name: String) + + fun getDeviceInfo(): String + fun getAppVersion(): String + + +// fun sync() +// fun delete() +} \ No newline at end of file diff --git a/field-smartphone/src/main/res/drawable-hdpi/ic_notf.png b/field-smartphone/src/main/res/drawable-hdpi/ic_notf.png index 61350f5c9cff3181f3401aa6c00748efc5454a89..986d0766a425232c0db05ec1cfbf782a17f73282 100644 GIT binary patch delta 877 zcmV-z1Cso(1A_;UBYy)*NkljpI7r{ORA&>{KvPhF!$PHSUPSBW6Zle)I3vEbvd+p1H^K)b^n*9e?dk6a_*nQGSiH=+JFR8L(y0PW{>N~)Rpu3F7*z=60Jk9L?{?^{8_ zm|;Gv<`RCFRDTN+ruw1kC?0XX2zw&tR&0-1_I?EtW)lXUfhQ_;M#31`P`xL=6tKf@ z)Wy$9Ia8Q0>Deb?YBHGz%%=7Hz(YT>kI~BF=nFu4cQI60=%vR zRWF`<5La|c!kEGYF>v2<@sv=3nPN8S?@_5{=B;W)+<#cXdAtrggZQFE=X-*+fUhm*TJll4NuGi7*F2V83TUN-#|M>MkN$iCcHKY zt2Q%DL72kKirJ!lUUjTGkH>ju$LnJj$cBo}ESUjY1_NsmziZSL0|SoX6R~`Au|j* zg*`X3NU^oEtdlwV61F=skb(wB0fO zR9$oasZ24r-x$Y~km{cLi{$O8pQ!G6KOpH$8TET=L(*Rz*mhp%s{=?nQ}4um*Cmk- zzh=uANq1_>`z4XQFV`8#c|t-$r(VQ%uyD7_y5xV`zZBcg3_!xfn3A@gF(m)rk6)_m zKK`#Ep$;|uKZI;e__%+9gwA1?fUOIr{zKJG@7I>!eG`RVkY~(000000NkvXXu0mjf DaA~f- delta 407 zcmV;I0cie%2e1Q>BYy#RNkl3duReA6{O@_+r~m)1-b*Ax zLP8P=5gEq|-koPW;>o>_uDxN|_Qn!6I+pz0;|3Xg;K9{1wtqL4hz`_(Mvn{I8%x|k z#kOw4XVSJ7OTs|Dgn@hs1NjmL@+A!9OBl$PFpw{9piZ>@pbeL{HAmovE*}jkBCubf#Xny?+ji)}!BPcC{juqw*UKqQ}?hwfB2ga9Fen8={Y? zI~oc}RHIjPuzyl_tp;P56dkI{^+!D=qUZ3W-J{^JNFU}^$Y}&O$LjHX|0R_~=Ul4z)M_m8_002ovPDHLkV1hHE B!f5~i diff --git a/field-smartphone/src/main/res/drawable-mdpi/ic_notf.png b/field-smartphone/src/main/res/drawable-mdpi/ic_notf.png index 680bfce529a8c0bb0cdfee270dcba8ae7ea7e848..91a5b1d700a4c648df4f0979f928571f7ef79c74 100644 GIT binary patch delta 540 zcmV+%0^|LT0<;8>BYy$=NklBb5G9d{M5!pKupt)2)2#8zYn-}B@oPi^7pnp$}X*W>c3S-3p(K+~| zlitBANjL`D!H(((&btV8ONd*!1%Cvqp05Ztz@|Qnp0BEUg7f1$5qIWxEEJd2UIvGr z&%iP5W!05rm4v=%O~e(7;C2@)$FmMTson4!jOnkdo;$JQy7hzeEUjBQYU9c+_$Sx{ zYvPWIPmXCPsDHDaux13~x!Q^AbAny5FpAs02@jU5@j$z*k_^?x#aef@O~^|`%yi6E z+L`w}Tb~+zE{aUQ>ZmsxHN@Bw@MaPt9kWXeHMKFQkG|S#o~@xa-V#QU@IZXvD)MJKpk0c@;7+4CJB%0MAqG873X;uU3>Gwoe=SNk&3gtO^^6H zm=`9w^!*V?j_-jFi6-fW{s<)fi^gIvcjg23J+Lbo7GyQyhke~Q`7YFN3PNY}p?=dO=jyolqahTIiSszA+1YV;1;EFF50bk!(IY&yJPs4;_XSals7> zO!e~>W!WDnT&&@M5;903fjH8zkVFhQUC+unp5ek0bu=-<9vgjLW2fA&Il_h|XLv@r t|GI)awwR-jiGDXk3u81;kTXmIdao#%8Q+Sb1Am_yRIx63>;{ zOsp(O2p@nY@d?(#_~ z-BssQRnL+o{}}*a!fcn>VY8EF=c4iRk@(wcE%)27#*CS5H-Ece_Q>oj@=){G>>@(h zG4;FXSfwk>X3d@?4eQHO1Yaw)o<+ebnlXD_Zo^1;V>YApjSO?X-t3lC+QfIWFVXn9 zEw|0qYyCrkF!|m}MaoOFOJ)bnHk++88;{1%yvE<1<+weAcFTu4Se8C2^9dn4D@>sE85@2fMu<`c{EiUMb9{@I zgTA~v80nPRRJQ;^0bSeh4pqjzT{|nf0%lxx_^8<=28oQHd5yo-W#uzYJ)95vgxSW* z6Qh_bLx1I}quYb3Q}WDgr|K1IqE?~L-Hx8gPT8(H3c0*hc8G@2#+$JB%~qw_Ca8^N zl!uV$gwSE-LaHf*+AywdbX+q0DZHDl8(+XOv8*yJIu32NCFN17p=dR#!{Sm&6m*Q? zDICx4ut6AVr_D~m85)FTqmPt%M|W8?Z0jH?B7ZC!l!J+nqLZ4W?xdQiTMIvW@Y-5O zuj?SzQOel%{g7zLV0nSCUsX0IKB(bRKslOfqRwl4fp>Qqos@l|*`fmDNGjl_JB5#R zUE+fpu4j}<(Qw%0C}m&!zEK6noM^b8(4lrR6>!0o!e#7u;=|K0L8*r0J;y02!#ttM z?th%4(PjNW!!PpV-_U{cso+-*p_^vQlQJV2Lf$0alK7xz6qC5%I$|g6?=k%mJtpxe z!{{1>eL;?&oj|q2UKr(JfKm#hiT+0bws z=#g${7#Z#cv<#SKMOPtM{dV+q(D%?rsCKEsV)WiPINn3BxpX|L_0FRT5cS5TaY>8_)>)y*_5GB(mQM`n~8Lei$qU*062J|Eoc5BKxC2NMfAu j++!F2?2%1m|1>`U?OeYmJm&Vz00000NkvXXu0mjfMDuRz delta 579 zcmV-J0=)g$3EKpaBYy%SNklF;bJL?ZTC5Q;JqI zOG=5F>dzc z@%_}R{dRu8JM-|so6o>Nib@pwF*)De+xe8%@8eZwH;#D8OzJfR4Dl)_A4%sjK# zUgNhN|3G~_UdcldmH1?)W+61NJ|3^+$!VY#%ZjudFUqZfSv}8O|HK=}BHlpicmt{9 z4Wy1YkUHK#>UaaG;|-*aH;_8sKA=e+?6M ziteVp!b{O-d47D|zrwX^F^&cCxspV5OK=^Y;~3VU1KX{+IvmA8REaL`@ZatgEq((g zu|#xD)SEuEiLRwE)=un==g%w*{s^6ReAj;8hqD+EeSh~kfkUEq>!NR~r$ig* z&z~{lNMQr}?Kl(@3cD)Wz&YGRtLS=Z6#X6{+>!8^u^Jz+RrCvzZK4hI=FgaMnBX)v z+wn;>+dT1c+_D10I#`M!+!0+Pt8fvEF^nzPCHg^k8Pzy~w)`1A9*?J-^9QT|>W9;( R#QgvO002ovPDHLkV1jLKCxrk2 diff --git a/field-smartphone/src/main/res/drawable-xxhdpi/ic_notf.png b/field-smartphone/src/main/res/drawable-xxhdpi/ic_notf.png index 17fb4a5aba2cedaba305a0375ad6941b45521cad..0f6764926854714d9076f038822dd57c3d9d553b 100644 GIT binary patch delta 2200 zcmV;J2xs@(2cr>?BYy~LNklbt>zVAH< zUvlztZ=X}uU9alYsZ-T=&Yb=1XRigo0Vao=oNRKw$>k=?s(-)y%n!IK5+pfnR4>s!CCbyd$uXWCP z;6vvkljkv9qeQT=UNE@?d&5w)o4m~A4U_+xPcaR!zBf%S(|ei~*o~b8?=kSa zd7XNFVHwju>sh5|ntUNQlj9GQEhcxFoM&>FmfHpOOwO(B7#`$81BLf_mXZv z9DI)9d=h-N$~okJO&&ElSna11)^GAibw4TiJmb%9@HmH4Ws1K|Zd2PFW^<~^D)_X{ z;O=#kcYmwD{Jg2Uewy0mFq<{ieWhTCGG3en54KY9CRV9k1bi55gwH^^|8foWI1_Hf z2*VfNxVc7Jt#-x0mk)Hf5xns=YS)G7oo@21$zSavMurc}a!%Jex`6kj)IWj|_las( z1iYK4ctmX!(>c^+1BPGKvpkl2sMe{#ePfE})qf@nd{v%Wcy2?+A=~wNCLgrT2IqZ~ zd0KZ9xZ~c|HfzD>EYAYK9Wzr05gc|&Utrf4n|v*HlY;du)_Z6I_kF22i7A>ghi!@% zBc0E=Q*D~S8|OPUDuDkiL~cs_~AyDs?rN2|>g@J+pl*Hpg-K82Ks&XTEKVc{W&um7lQ2iUt?-w9m3^L9Mr*gY?hRK&=qwxBfYFh&j0a#BLysrgn zL*N?$KZ|}1+oA0e;1xxV>!X%(1All*A>v8ZuYvcRT;tSGFYHP1U@{_1#n`ovRk?!g zaJVY-s7b`jE#(IAWLLyW)vtjMpHT=i2k?kHVw39EAe{(2S(SMnC*p~gas#AOBAt0( zxIncf`R3cs!!lkc^|IOvWR_uB;xhsyQ*IU-{y{3 zrpkSL&{kEhNcAAbIej==%Oda|6wRcst9}jAJ*d5RhS~3fxLoPXB-r;0Rk^`nKB(_3 z#;uu%A!jkdVRTJLSvRX1PpN(lJPz8S1_iK@u9dQ$R;30vH+7p}#5icS zM$%pE>Phf_Y;<@l7fDAs>OCXr%4jyN#ZvhmGSx`%Lkkh9wHVJEMRaE_FqzdnSKFnl zdCqQERDYQoji&AzUXRvcWboB|Dj&2(^=pvN2XQBK&n}w*-a~SY6n`<1e9$-2^->aBD`~ z^AShRr2KRT!Eon;N_lQz~2)qdE1Ba27|IN z;+`}pYou`T-5ZSlz8GVWgS4FK0QpqOeQGlZ==a7UGkE!h;J_g6Nem7zRQ-r)oAy6HqQO3H#ivot!JLk`G45t73_ZNd4tJKNF3$u13b$> zzjz-e*>e!r#WJf8Gv!Gy{K-JcGu;51ZzET-ux zl+_{5pW-3qHPIoAq8ym*=nn7-bymIKffDTGt>y`vr6T4vB3a&rkNaFhTD_pUIi% zABR4-YyW1uzw@ccov=sqwI?o6tlfL;r$L8tLH(KL50ka)S+nmJ&3XE7rx2&qrTh7j zZ4oblsysw4B2bj1L z7+E(oFhw{p>MURos}H~~PNeXYA_w)GD|mMB);-%)zs^M|(1G`(#Js*r-n#XhkI#SN zK-lg+V-^LcJ~Px>&;R&SHB%>=i5N|pZ%|8u>1 z@%Gj1XVXoq3of#L*z|en3+d11cYhgOjDB72@o~eBGn-Z{;|LeKU%o(N_2b`5rUk!# zx$SEBG|>VAM+;8>YTka-jHpqxOTQ7haoNuE<{qzR37h&xXa@rCLf};-@ZK`Xb&% zy{lT{A4FbESJ3mHrm?`ZpzVe9H=DM`bwafXDR+dXabEYg7pv-jY)xbJ zlowe3ooV|y^C!m-ITozQXg=57m+Jq(^@iZL7HijPx$mkvkHziX6V)%?{%vQ&&fZ;@ zchu^~PG_wJQ8VOYQ^h8JVT|ov%P*d*QGb?(F9K?j;h?8y|md!hY@-^&B-E?Hz50+>TTWNYB*2b=g8pchU;>j}fQh z!nwE~Dpz=YsWw^XFZ$NAlec5PoxF(U0=daA#2)7yk9@y6BY%gk728zryNX>FVdQ&MBb@0FiCKk^lez diff --git a/field-smartphone/src/main/res/drawable-xxxhdpi/ic_notf.png b/field-smartphone/src/main/res/drawable-xxxhdpi/ic_notf.png index 968b211e163321d0a6e6a2db0619773e1974ccc1..77e1b96d4df6ac7bac89a297f5a7168238ba62cb 100644 GIT binary patch delta 3058 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 a0V-QP=tgO)Iz^4(`Tp+ooLs#?NXb-~K}Hn- delta 1208 zcmV;p1V{VK4Z;b3Qb|Tex�KS4BclR!}%ht^@!8FGs);5Z+MGwhfc;r`=ry5itR@ zRU2O+(1lyuk|L+FSVk!jcI7cMGj@3syP&F1FPasEm`z5DZ6zR!nLTESbtK73^AiA; zfx}bmgm0YIwyj3DqM(Y4#hy|^n&1M)kz!^!mYL~Yr`5KRkQ;v(Z`t z(0N7|%qjoFd~<)w!v0?Q&~{3i|)kZ68$C#pr~#+p4GWe~7$w+ZPq*Vzo;Hz%;+xNvO;`m6!4Q zi_!9Ir&_oKEEKf?t>PM)GtgF+(}r|hZ;$Lz>}EBnP&|?sU_3n=y13bzq}7M#cz3c^(pf_#d9|O z@AO$%%C?l;5h7dOM#<-*dAw>@xZ zqd{BO9geqNkqxai8L{O*!$5E!{1LU2;u%y+dXePv7+OM7(W_cA1?;Utq*Or2a*Xlc zuwj2O71E?aB|$5r;Bh?O%0%Inlzy64Dn=#P9oqktyubbeNO~u%1--mPhsSWtevDDv zXH-5M?lY3K(noXe+S*ske|;vMyWIMZ|0#TCp##za=Yy9&2F}G8`J_oMuTgQ|ur9HsKiI_`_Fofd5+NIWrDA5@}25b zhzwckd=k0MFJFk>jWrH%^3Fx?O%S8$Sx;$XAX>~4EbZD3+b0+ZR0tXrDd0UFZqFD{ zKxxrrz$H5Dixao@P$S_59p(g1HD26X^13mkTnMxc*h8U6uln1V96b)LzT`)5=T;T6rofy%&J^_=8YoQqTa5#39I6 z)bXoTj_9&Co{V?dCMT4Z&O4!ZN_Rc%hmAOhu3m{}ekxzsTFj@a# W8_w2rY>na>?`-ctv^ohv5=SL2A6okW diff --git a/field-smartphone/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/field-smartphone/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp index 850633302fcf8fc72e68ede197d6e25f1c000fa8..936fd28adfa6b096b92e516f5f06f46ab8982190 100644 GIT binary patch literal 1350 zcmV-M1-bfCNk&FK1pok7MM6+kP&iC61pojqp+G1Qzl5M|+n7J;?SFd=^FRLz+qP|E zk?eGcO%j>|@4(D}3z&mn!_w1`Zx-^@OQ@P88rD z`UPKhen`QvS6Dm%Xt!;fZc^J!ZQHhO+qRLKQSn~y5AHeV-gM-?uZ`$G1WA(HHghKx z8~|)F91h_HIt{dc|M^D-XLp}KA9rVs!YQh9aOcvfFvb+))$UMrjFYzDmOXaI_;x>@ z>!uk*_B}CCdm4z+1Y#Gym@W3T5GOsse&dAl_XBwu$6uw1RO4SUW%vYgJ`K~A{-mNN1?gOO zl!iK7CO-+&7RCw7GYO>QqBIQz8xcKJJX=v2-_%t-p{p119xMqHxHU(FLyMV-nYqOQ zKaZI4M{_0|bu0zDLX!JMzWx7!4WoG7$9znbAQ! z>5t`Pdu98?LOU+bqRb&u0{R)i8is06e>IP#qns*Z%tZYWRR}}5L7xPZwI|514$?vl zrX%z?5HzA-LCr|Y2%0mw**&@FA&Ee}nh_7FT!E<%_?DthBhh1-WA}_hwsa62y)H!Z zs;~BdSsv!F)NwMJ96%$_4SVG;$5fuAS`f>oNv~va_U64w2Cvit{d+P7gv>S)}LGNz` zqzf`==j=lAAk(~eF5is9MgdYX2AM*-AiH+XB^+t>5CbF~mCe+kfr=TBYl!{_aB1i3 zl1BJI!!gVI6RCjgG0OHJ9u}BNdqWj)ID2=tYupvU3se!q04Jz5IyF^MV<%8X3{>#~ zX9VtcjSmADAjyzzNC~5@1hNfD<~)r)c1<7}xgcqrv8s_n17rn~2DyL)YQ$GDWJmBz z8-C4NA*Y;QKcipEP|Ye>ZI?y?KYEH)GdfEchcj>-7Vu2D;xOm_noHm1~b){8V@&FwKp z$g}X8o22WOb6j7;ovMWU-SO5fC25$g5^KNBW)81T{Kk|sy!I>G`gt!QMZc_)+&GBD zV~8fv9hxaE@zWN94jTsES_$`YntA&;*Pp*pV$?;)clq{lbV0Yc*ekFXmKw^_ zSWo#~kevI8t2XQ>id1NqY}=jMlg;`=e{z#{Uwbx8z=i)9CGXh)>uBU3`B#9{=PR}a z&Q}Pf($f;2QMu)8!X9+z>pjFZmOrwuX)jGU=VN^_oG&GcX>^SWkoByTk%^c!MMs-9 zoi{twX^h3rfAM+Kp2MS&eUCiObsA)DIrQ1lpBfE2f4h76t}yo4?4WgLnEjn}8fgFi I^KSw=0Kz?f?*IS* literal 918 zcmV;H18MwHNk&GF0{{S5MM6+kP&iD20{{Rop+G1Ql}M6p+iDx;Kk3x~QKl-7rgbF$ zISNEb006LTr206WZQH(fZ`-zQ+qP}nwr$R~*{zv?-2MOlScpnHiJ3NJ@Z318@k#d? z*qGaN=bzOKSBlPr?)wHGCQ z7dcGX6>q!?xCs5*lKNv8IIF4J4b>S-whK&yWJ@{T?SlW{O=Nm=_uqY7rfwt4+g;$Y zS_NAgNx2J(=Gl<V4&QFqEf$(`1i_R0Y?Byyp<3|T} zaTTwVibacRCFn-Iib|`|MFSmXMfo_`=G@nsmxJ)(!Sr#Rl4V0C-|Z5Y@k1r3*K9*g zlweQLaKSj@Zi|I1yfs4Xf%TlAF(Zs|_qJ=en<#YgWgu)&0&VGS4pEyRnE;CsgMw;x zU6+sjvirKs6C&UbateAR`Nqok4FuL>4dm|+o&+oy8#+7w{?0=v;$OFDJHV(ki404v z#Mi+F6A;IT*>;`gXq-KC|F+XcLuEpiPF<|BT8kqxOtsk_b5z47C>kD?$T!{&%|@ar z3$Nq;VBE&igMg>y^;@3_3YG=bKrdF0#4l;|1LKqx%KfS3F^#{@zB0xY zd^RKrOq~(dx=2|9lz6VXJ3)a+QlSuzWGW+nZ4sPU(YoT;%LQt{jhPM^4chV~5?teB zC-;$!xQBvTQ;VTw;%U0G}YJG6QtO zMlu0@y4YF-iv>XSRXYD2n633)MMSuOAwc1}S6VG1+%-m@aga>4PKi$?x+X|V?wXf~ zx77&)24)u5%LOytX8Itwz&cW3=slzZc+H<6Ol>7ApOUFZXVAa@B_n$9GDd6 z*a1F|oaH1yMUVmBw-{1e#V#PQY)1dua7wq0spa?AMCedt;%g9^1WzMMYLZCM)+D{9 sOF^o^P2h6$G09-1*vV({AK-}jsPZFtC}X~LQz$Tzh+n)>-2MNqM>^@i9smFU diff --git a/field-smartphone/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/field-smartphone/src/main/res/mipmap-hdpi/ic_launcher_round.webp index 12b9c67e9ade40a057ad80963d9ff6ec87c274bf..3d9614dc01c9bcd270b94e5f252224b0e7cfd87f 100644 GIT binary patch literal 3524 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 M_IlSe&`pRi0Hx~B&j0`b diff --git a/field-smartphone/src/main/res/mipmap-mdpi/ic_launcher.webp b/field-smartphone/src/main/res/mipmap-mdpi/ic_launcher.webp index 0d234e3368af1c6da5030c3ebc1d1a5c24a17fe2..b9ce0be47d0c0a874e6e95220d7b8d99e01f2d37 100644 GIT binary patch literal 1048 zcmV+z1n2uwNk&Ex1ONb6MM6+kP&iBk1ONapFTe{Bm*y&xBulAXtYR4x@coAA3T{S( z+enfmrRK!rlduH;EduptyUT4P$xhX%{8Pdr3OszR`C{SLwymm~&v1t(7}P)$6yp_9 zf>l87o)PigRc+fg%$iES0e}F6AOQggK?LfR-w6>AML`54;Q;gli!cC60wP!}3Ze*z zVo;O>OM+raNI{e=s?;9^QIHfw8L|XKQ4C5Jh>&nlT9+UomRhv3MV-JRAQp>&eBM}; zkxFUEKUvc)3L;P{APOP~$jFDxI{JhBpqO!E5=X%zzo0I0ng$dYjji0Tg1uz{Nx&FN z@fUuxNAmB0h$(_i^$)%o&0pW0#wXL<^&PpNm+#vc5`}NDR=U}Kf0;bL|6{fIJuRMc z{06ZM?rV6xzWNrt3V7U;=Vfj~f?uv{{DI(_O4=ToOIKYGU|Ovc&s^K^!|pSk?-JsUUo z_MDA;wBo#teEN5YT?aQTUcUMGpp6K-nvz`sATV!1mo0SEd%()1Rs41&0$xS)cN{ys zu{}ixL!-cHM>vdSdj&F;`xnMSUoCR!NQBqy0UF&hZ_11nCrCPw1N}^igHR9S>``e` zzP}c5i<#anig4DQ_*^i4n?DwC;_FS zTI+!wG*26_mDacc**#Q)$YCl(R-amnfnziUaYv;J0J_~Y3xUU}7HOSoEe8(M3`8BG zRsh;KXXu3gPbcje^76wEfqQ%gyQ;iltyHM&X&}pM<=4=<2h9O{SPX>}Y z)ay<{?0z~~=?NgES6&*1&@!!duB0BVG9c#&4X-VZ3@RBSd1)ws%s%2kje4YS5s8M6 z3q&NZ2n3c87ol1Y?}j~Od9^R*^C5AjAE1jbtXnIY(6;#kCz5uS`vO2rw;9VEh=vc$ zEzL@68?hZC68ky@;&JZp)I4+f;YUkv&fCama}(^th0CW;o#A}y^p$&G*rW+l9G+dg za`_79%U3SFbX1r*``@pgKDhsY>-`5$UjMPTz!XQ9pI^Uy`}SS+Z{NOs{pI4QFw4ph SCuizy8ht#TVlvQsrG|CF$Z0uNtnzF5e$Z7pl|XShQXENY+$iqTX= z36Tmicb{eC?hz6s(S|nw5I_V41Q0<2!&$#eqrM>hXC{PFKL}+e{X;PfgZM8+APhrM z3`0#~5h3~yp%@WGQG`qc zBBCh9pDRKr29zcJzX{@xFZ{O1ALNH*hFeB*5*GOdLy5y^K!IVj(`bdgWgk+&21@A{ zYwbz?GmscX*i`@En_=9(rxvd@?K!_&9?t7}+dxwIhBY(K_WR4o^YcIUhu_oU>85W; zWw_4a_5A85@EYKGZ{M%;6cTz}H|Zabdw0NH?R4{bd9=YG?Y1pLlA>*m+O}=mwr$%; zv^BPEM`!)NuE?nv&wP3>qW=>>ewE4$cSRiq@*Pr8K?xilA8|fL0+d0?$(4(jxWnZW zLRnY$h41m2o;C&UYzmSna~7rj+s}z|Y6c!`3o>xW{aY`{jJ9W6-^nW#+v*l`ygU#+ z0TKP`)VbBUmoAPc>}m2+>9 z>9)1wO^mv+IjU{jQLRyu@{icIZFAbpYTLHm$AKiNaWkxZBvC*)5YiL+pD06OFPu{9 z2o0Th7?8R>dy*5y-)e%tz@boqgqQB=T%hnexnFwm0*WsWyw3J8+-m!C}Yn@2F? z;du`fb0IIiTlNUotHEZM!XpOxU}nf4M{U5oyJ2jl2Dyz1GRf9^!8}m=ICf{|>+U8= z&%&l)ppl@H1Gto|NY?o5JYHz{yHFEo3wTEasH#0rf2a`x??79i2F&otCll!xvQTM}NwZnc-j<4*>vVf+qzh!j2yPz~yj%+^=vLl;aLlCmUJ=@1 oUqxT}ohc$k?l+kELG+*42lE&BA^9u&dmJbzB{Tg&{AXhg00@VR9RL6T literal 568 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 E00Nu5AOHXW diff --git a/field-smartphone/src/main/res/mipmap-xhdpi/ic_launcher.webp b/field-smartphone/src/main/res/mipmap-xhdpi/ic_launcher.webp index 262515aa6efcf94c5e7b402f34362a8abe537430..3cd1159686ec9ab50b5e23446c27a111c815e687 100644 GIT binary patch literal 2064 zcmV+r2=Dh&Nk&Ep2mkXov~4FN+qRvo^d=<)>`N(ft;ZM+F>+d8F%`H=xCT-r$*QedK4xZSIOpE! z2fJs2B%p^Uhje@`Uk8+J+m;vgM;^V z$HA_I92HFejT=nnn;ZWPiXJiXdzk&8r#osYu&83NPGjQ0Vlux5Nw-ZOcQr{UcCqQK z{MlvIF>vwL@X-8WvE)X7e|uLsm~xUM1%S|Ydf?{97fc4Y``7Z`WW<&OKx{qP@u1S* z-@TM|$D%EA07#9a6(1`7-Q8<(XE5%bD-#tGkC>d7R-Qq9=?|{>!~i`K1(cYP+g2sj zV%T-B@8zJUp|6*$?R)iEi&5279(%|#eGwCxRa?K(^JLWT=zpoKUZ?e|*>r|}hz`$b zYTh65jW+0`=7XH(MfxBzAUU^D6TGwO%>mP^10n+pyZc?Es39+%wwiVtxAup<`XGwx zcT?E2R=Y)2Gam_))N))rlv*4hTsD=sc(CP2^k(Z}&5VZikw3uyp>MUy{?VW$mFKmZ zrAvTAo3_oHfD%R-Fxo^8Qu8j+u&BbO3BU2P}r8`{AfRl*l~GVE|hUHXc}{h8sRY z#7)nEt%+z&Iu>fkI{FgWSR%3s5kFd|1f%%?8RvodNyh26k5HrkENc!zZMy|{cOEi5 zWvC#l_9Gr|&w%&BGT^>ZV@pA&d&`+u3I^V$k$L#ba|&2izk-ZfB_40aQRM6{7Knpc z3!)`9X7X;}$Weocrq?PX(|614Psn44_|a?pLJn}m=@fjgBw+TO1_u=Ja!>p}V)Q%> zu|K)(7GnudMrWS|mV^|NDgapbgO`ZNvjh>X9rkAmGJYk-DwEOYa*vSkJOs3dZ!}jC z(-vf!Vt1lKz{L0y(e+D^G1*H*2Y;xZZ?jnV&kqOr#>t3?ORi_6mVjvuus89EpCRId z0gRNw{aWH(jKOC9*-`>wH4(3YkLP`M<1q^v*e74r!xb9)RN{n06I16c#yu{3^TVJgMi|F|h;z0O;~y0C4>6Sp%|S#5CyX)Z@dtZzDKA_5 zIXe;2a`Uq-6j|tDxGhGxC}%(8l1p_(n|T1Ekc+dEbATt93>sa1*p`&TC=u5Dpr>8X zY`1)i_Aa|!>Q0nWAUo0C{@I43Qc`p4e5C*sS9sw)`eS~b5K?n{9s*BTi4E$)it1`?DngGr(M#z zrnW98q>6RSeONIsD2FKi8j*Z&i=B-0yDjx^HNtpLjaG2VDHJ$1$pb+4Pe#Q6%;EWTrg}Kv>AYxd zd#z6%`>ZeC`mQft-CBF>?!7*Jx?0_z>4xQ!S&v^LEcr_cYyh;tSWM{w;PsC>Q?a>f zF1g$+rxY6{%_SF_n_l*Z+UJ;uCfCYd9fKz)1j+oOQTSzS% u00jWcAD75C?w9+-dh#JnCz+YyhOcPqY_t_fcOTB@qDUItOb2 literal 1662 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 diff --git a/field-smartphone/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/field-smartphone/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index fe942173beda46189e0dd924c2681f0ab6b8313c..a65e04961cf452b3e9ff2cc37df43969da79e22b 100644 GIT binary patch literal 4546 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 S*R((&7K_CK0Zro^d=>!y4MuYS diff --git a/field-smartphone/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/field-smartphone/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 388c3cd4504699f6b67e702d7ea8d2c8bf70cb29..0b3454004ca46e0f3b8fe882d5aa31f3c1d223bf 100644 GIT binary patch literal 3424 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 C+L>7Z literal 2480 zcmV;h2~YM?Nk&Gf2><|BMM6+kP&iDR2><{ukH8}k=1|+V4I}^mx6?C6gu=9K7_!?> z7=t|A>lHvtx?m8&adRY|3T}*R8P_7F0A$v5fFByrNL!e-Tt~8P z+M?zf&$g}0yn~FSeVv9cE@` zW@ct)W@ct){GZKEHnaa58FgkSaSZEj9NDC#qBpwSp1y+4U0ddWLRYRu#mvl1ikX?K zQf^N*GlS~YTaw&{T}g!#*}5~g%*=fO%qu(Rs!opU{L9P?HfMe_5E}p>*nCynwrzK| zZQHhO+qP|+N&EZn$c-dLa-4=|mZ4w3J5v)NuNCAM{ZpWLS2*SDY1L4;f|GECzf&gs zIp?h!N=>56{H2fm=vBBcd%obD`^!Qw_cw}ThA(#w;uIH!a*+jqKtv z**wp5*NG`BJXS*nsZY&^7Cxd*asQS5(3u&EC^9rN!r$eeiwT1i;YKpSiMqujW!raG zey6AhL3JW%>wQ=J3BD*%!Lm+>q`(rz0XYzba4(=ax-vL1R&BAna$n_tEBrD^S?oSeqa7Mv z!e*3yt@uw{g}-VdsEU6)p;YE0Ou9uFPRs(bt?{%m zFl(EF6ARFM$Anhtw~X_*Q%W~g>J`bomwHS^DCIr0&&G*Gc>ZHj1G=bYugHyI>^epz zY%g$%pXim=@m7$GvPF)Vi=Xa?Gd!^%(fEmw%T$o4TK=mAiHLay&i}SUHnaq1RNZ6n zvuc_5QZI~>RQRE<>{p@70%xd#7t)Ld#W)T9Pwvfb^(c47P{~EE@pVPMBYJ5k9r1xu z-Sjg&t-J^JSvWBV%DTELt!(QaZI+Q;P@F4uj!jxKYEHyzTvoWu$!V4UUL|g0%otE4|{k>dSqv%BY@8C+f1#2CN)zljPTbNV=)?(@Yw2U){smLpidU ze4-a;np`N&ftj&}HNC~T%sd1G=D|L_ir&!FpJQf>Obfrpu5XV6AD)G;L{YygF%d=a z7fVQH+#1#+LmtIp!Y;$N?|=bOikIdy%=rr(Fkq1vtf07hQ$A`PX9fVEvq7{&wSumO zIiOXvEsB=T;g%3uuAX8z;@D&B005@sw09qF4fp79maXF1L0mO{+uZ;#;PxavJB7_0 z6}6P(yt>3|9-MJ5<#2?YvkwQ*xll~F>tv}$mKehVgi7~#(!SKAH;xS_1dU1ByVOz% zJ5JVK1D+j|58VD&=(N;hk&4>0)OejsJpFuU&omQ{1`N-&+qp;4+n$rRu*Z_8tgR80 zllPRC8bhXVB6>x&P{^dawas@IN<6;bIseIZT_A9zi8NWk^PVY&Y!X>ZWvtmeR7$S} z+GgJf8oN{Ku~mg&u~*n%B8(q(8rt{=ZOEx_(Rx2HNM;>93mmb)(&__M`ec318Hp+#fl&sEHlY~zBX{Tio(@k!3r2*84wfawZwL@;5pT@l#4nOg} z#=FI++GlJTzN-@{-kOXHfi)S0&ZBvP|9RCZ9w_~$p$S6<>V$fpgO>S8k6QWlSIT8+y`F+% zQHKL03G_JQXnQmJebC7LCs_Hl`{LC#2X!Dy*x>-+dtsIVFf^`TYMLa$Xu+gd)A}2w z@3Z>=j1x~INIc6@sO4TO%aZ*=l4PV{Ch(zlevdqRZB0!uNm3%X3q(i&=%Pjeq1wg6 z-8+LMQA*1Xm3hoPM-F{sSAY=dT?7C`u{)7Q0IuwtuF2Z@9Y#zaK_`hy?`@=A)y!^q z?7;9FfHFej?*tIed(u{_eNHjv!w0uyf+L{&!-r<}gG#S0WZSiE4UUlbxm>>y@YA{) zF7gThNTlC1&X#*DKG)2y(WL%>CMdYDR(69@_sN&E>&m5v7ghZ<=a{AofHiFB9tDt! zl6ci++jnjkzfGK!yDz_2c^`h)$Szp*?2Hs{_3WHgeII|9d#=0}yIM}N?AmssBwqC+ z|NFUGw=o3(2>>ks$4vmqFo7u>CNSk=wZq%v4-)B3 zdzipfSbYBGw;uJKw)($TC3IWYjX^}+d9coIemI40Z*U4dzZ`TT>|n>l;i;WkJhpP diff --git a/field-smartphone/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/field-smartphone/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp index b5bb8ad837d5e8df61477ac37b647a506dff1079..5274a989f3c70ba6d58ecea77365b2930721aabe 100644 GIT binary patch literal 3130 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 Uf874#_8+(Zxc$fNKmUP72MW_3TL1t6 literal 2080 zcmV+*2;cWoNk&E(2mkY3)dExB=>)r z8K%v)t;wS~?}+|S0I~Sr;(v?(E&jLo-{SxOB42Oh@#y#zlS~ddF}JF{S)hEIomW3+ zWaXMZtvr_QTk257oYPfa}SnXOXt&AVav`B(|^qFR@gd{<+r z*alr*2@84j1>f}sK}eriUE;}f(8vUw4prYRwP$iT@kT z_vnE0CK~uT(|izpY0@|ooqRgfe6Zg|MoiUrvH4(+o#`~6Vm{byMNGEQ7>oZb{0& zCnaq6sX#3DQtByezvxA~IH?4(PEkc-<=_2X;>lIPd7|B4RmxVa;$=4Fh9?w@m3y|0 z!~@Poh*qGT*VfaGw_4MU_o?XVW4$Hcuv4PxbAukz{^=;IeEs2dZCiqR61>r zVT}1ykyy^vsi;p~txTnlKLyav{ogQbD_rA_%%RX{y}ZV`zLe6eqIR0beLbE+m$G*n zXCFIT0M;s*T;-rCCFfGvmI55rB~e&8y1-E(j>^gwuu;jmn60K1Y?OR*m5nW+CSZ=G z>{b8t1kQOuQKW50cq_OuR!JjQYw9%fRdZ?FeBsleOiAp^H)zdZD~_gu8)L2-H5YSh z?u#n+d}>WqH^xd4lx^TsQzKP&JPE<=RZWLK3yp}Xo}Lhp~}q{A;61fWeDIn=;*VMh$t5P^k9op9Q9Vc4#B;76^|f1YGX60 z5|Bip^_zY$$3W58g;XE7D>{=JXUCnL(40e|>98<3n;PfE-Jj~Kk_oHTFFBVY?s08r z0{7pKgy&J?oZ#iXT-5|<;g>}bd%SB%I>j*`X%mu4!7=|16&i7l!@9w>EGjU5pGGch z#6t?;;%EY3Odu8dX;>)%Zvu;`)X9y_)6vA_tVUHTX>%x2L)WkkaoH4wvE8c@xU_hZ zs;9_$b^TskAw_Zc?lOWVT+XE^+|^M-;4txF7Deu>6T8@lREnJNrZzkonOTPHxbFug zjV|}=oAbMNA9Q04za%5${>Jv`wC58AzRj*fP-9R^A;p(_izdz7{yB{z{f&o`uUXym zp%m$%Jv|9f+CQ2i?c<_hJ+b~ujt;p)ym|6+*!5%E500uSWq^Q*8aV+xy5D-hK1|mT z$k-kVI( zLm(GtQG>F?xXTu!6fNi9nZ*=Y$N6>>iTPvHj>lf6WM<_S6zNKmcC79X(5-GGNU!5& z6fj(rWNsZlrBY&CBw zG6EZjl%Gif>}xIL$fRLHf01Mp@+ok3u#>&5m10dM&&DW<;*?HyMxK2xF~)o<^K6~m zRZoG$7fxz|#^a4vw6qqgl4O%9fX#mrK<>FepJ&D$3Vq((W=1_WMlr_Pl;N51r`Nkg zirlz%RSDcW7w9Pb1`*>@od*v`$NJK3MhgNcyI-L^z4uT60h5e2yrcjc+48h=C!1eV zpm1v&0^8b|g%q#0aMTG5*5bk>NjCPIfgF5}g078FmB71Ya9k~Nr?Pq_`+&>$XI zH@KEX1;!t7Qwf6G??&nGG-`q|fmCRmpDU4MGR4Ax7y|{q-1c7dln=6sKF8uYvfr<$ z=_DhoF^_Y^E3AfVnF%Z=@3!(6;|f|6~3; z<`31h9la7udb%3Hu4>PP+m5lL2bPcj#YIH`)akhrfVVRNMX1eOl;B7k+nAqx)p z9bQK!^O1xFn_Gaf{zre_^0*rC$)j0KuP8FSod*V(q^vKt>sax^&v#*P_h{mabE%*@Qp z%*@Qp%=WDHzTfx%^M|^XjIaJ{5PIn5vZ4Sixg(9~gzRPry)yO=YN}(h}Qk zU`H$5MH*>KVNOPS z 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 literal 6772 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 WE^{B%Mlrk0Fkr!EhP_Q?=L!I16+P|% diff --git a/field-smartphone/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/field-smartphone/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index 915b389d74e8d2ac6abea842583ca692b07f8b11..05cc549c148dc2a50606a4df554022c363a520c7 100644 GIT binary patch literal 4382 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 KB?IJpb0G)yGq&9T diff --git a/field-smartphone/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/field-smartphone/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp index e780d11d398d31a1b94744cbd373dd8b916b8b32..57e6fa25dc5eb932682254cfb25fa2135ee44b3f 100644 GIT binary patch literal 4428 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 S{Ql$jAHV$& literal 3036 zcmV<23nTPWNk&H03jhFDMM6+kP&iD-3jhEwufb~&_eUme6aS`bVCLB( zH)yqV$F;1S0ymlc@Nn{o5E^Gmq#^B=@4B9y>n@%@I+pRYD17KiQcq{N!<6gVmQ)%|<01rGh>&7ee=eL0q0FKRW zg{S7+%5|C5Qc>>2N}D~K#aLWw1t*+b&=yBY1~KfCo3f32$RLAg=QqC@y=1+8=^vGaeyUyD6t?j&;b z4$*(2|3v?Z{uBKt`cL$q=s(eaqW?tyiT)G)C-P~@UoW0+C29-?+Za>ZowQkf0)K6v z80AJJ{r2ebSK6-i3*K;-3l@HT`z8JYFjrSqy2)y@1#z*LrN-xmA`5BvD&J)L*mc*jl)`6&#pbe)ll`0^X^_FVjGUi zUS(W8uxUmY+7n*^)DD}Ha`N8OZ1qu;E$zmn*6efTbemto`J=et$jC^|ba6 zuw@9KhW$fpe*x|N*MnTy+nEiAYZv4FA}KY$|!)a@YpzC^1q(oM!K5_z{u>Q zg!-zv!L%3UNvky-5*i5zRJThYq>aYcDFL(w65=&2T%x4wsky+sQ>+RtY^G{w(yQQC}@}>Y{`n~(zscY$a0FriS&!Q=H z1=S1sAm!raG@=J`svSAX))r%qvx-s>jAD^PEF|=#^!53RX22sk9sCkh`4*>!+5$ZI>{Ol{{!7nwU?b_Z3zN+!L(Ih&|45d&v;C- z|HSV#Ry&=h{ghEqQDets%*o#IX#+c9kwYOHoD4Uf{^>|e)$FSiy}*l@}@rUY?0gOYC}N7Ho?cA9IlSQ`>-VXo5t zPUxu(?6y9{ds1l=OBQmx>->n!GyHybNE#?vRXc# z8!W&=@?MP_6dMxFkeVOO5wmt&j5x^h%R)YF!d~|Jo>mEZQf)&3H?Mi{NxXeSxeXL@0X1v|&$$7yGAU1om1lS|FP}flf1jyD0Bk`= zLE)Yfobp09+kxnWNeff7$90dGD#Xv1g}9e>N>tyFQ2bqz&hr#A_6t}u0w`xY z32I1T$k)ZwfA)^F7kLG2X+@REY;(%Kor>4t?wUl0EN1(}YC`~4=!%ED=vBHL!)o_WzK)bYSPxFH8;{{#ogon$k)HU?%%JggJ0(&FZC& zHpD~YwT}_^q3*|&1(REb{=yg?I(pDWX0`%xPb5S+@XNwklO0vzq0N;w2;lm@zJav@ ze7zO2BgHiW&OWF?0Qq@={yfs=ZTEA(ZXO{ZYs)*sKjJC6o$T*P0W=x8kLRo2;ogo) z@zAG;dr8}l;y=m?IiWWr00XO{d1RW-I_?2Y69IoDy^-)PPdT%R_M!ly68G?Yp3s1D zCN|o=G@7Sv|2sA$V8zCA1Ym;?)=M~EJNKKokL%C^(tEd3w*BT;1YonItsh7Mc-LEN zFd~3kyL#K1uRWJWNa`oyIBCbiD4tG<_mPlANo9!pS&Ee7eegKqX3~BFZpOz@u_?z9 z_ioBC3c!U_$`a z*2jh0TO82>AAs%04&4|Hb4L*`(|{Q zLm&z6zlo>h(Ypbjl+0u7u15&KW;GiD%oZC0IKMMaA;Cili>@G{dCYx%g#du&nk<$h z(863-H*gH(6CX@I1IUH2Rwh20>C18U6is z3!lx9yMDDQPy$YnNZWsQ4grAe@xHMXfK1tO*8Z$AJC%lSmqjY2xCmfi(ix_l=bmnm z`YW9;9_Nd-xWOdit%phdW!Oag?+6##@#b|pYaUFZP-ZCc7fN+`R8D7bP7@JM7t!Icv! zkA%qN>NZ__0K|s-%VkoD9U_w}xDp)kdN+v&Xd3E8IXi&v77w6lLA~w`H67ytnuU5( eqW?wzi~bk=FZy5fzvzF_|Dyjz|BL?rhcEye=j!SJ diff --git a/field-smartphone/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/field-smartphone/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index c6cafcde0254e24d8d99d17f6138f7fe2de796b3..952ab81dfbf6973bfc3cebbb412f715eff666231 100644 GIT binary patch literal 10248 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 O;KqYY1{u5_E^{&`mrASv literal 9564 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 - abstract val foregroundServiceTypes: Array - open val TAG: String = this::class.simpleName ?: "UnnamedClass" - open val NAME: String = extractName(this::class.simpleName ?: "UnknownCollector") - - var listener: ((DataEntity)-> Unit)? = null - - abstract class Config - abstract class DataEntity - - /* Check whether the system allow to collect data - * In case of sensor malfunction or broken, it would not be available.*/ - abstract fun isAvailable(): Boolean - - /* Enable the collector by checking and requesting permissions - * Different with `isAvailable`, `enable` is used to request permissions when - * the collector is available, but does not have permission - * */ - fun enable(permissionManager: PermissionManagerInterface, onResult: (granted: Boolean)-> Unit) { - permissionManager.request(permissions){ - Log.d(TAG, "Permission granted: ${permissions.all { permission -> it[permission] == true }}") - onResult(permissions.all { permission -> it[permission] == true }) - } - } - - - /* Start collector to collect data - * */ - abstract fun start() - - /* Stop collector to stop collecting data - * */ - abstract fun stop() - - - private fun extractName(className: String): String { - // Replace "Collector" with an empty string - val nameWithoutCollector = className.replace("Collector", "") - - // Split the name into parts based on camel case and underscores - val parts = nameWithoutCollector.split("(?=\\p{Upper})|_|(?<=\\p{Lower})(?=\\p{Upper})".toRegex()) - - // Join the parts and convert to uppercase - return parts.joinToString("_").uppercase() - } - -} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ActivityTransitionCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ActivityTransitionCollector.kt index 0f5be4b..8d4f2cc 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ActivityTransitionCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ActivityTransitionCollector.kt @@ -1,106 +1,106 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager -import android.net.wifi.WifiManager -import android.os.Build -import com.google.android.gms.location.ActivityRecognition -import com.google.android.gms.location.ActivityRecognitionClient -import com.google.android.gms.location.ActivityTransition -import com.google.android.gms.location.ActivityTransitionRequest -import com.google.android.gms.location.ActivityTransitionResult -import com.google.android.gms.location.DetectedActivity -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger -import java.util.concurrent.TimeUnit - -class ActivityTransitionCollector( - override val context: Context -) : AbstractCollector(context) { - - val ACTION = "kaist.iclab.tracker.${NAME}_REQUEST" - val CODE = 0xF1 - - data class DataEntity( - val timestamp: Long, - val activityType: Int, - val transitionType: Int - ) : AbstractCollector.DataEntity() - - override val permissions = listOfNotNull( - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else null, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else null - ).toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - - override fun isAvailable(): Boolean = true - - private val client: ActivityRecognitionClient by lazy { - ActivityRecognition.getClient(context) - } - - private val activityTransitionIntent by lazy { - PendingIntent.getBroadcast( - context, CODE, - Intent(ACTION), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - } - - private val broadcastTrigger = SystemBroadcastTrigger( - context, - arrayOf( - ACTION - ) - ) { - val result = ActivityTransitionResult.extractResult(it) - result?.transitionEvents?.forEach { event -> - listener?.invoke( - DataEntity( - System.currentTimeMillis(), - event.activityType, - event.transitionType - ) - ) - } - } - - - override fun start() { - val request = listOf( - DetectedActivity.IN_VEHICLE, - DetectedActivity.ON_BICYCLE, - DetectedActivity.RUNNING, - DetectedActivity.ON_FOOT, - DetectedActivity.STILL, - DetectedActivity.WALKING - ).map { activity -> - listOf( - ActivityTransition.Builder() - .setActivityType(activity) - .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER) - .build(), - ActivityTransition.Builder() - .setActivityType(activity) - .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT) - .build() - ) - }.flatten().let { ActivityTransitionRequest(it) } - client.requestActivityTransitionUpdates(request, activityTransitionIntent) - broadcastTrigger.register() - } - - override fun stop() { - client.removeActivityTransitionUpdates(activityTransitionIntent) - broadcastTrigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.app.PendingIntent +//import android.content.Context +//import android.content.Intent +//import android.hardware.Sensor +//import android.hardware.SensorEvent +//import android.hardware.SensorEventListener +//import android.hardware.SensorManager +//import android.net.wifi.WifiManager +//import android.os.Build +//import com.google.android.gms.location.ActivityRecognition +//import com.google.android.gms.location.ActivityRecognitionClient +//import com.google.android.gms.location.ActivityTransition +//import com.google.android.gms.location.ActivityTransitionRequest +//import com.google.android.gms.location.ActivityTransitionResult +//import com.google.android.gms.location.DetectedActivity +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +//import java.util.concurrent.TimeUnit +// +//class ActivityTransitionCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// val ACTION = "kaist.iclab.tracker.${NAME}_REQUEST" +// val CODE = 0xF1 +// +// data class DataEntity( +// val timestamp: Long, +// val activityType: Int, +// val transitionType: Int +// ) : AbstractCollector.DataEntity() +// +// override val permissions = listOfNotNull( +// Manifest.permission.ACCESS_COARSE_LOCATION, +// Manifest.permission.ACCESS_FINE_LOCATION, +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else null, +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else null +// ).toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// +// override fun isAvailable(): Boolean = true +// +// private val client: ActivityRecognitionClient by lazy { +// ActivityRecognition.getClient(context) +// } +// +// private val activityTransitionIntent by lazy { +// PendingIntent.getBroadcast( +// context, CODE, +// Intent(ACTION), +// PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE +// ) +// } +// +// private val broadcastTrigger = SystemBroadcastTrigger( +// context, +// arrayOf( +// ACTION +// ) +// ) { +// val result = ActivityTransitionResult.extractResult(it) +// result?.transitionEvents?.forEach { event -> +// listener?.invoke( +// DataEntity( +// System.currentTimeMillis(), +// event.activityType, +// event.transitionType +// ) +// ) +// } +// } +// +// +// override fun start() { +// val request = listOf( +// DetectedActivity.IN_VEHICLE, +// DetectedActivity.ON_BICYCLE, +// DetectedActivity.RUNNING, +// DetectedActivity.ON_FOOT, +// DetectedActivity.STILL, +// DetectedActivity.WALKING +// ).map { activity -> +// listOf( +// ActivityTransition.Builder() +// .setActivityType(activity) +// .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER) +// .build(), +// ActivityTransition.Builder() +// .setActivityType(activity) +// .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT) +// .build() +// ) +// }.flatten().let { ActivityTransitionRequest(it) } +// client.requestActivityTransitionUpdates(request, activityTransitionIntent) +// broadcastTrigger.register() +// } +// +// override fun stop() { +// client.removeActivityTransitionUpdates(activityTransitionIntent) +// broadcastTrigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AmbientLightCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AmbientLightCollector.kt index 0292a43..74060cc 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AmbientLightCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AmbientLightCollector.kt @@ -5,48 +5,84 @@ import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager +import kaist.iclab.tracker.controller.AbstractCollector +import kaist.iclab.tracker.controller.Availability +import kaist.iclab.tracker.controller.CollectorConfig +import kaist.iclab.tracker.controller.CollectorState +import kaist.iclab.tracker.controller.DataEntity +import kaist.iclab.tracker.permission.PermissionManagerInterface import java.util.concurrent.TimeUnit class AmbientLightCollector( - override val context: Context -) : AbstractCollector(context) { - var config: Config = Config( - TimeUnit.MINUTES.toMillis(3), - ) - - data class DataEntity( - val timestamp: Long, - val accuracy: Int, - val value: Float - ) : AbstractCollector.DataEntity() - + val context: Context, + permissionManager: PermissionManagerInterface, +) : AbstractCollector(permissionManager) { + override val permissions = listOfNotNull().toTypedArray() + override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() data class Config( val interval: Long - ) : AbstractCollector.Config() + ) : CollectorConfig() + override val defaultConfig = Config( + TimeUnit.SECONDS.toMillis(3) + ) - override val permissions = listOfNotNull().toTypedArray() + // Check whether there is a light sensor + override fun isAvailable(): Availability { + val status = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) != null + return Availability(status, if (status) null else "AmbientLight Sensor is not available") + } - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() + override fun enable() { + if (_stateFlow.value.flag == CollectorState.FLAG.DISABLED) { + _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.ENABLED)) + } + } - // Check whether there is a light sensor - override fun isAvailable(): Boolean { - return sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) != null + override fun disable() { + if (_stateFlow.value.flag == CollectorState.FLAG.ENABLED) { + _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.DISABLED)) + } } - private val sensorManager: SensorManager by lazy { + override fun start() { + _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.RUNNING)) + sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)?.let { sensor -> + sensorManager.registerListener( + sensorEventListener, + sensor, + TimeUnit.MILLISECONDS.toMicros(configFlow.value.interval).toInt() + ) + } + } + + override fun stop() { + sensorManager.unregisterListener(sensorEventListener) + _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.ENABLED)) + } + + data class Entity( + override val received: Long, + val timestamp: Long, + val accuracy: Int, + val value: Float + ) : DataEntity(received) + + private val sensorManager: SensorManager by lazy{ context.getSystemService(Context.SENSOR_SERVICE) as SensorManager } + private val sensorEventListener = object:SensorEventListener { override fun onAccuracyChanged(sensor: Sensor?, p1: Int) {} - override fun onSensorChanged(event: SensorEvent?) { + val timestamp = System.currentTimeMillis() event?.let { listener?.invoke( - DataEntity( - System.currentTimeMillis(), + Entity( + timestamp, + it.timestamp, it.accuracy, it.values[0] ) @@ -55,17 +91,5 @@ class AmbientLightCollector( } } - override fun start() { - sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)?.let { sensor -> - sensorManager.registerListener( - sensorEventListener, - sensor, - TimeUnit.MILLISECONDS.toMicros(config.interval).toInt() - ) - } - } - override fun stop() { - sensorManager.unregisterListener(sensorEventListener) - } } \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AppUsageLogCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AppUsageLogCollector.kt index 4f434c9..7523f54 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AppUsageLogCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/AppUsageLogCollector.kt @@ -1,79 +1,80 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.app.PendingIntent -import android.app.usage.UsageEvents -import android.app.usage.UsageStatsManager -import android.content.Context -import android.content.Intent -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager -import android.net.wifi.WifiManager -import android.os.Build -import com.google.android.gms.location.ActivityRecognition -import com.google.android.gms.location.ActivityRecognitionClient -import com.google.android.gms.location.ActivityTransition -import com.google.android.gms.location.ActivityTransitionRequest -import com.google.android.gms.location.ActivityTransitionResult -import com.google.android.gms.location.DetectedActivity -import kaist.iclab.tracker.triggers.AlarmTrigger -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger -import java.util.concurrent.TimeUnit - -class AppUsageLogCollector( - override val context: Context -) : AbstractCollector(context) { - - val ACTION = "kaist.iclab.tracker.${NAME}_REQUEST" - val CODE = 0xEE - - var config = Config( - TimeUnit.MINUTES.toMillis(30) - ) - - data class Config( - val interval: Long, - ) : AbstractCollector.Config() - - data class DataEntity( - val timestamp: Long, - val packageName: String, - val eventType: Int - ) : AbstractCollector.DataEntity() - - override val permissions = listOfNotNull( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Manifest.permission.PACKAGE_USAGE_STATS else null, - ).toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - override fun isAvailable(): Boolean = true - - private val alarmTrigger = AlarmTrigger( - context, - ACTION, - CODE, - config.interval - ){ - val usageStatManager =context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - val timestamp = System.currentTimeMillis() - /*Give margin for alarm amy not correctly given*/ - val events = usageStatManager.queryEvents(timestamp - config.interval - TimeUnit.MINUTES.toMillis(5), timestamp) - val event = UsageEvents.Event() - while(events.hasNextEvent()){ - events.getNextEvent(event) - listener?.invoke(DataEntity(event.timeStamp, event.packageName, event.eventType)) - } - } - - - override fun start() { - alarmTrigger.register() - } - - override fun stop() { - alarmTrigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.app.PendingIntent +//import android.app.usage.UsageEvents +//import android.app.usage.UsageStatsManager +//import android.content.Context +//import android.content.Intent +//import android.hardware.Sensor +//import android.hardware.SensorEvent +//import android.hardware.SensorEventListener +//import android.hardware.SensorManager +//import android.net.wifi.WifiManager +//import android.os.Build +//import com.google.android.gms.location.ActivityRecognition +//import com.google.android.gms.location.ActivityRecognitionClient +//import com.google.android.gms.location.ActivityTransition +//import com.google.android.gms.location.ActivityTransitionRequest +//import com.google.android.gms.location.ActivityTransitionResult +//import com.google.android.gms.location.DetectedActivity +//import kaist.iclab.tracker.triggers.AlarmTrigger +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +//import java.util.concurrent.TimeUnit +// +//class AppUsageLogCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// val ACTION = "kaist.iclab.tracker.${NAME}_REQUEST" +// val CODE = 0xEE +// +// var config = Config( +// TimeUnit.MINUTES.toMillis(30) +// ) +// +// data class Config( +// val interval: Long, +// ) : AbstractCollector.Config() +// +// data class DataEntity( +// val timestamp: Long, +// val packageName: String, +//// val installed: String, +// val eventType: Int +// ) : AbstractCollector.DataEntity() +// +// override val permissions = listOfNotNull( +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Manifest.permission.PACKAGE_USAGE_STATS else null, +// ).toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// override fun isAvailable(): Boolean = true +// +// private val alarmTrigger = AlarmTrigger( +// context, +// ACTION, +// CODE, +// config.interval +// ){ +// val usageStatManager =context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager +// val timestamp = System.currentTimeMillis() +// /*Give margin for alarm amy not correctly given*/ +// val events = usageStatManager.queryEvents(timestamp - config.interval - TimeUnit.MINUTES.toMillis(5), timestamp) +// val event = UsageEvents.Event() +// while(events.hasNextEvent()){ +// events.getNextEvent(event) +// listener?.invoke(DataEntity(event.timeStamp, event.packageName, event.eventType)) +// } +// } +// +// +// override fun start() { +// alarmTrigger.register() +// } +// +// override fun stop() { +// alarmTrigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/BatteryCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/BatteryCollector.kt index 3f6ea46..3e1d148 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/BatteryCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/BatteryCollector.kt @@ -1,50 +1,50 @@ -package kaist.iclab.tracker.collectors - -import android.content.Context -import android.content.Intent -import android.os.BatteryManager -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger - -class BatteryCollector( - override val context: Context -) : AbstractCollector(context) { - - data class DataEntity( - val timestamp: Long, - val connectedType: Int, - val status: Int, - val level: Int, - val temperature: Int - ): AbstractCollector.DataEntity() - - // No permission required for it - override val permissions: Array = emptyArray() - override val foregroundServiceTypes: Array = emptyArray() - - val trigger: SystemBroadcastTrigger = SystemBroadcastTrigger( - context, - arrayOf( - Intent.ACTION_BATTERY_CHANGED - ) - ){ intent -> - listener?.invoke(DataEntity( - System.currentTimeMillis(), - intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1), - intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1), - intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1), - intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1) - )) - - } - - // Access to Battery Status might be supported for all android systems - override fun isAvailable(): Boolean = true - - override fun start() { - trigger.register() - } - - override fun stop() { - trigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.content.Context +//import android.content.Intent +//import android.os.BatteryManager +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +// +//class BatteryCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// data class DataEntity( +// val timestamp: Long, +// val connectedType: Int, +// val status: Int, +// val level: Int, +// val temperature: Int +// ): AbstractCollector.DataEntity() +// +// // No permission required for it +// override val permissions: Array = emptyArray() +// override val foregroundServiceTypes: Array = emptyArray() +// +// val trigger: SystemBroadcastTrigger = SystemBroadcastTrigger( +// context, +// arrayOf( +// Intent.ACTION_BATTERY_CHANGED +// ) +// ){ intent -> +// listener?.invoke(DataEntity( +// System.currentTimeMillis(), +// intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1), +// intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1), +// intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1), +// intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1) +// )) +// +// } +// +// // Access to Battery Status might be supported for all android systems +// override fun isAvailable(): Boolean = true +// +// override fun start() { +// trigger.register() +// } +// +// override fun stop() { +// trigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/CallLogCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/CallLogCollector.kt index 6040037..178d1f0 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/CallLogCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/CallLogCollector.kt @@ -1,86 +1,86 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.content.Context -import android.provider.CallLog -import kaist.iclab.tracker.triggers.AlarmTrigger -import java.util.concurrent.TimeUnit - -class CallLogCollector( - override val context: Context -) : AbstractCollector(context) { - - var config: Config = Config( - TimeUnit.MINUTES.toMillis(30) - ) - data class DataEntity( - val timestamp: Long, - val duration: Long, - val number: String, - val type: Int - ) : AbstractCollector.DataEntity() - - data class Config( - val interval: Long - ) : AbstractCollector.Config() - - - // No permission required for it - override val permissions: Array = arrayOf( - Manifest.permission.READ_CONTACTS, - Manifest.permission.READ_CALL_LOG - ) - override val foregroundServiceTypes: Array = emptyArray() - - - - // Access to Battery Status might be supported for all android systems - override fun isAvailable(): Boolean = true - - val trigger = AlarmTrigger( - context, - "kaist.iclab.tracker.${NAME}_REQUEST", - 0x11, - config.interval - ){ - val current = System.currentTimeMillis() - val from = current - config.interval - TimeUnit.MINUTES.toMillis(5) - val cursor = context.contentResolver.query( - CallLog.Calls.CONTENT_URI, - arrayOf( - CallLog.Calls.DATE, - CallLog.Calls.NUMBER, - CallLog.Calls.DURATION, - CallLog.Calls.TYPE - ), - "${CallLog.Calls.DATE} BETWEEN ? AND ?", - arrayOf(from.toString(), current.toString()), - CallLog.Calls.DATE + " DESC" - ) - cursor?.use { - while (it.moveToNext()) { - val timestamp = it.getLong(it.getColumnIndexOrThrow(CallLog.Calls.DATE)) - val number = it.getString(it.getColumnIndexOrThrow(CallLog.Calls.NUMBER)) - val duration = it.getString(it.getColumnIndexOrThrow(CallLog.Calls.DURATION)) - val type = it.getInt(it.getColumnIndexOrThrow(CallLog.Calls.TYPE)) - listener?.invoke( - DataEntity( - timestamp, - duration.toLong(), - number, - type - ) - ) - } - } - cursor?.close() - } - - override fun start() { - trigger.register() - } - - override fun stop() { - trigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.content.Context +//import android.provider.CallLog +//import kaist.iclab.tracker.triggers.AlarmTrigger +//import java.util.concurrent.TimeUnit +// +//class CallLogCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// var config: Config = Config( +// TimeUnit.MINUTES.toMillis(30) +// ) +// data class DataEntity( +// val timestamp: Long, +// val duration: Long, +// val number: String, +// val type: Int +// ) : AbstractCollector.DataEntity() +// +// data class Config( +// val interval: Long +// ) : AbstractCollector.Config() +// +// +// // No permission required for it +// override val permissions: Array = arrayOf( +// Manifest.permission.READ_CONTACTS, +// Manifest.permission.READ_CALL_LOG +// ) +// override val foregroundServiceTypes: Array = emptyArray() +// +// +// +// // Access to Battery Status might be supported for all android systems +// override fun isAvailable(): Boolean = true +// +// val trigger = AlarmTrigger( +// context, +// "kaist.iclab.tracker.${NAME}_REQUEST", +// 0x11, +// config.interval +// ){ +// val current = System.currentTimeMillis() +// val from = current - config.interval - TimeUnit.MINUTES.toMillis(5) +// val cursor = context.contentResolver.query( +// CallLog.Calls.CONTENT_URI, +// arrayOf( +// CallLog.Calls.DATE, +// CallLog.Calls.NUMBER, +// CallLog.Calls.DURATION, +// CallLog.Calls.TYPE +// ), +// "${CallLog.Calls.DATE} BETWEEN ? AND ?", +// arrayOf(from.toString(), current.toString()), +// CallLog.Calls.DATE + " DESC" +// ) +// cursor?.use { +// while (it.moveToNext()) { +// val timestamp = it.getLong(it.getColumnIndexOrThrow(CallLog.Calls.DATE)) +// val number = it.getString(it.getColumnIndexOrThrow(CallLog.Calls.NUMBER)) +// val duration = it.getString(it.getColumnIndexOrThrow(CallLog.Calls.DURATION)) +// val type = it.getInt(it.getColumnIndexOrThrow(CallLog.Calls.TYPE)) +// listener?.invoke( +// DataEntity( +// timestamp, +// duration.toLong(), +// number, +// type +// ) +// ) +// } +// } +// cursor?.close() +// } +// +// override fun start() { +// trigger.register() +// } +// +// override fun stop() { +// trigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/DataTrafficStatCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/DataTrafficStatCollector.kt index 6d4f3ea..5a4ab32 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/DataTrafficStatCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/DataTrafficStatCollector.kt @@ -1,60 +1,60 @@ -package kaist.iclab.tracker.collectors - -import android.content.Context -import android.net.TrafficStats -import kaist.iclab.tracker.collectors.LocationCollector.Config -import kaist.iclab.tracker.triggers.AlarmTrigger -import java.util.concurrent.TimeUnit - -class DataTrafficStatCollector( - override val context: Context -) : AbstractCollector(context) { - - val ACTION = "kaist.iclab.tracker.ACTION_DATA_TRAFFIC_STAT" - val CODE = 0x1 - - var config: Config = Config( - TimeUnit.MINUTES.toMillis(3) - ) - - data class DataEntity( - val timestamp: Long, - val totalRx: Long, - val totalTx: Long, - val mobileRx: Long, - val mobileTx: Long, - ) : AbstractCollector.DataEntity() - - data class Config( - val interval: Long, - ) : AbstractCollector.Config() - - override val permissions = listOfNotNull().toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - - override fun isAvailable(): Boolean = true - - private val alarmTrigger = AlarmTrigger(context, ACTION, CODE, - config.interval) { - val timestamp = System.currentTimeMillis() - listener?.invoke( - DataEntity( - timestamp, - TrafficStats.getTotalRxBytes(), - TrafficStats.getTotalTxBytes(), - TrafficStats.getMobileRxBytes(), - TrafficStats.getMobileTxBytes(), - ) - ) - } - - override fun start() { - alarmTrigger.register() - } - - override fun stop() { - alarmTrigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.content.Context +//import android.net.TrafficStats +//import kaist.iclab.tracker.collectors.LocationCollector.Config +//import kaist.iclab.tracker.triggers.AlarmTrigger +//import java.util.concurrent.TimeUnit +// +//class DataTrafficStatCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// val ACTION = "kaist.iclab.tracker.ACTION_DATA_TRAFFIC_STAT" +// val CODE = 0x1 +// +// var config: Config = Config( +// TimeUnit.MINUTES.toMillis(3) +// ) +// +// data class DataEntity( +// val timestamp: Long, +// val totalRx: Long, +// val totalTx: Long, +// val mobileRx: Long, +// val mobileTx: Long, +// ) : AbstractCollector.DataEntity() +// +// data class Config( +// val interval: Long, +// ) : AbstractCollector.Config() +// +// override val permissions = listOfNotNull().toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// +// override fun isAvailable(): Boolean = true +// +// private val alarmTrigger = AlarmTrigger(context, ACTION, CODE, +// config.interval) { +// val timestamp = System.currentTimeMillis() +// listener?.invoke( +// DataEntity( +// timestamp, +// TrafficStats.getTotalRxBytes(), +// TrafficStats.getTotalTxBytes(), +// TrafficStats.getMobileRxBytes(), +// TrafficStats.getMobileTxBytes(), +// ) +// ) +// } +// +// override fun start() { +// alarmTrigger.register() +// } +// +// override fun stop() { +// alarmTrigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/LocationCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/LocationCollector.kt index c219d91..021db50 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/LocationCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/LocationCollector.kt @@ -1,133 +1,133 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.content.pm.ServiceInfo -import android.location.LocationManager -import android.os.Build -import android.util.Log -import com.google.android.gms.location.FusedLocationProviderClient -import com.google.android.gms.location.LocationRequest -import com.google.android.gms.location.LocationResult -import com.google.android.gms.location.LocationServices -import com.google.android.gms.location.Priority -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger -import java.util.concurrent.TimeUnit - -class LocationCollector( - override val context: Context -) : AbstractCollector(context) { - - val ACTION = "android.intent.action.LOCATION_CHANGED" - - var config: Config = Config( - TimeUnit.MINUTES.toMillis(3), - 0, - TimeUnit.MINUTES.toMillis(10), - 0.0f, - 0, - Priority.PRIORITY_HIGH_ACCURACY - ) - - data class DataEntity( - val timestamp: Long, - val latitude: Double, - val longitude: Double, - val altitude: Double, - val speed: Float, - val accuracy: Float - ) : AbstractCollector.DataEntity() - - - data class Config( - val interval: Long, - val maxUpdateAge: Long, - val maxUpdateDelay: Long, - val minUpdateDistance: Float, - val minUpdateInterval: Long, - val priority: Int - ) : AbstractCollector.Config() - - - override val permissions = listOfNotNull( - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else null - ).toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION else null - ).toTypedArray() - - lateinit var trigger: SystemBroadcastTrigger - - // Check whether there is at least one location provider - override fun isAvailable(): Boolean { - val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager - val pm = context.packageManager - - // Check if the device has GPS hardware - val hasGpsHardware = pm.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS) - - // Check if any location provider is enabled (GPS or Network) - val locationEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || - locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) - - // Return true if the device has GPS hardware and location providers are enabled - return hasGpsHardware && locationEnabled - } - - private val client: FusedLocationProviderClient by lazy { - LocationServices.getFusedLocationProviderClient(context) - } - - private val intent: PendingIntent by lazy { - PendingIntent.getBroadcast( - context, - 0xFF, - Intent(ACTION), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - } - - override fun start() { - trigger = SystemBroadcastTrigger( - context, - arrayOf(ACTION) - ) { intent -> - if (ACTION != intent.action) { - Log.e(TAG, "Invalid action: ${intent.action}") - } - val location = LocationResult.extractResult(intent)?.lastLocation - ?: return@SystemBroadcastTrigger - listener?.invoke( - DataEntity( - location.time, - location.longitude, - location.latitude, - location.altitude, - location.speed, - location.accuracy, - ) - ) - } - trigger.register() - - val request = LocationRequest.Builder(config.interval) - .setMaxUpdateDelayMillis(config.maxUpdateDelay) - .setMinUpdateDistanceMeters(config.minUpdateDistance) - .setMaxUpdateAgeMillis(config.maxUpdateAge) - .setMaxUpdateDelayMillis(config.maxUpdateDelay) - .setPriority(Priority.PRIORITY_HIGH_ACCURACY) - .build() - client.requestLocationUpdates(request, intent) - } - - override fun stop() { - trigger.unregister() - client.removeLocationUpdates(intent) - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.app.PendingIntent +//import android.content.Context +//import android.content.Intent +//import android.content.pm.PackageManager +//import android.content.pm.ServiceInfo +//import android.location.LocationManager +//import android.os.Build +//import android.util.Log +//import com.google.android.gms.location.FusedLocationProviderClient +//import com.google.android.gms.location.LocationRequest +//import com.google.android.gms.location.LocationResult +//import com.google.android.gms.location.LocationServices +//import com.google.android.gms.location.Priority +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +//import java.util.concurrent.TimeUnit +// +//class LocationCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// val ACTION = "android.intent.action.LOCATION_CHANGED" +// +// var config: Config = Config( +// TimeUnit.MINUTES.toMillis(3), +// 0, +// TimeUnit.MINUTES.toMillis(10), +// 0.0f, +// 0, +// Priority.PRIORITY_HIGH_ACCURACY +// ) +// +// data class DataEntity( +// val timestamp: Long, +// val latitude: Double, +// val longitude: Double, +// val altitude: Double, +// val speed: Float, +// val accuracy: Float +// ) : AbstractCollector.DataEntity() +// +// +// data class Config( +// val interval: Long, +// val maxUpdateAge: Long, +// val maxUpdateDelay: Long, +// val minUpdateDistance: Float, +// val minUpdateInterval: Long, +// val priority: Int +// ) : AbstractCollector.Config() +// +// +// override val permissions = listOfNotNull( +// Manifest.permission.ACCESS_COARSE_LOCATION, +// Manifest.permission.ACCESS_FINE_LOCATION, +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else null +// ).toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull( +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION else null +// ).toTypedArray() +// +// lateinit var trigger: SystemBroadcastTrigger +// +// // Check whether there is at least one location provider +// override fun isAvailable(): Boolean { +// val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager +// val pm = context.packageManager +// +// // Check if the device has GPS hardware +// val hasGpsHardware = pm.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS) +// +// // Check if any location provider is enabled (GPS or Network) +// val locationEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || +// locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) +// +// // Return true if the device has GPS hardware and location providers are enabled +// return hasGpsHardware && locationEnabled +// } +// +// private val client: FusedLocationProviderClient by lazy { +// LocationServices.getFusedLocationProviderClient(context) +// } +// +// private val intent: PendingIntent by lazy { +// PendingIntent.getBroadcast( +// context, +// 0xFF, +// Intent(ACTION), +// PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE +// ) +// } +// +// override fun start() { +// trigger = SystemBroadcastTrigger( +// context, +// arrayOf(ACTION) +// ) { intent -> +// if (ACTION != intent.action) { +// Log.e(TAG, "Invalid action: ${intent.action}") +// } +// val location = LocationResult.extractResult(intent)?.lastLocation +// ?: return@SystemBroadcastTrigger +// listener?.invoke( +// DataEntity( +// location.time, +// location.longitude, +// location.latitude, +// location.altitude, +// location.speed, +// location.accuracy, +// ) +// ) +// } +// trigger.register() +// +// val request = LocationRequest.Builder(config.interval) +// .setMaxUpdateDelayMillis(config.maxUpdateDelay) +// .setMinUpdateDistanceMeters(config.minUpdateDistance) +// .setMaxUpdateAgeMillis(config.maxUpdateAge) +// .setMaxUpdateDelayMillis(config.maxUpdateDelay) +// .setPriority(Priority.PRIORITY_HIGH_ACCURACY) +// .build() +// client.requestLocationUpdates(request, intent) +// } +// +// override fun stop() { +// trigger.unregister() +// client.removeLocationUpdates(intent) +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/MessageLogCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/MessageLogCollector.kt index 2a212db..eea1f53 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/MessageLogCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/MessageLogCollector.kt @@ -1,133 +1,133 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.content.ContentResolver -import android.content.Context -import android.database.Cursor -import android.net.Uri -import android.provider.CallLog -import android.provider.Telephony -import kaist.iclab.tracker.triggers.AlarmTrigger -import java.util.concurrent.TimeUnit - -class MessageLogCollector( - override val context: Context -) : AbstractCollector(context) { - - var config: Config = Config( - TimeUnit.MINUTES.toMillis(30) - ) - data class DataEntity( - val timestamp: Long, - val number: String, - val messageType: String, - val contactType: Int - ) : AbstractCollector.DataEntity() - - data class Config( - val interval: Long - ) : AbstractCollector.Config() - - - // No permission required for it - override val permissions: Array = arrayOf( - Manifest.permission.READ_CONTACTS, - Manifest.permission.READ_SMS - ) - override val foregroundServiceTypes: Array = emptyArray() - - - - // Access to Battery Status might be supported for all android systems - override fun isAvailable(): Boolean = true - - val trigger = AlarmTrigger( - context, - "kaist.iclab.tracker.${NAME}_REQUEST", - 0x13, - config.interval - ){ - val current = System.currentTimeMillis() - val from = current - config.interval - TimeUnit.MINUTES.toMillis(5) - var cursor = context.contentResolver.query( - Telephony.Sms.CONTENT_URI, - arrayOf( - Telephony.Sms.ADDRESS, // Sender/receiver number - Telephony.Sms.DATE, // Timestamp - Telephony.Sms.TYPE, // Type (Incoming/Outgoing) - ), - "${Telephony.Sms.DATE} BETWEEN ? AND ?", - arrayOf(from.toString(), current.toString()), - Telephony.Sms.DATE + " DESC" - ) - cursor?.use { - while (it.moveToNext()) { - val timestamp = it.getLong(it.getColumnIndexOrThrow(Telephony.Sms.DATE)) - val number = it.getString(it.getColumnIndexOrThrow(Telephony.Sms.ADDRESS)) - val type = it.getInt(it.getColumnIndexOrThrow(Telephony.Sms.TYPE)) - - listener?.invoke( - DataEntity( - timestamp, - number, - "SMS", - type - ) - ) - } - } - cursor?.close() - cursor = context.contentResolver.query( - Telephony.Mms.CONTENT_URI, - arrayOf( - Telephony.Mms._ID, // MMS ID - Telephony.Mms.DATE, // Timestamp - Telephony.Mms.MESSAGE_BOX // Type (Incoming/Outgoing) - ), - "${Telephony.Mms.DATE} BETWEEN ? AND ?", - arrayOf(from.toString(), current.toString()), - Telephony.Mms.DATE + " DESC" - ) - cursor?.use { - while (it.moveToNext()) { - val number = it.getLong(it.getColumnIndexOrThrow(Telephony.Mms._ID)) - val timestamp = it.getLong(it.getColumnIndexOrThrow(Telephony.Mms.DATE)) * 1000 // MMS date is in seconds - val type = it.getInt(it.getColumnIndexOrThrow(Telephony.Mms.MESSAGE_BOX)) - - - listener?.invoke( - DataEntity( - timestamp, - getMmsAddress(context.contentResolver, number) ?: "UNKNOWN", - "MMS", - type - ) - ) - } - } - cursor?.close() - } - - fun getMmsAddress(contentResolver: ContentResolver, mmsId: Long): String? { - // Build the URI for the MMS address table using the MMS ID - val uri = Uri.parse("content://mms/$mmsId/addr") - val projection = arrayOf(Telephony.Mms.Addr.ADDRESS) - - val cursor: Cursor? = contentResolver.query(uri, projection, null,null, null) - - cursor?.use { - if (it.moveToFirst()) { - return it.getString(it.getColumnIndexOrThrow(Telephony.Mms.Addr.ADDRESS)) - } - } - return null - } - - override fun start() { - trigger.register() - } - - override fun stop() { - trigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.content.ContentResolver +//import android.content.Context +//import android.database.Cursor +//import android.net.Uri +//import android.provider.CallLog +//import android.provider.Telephony +//import kaist.iclab.tracker.triggers.AlarmTrigger +//import java.util.concurrent.TimeUnit +// +//class MessageLogCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// var config: Config = Config( +// TimeUnit.MINUTES.toMillis(30) +// ) +// data class DataEntity( +// val timestamp: Long, +// val number: String, +// val messageType: String, +// val contactType: Int +// ) : AbstractCollector.DataEntity() +// +// data class Config( +// val interval: Long +// ) : AbstractCollector.Config() +// +// +// // No permission required for it +// override val permissions: Array = arrayOf( +// Manifest.permission.READ_CONTACTS, +// Manifest.permission.READ_SMS +// ) +// override val foregroundServiceTypes: Array = emptyArray() +// +// +// +// // Access to Battery Status might be supported for all android systems +// override fun isAvailable(): Boolean = true +// +// val trigger = AlarmTrigger( +// context, +// "kaist.iclab.tracker.${NAME}_REQUEST", +// 0x13, +// config.interval +// ){ +// val current = System.currentTimeMillis() +// val from = current - config.interval - TimeUnit.MINUTES.toMillis(5) +// var cursor = context.contentResolver.query( +// Telephony.Sms.CONTENT_URI, +// arrayOf( +// Telephony.Sms.ADDRESS, // Sender/receiver number +// Telephony.Sms.DATE, // Timestamp +// Telephony.Sms.TYPE, // Type (Incoming/Outgoing) +// ), +// "${Telephony.Sms.DATE} BETWEEN ? AND ?", +// arrayOf(from.toString(), current.toString()), +// Telephony.Sms.DATE + " DESC" +// ) +// cursor?.use { +// while (it.moveToNext()) { +// val timestamp = it.getLong(it.getColumnIndexOrThrow(Telephony.Sms.DATE)) +// val number = it.getString(it.getColumnIndexOrThrow(Telephony.Sms.ADDRESS)) +// val type = it.getInt(it.getColumnIndexOrThrow(Telephony.Sms.TYPE)) +// +// listener?.invoke( +// DataEntity( +// timestamp, +// number, +// "SMS", +// type +// ) +// ) +// } +// } +// cursor?.close() +// cursor = context.contentResolver.query( +// Telephony.Mms.CONTENT_URI, +// arrayOf( +// Telephony.Mms._ID, // MMS ID +// Telephony.Mms.DATE, // Timestamp +// Telephony.Mms.MESSAGE_BOX // Type (Incoming/Outgoing) +// ), +// "${Telephony.Mms.DATE} BETWEEN ? AND ?", +// arrayOf(from.toString(), current.toString()), +// Telephony.Mms.DATE + " DESC" +// ) +// cursor?.use { +// while (it.moveToNext()) { +// val number = it.getLong(it.getColumnIndexOrThrow(Telephony.Mms._ID)) +// val timestamp = it.getLong(it.getColumnIndexOrThrow(Telephony.Mms.DATE)) * 1000 // MMS date is in seconds +// val type = it.getInt(it.getColumnIndexOrThrow(Telephony.Mms.MESSAGE_BOX)) +// +// +// listener?.invoke( +// DataEntity( +// timestamp, +// getMmsAddress(context.contentResolver, number) ?: "UNKNOWN", +// "MMS", +// type +// ) +// ) +// } +// } +// cursor?.close() +// } +// +// fun getMmsAddress(contentResolver: ContentResolver, mmsId: Long): String? { +// // Build the URI for the MMS address table using the MMS ID +// val uri = Uri.parse("content://mms/$mmsId/addr") +// val projection = arrayOf(Telephony.Mms.Addr.ADDRESS) +// +// val cursor: Cursor? = contentResolver.query(uri, projection, null,null, null) +// +// cursor?.use { +// if (it.moveToFirst()) { +// return it.getString(it.getColumnIndexOrThrow(Telephony.Mms.Addr.ADDRESS)) +// } +// } +// return null +// } +// +// override fun start() { +// trigger.register() +// } +// +// override fun stop() { +// trigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/NotificationCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/NotificationCollector.kt index 435a39a..404a00f 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/NotificationCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/NotificationCollector.kt @@ -1,112 +1,112 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.app.PendingIntent -import android.app.usage.UsageEvents -import android.app.usage.UsageStatsManager -import android.content.Context -import android.content.Intent -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager -import android.net.wifi.WifiManager -import android.os.Build -import android.service.notification.NotificationListenerService -import android.service.notification.StatusBarNotification -import android.util.Log -import androidx.core.app.NotificationManagerCompat -import androidx.transition.Visibility -import com.google.android.gms.location.ActivityRecognition -import com.google.android.gms.location.ActivityRecognitionClient -import com.google.android.gms.location.ActivityTransition -import com.google.android.gms.location.ActivityTransitionRequest -import com.google.android.gms.location.ActivityTransitionResult -import com.google.android.gms.location.DetectedActivity -import kaist.iclab.tracker.triggers.AlarmTrigger -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger -import java.util.concurrent.TimeUnit - -class NotificationCollector( - override val context: Context -) : AbstractCollector(context) { - - data class DataEntity( - val timestamp: Long, - val packageName: String, - val eventType: String, - val title: String, - val text: String, - val visibility: Int, - val category: String - ) : AbstractCollector.DataEntity() - - override val permissions = listOfNotNull( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE else null - ).toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - override fun isAvailable(): Boolean = - context.packageName in NotificationManagerCompat.getEnabledListenerPackages(context) - - object NotificationTrigger: NotificationListenerService() { - - var instance: NotificationTrigger? = null - var isListening = false - var listener: ((DataEntity) -> Unit)? = null - fun startListening() { - isListening = true - } - - fun stopListening() { - isListening = false - } - - override fun onCreate() { - super.onCreate() - instance = this - } - - override fun onDestroy() { - super.onDestroy() - instance = null - } - - override fun onNotificationPosted(sbn: StatusBarNotification) { - handleNotf(sbn, "POSTED") - } - - override fun onNotificationRemoved(sbn: StatusBarNotification) { - handleNotf(sbn, "REMOVED") - } - - private fun handleNotf(sbn: StatusBarNotification, eventType: String) { - if (isListening) { - listener?.invoke( - DataEntity( - System.currentTimeMillis(), - sbn.packageName, - eventType, - sbn.notification.extras.getString("android.title") ?: "", - sbn.notification.extras.getString("android.text") ?: "", - sbn.notification.visibility, - sbn.notification.category - ) - ) - } - } - } - - - override fun start() { - NotificationTrigger.instance?.let { - it.listener = listener - it.startListening() - } - } - - override fun stop() { - NotificationTrigger.instance?.stopListening() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.app.PendingIntent +//import android.app.usage.UsageEvents +//import android.app.usage.UsageStatsManager +//import android.content.Context +//import android.content.Intent +//import android.hardware.Sensor +//import android.hardware.SensorEvent +//import android.hardware.SensorEventListener +//import android.hardware.SensorManager +//import android.net.wifi.WifiManager +//import android.os.Build +//import android.service.notification.NotificationListenerService +//import android.service.notification.StatusBarNotification +//import android.util.Log +//import androidx.core.app.NotificationManagerCompat +//import androidx.transition.Visibility +//import com.google.android.gms.location.ActivityRecognition +//import com.google.android.gms.location.ActivityRecognitionClient +//import com.google.android.gms.location.ActivityTransition +//import com.google.android.gms.location.ActivityTransitionRequest +//import com.google.android.gms.location.ActivityTransitionResult +//import com.google.android.gms.location.DetectedActivity +//import kaist.iclab.tracker.triggers.AlarmTrigger +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +//import java.util.concurrent.TimeUnit +// +//class NotificationCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// data class DataEntity( +// val timestamp: Long, +// val packageName: String, +// val eventType: String, +// val title: String, +// val text: String, +// val visibility: Int, +// val category: String +// ) : AbstractCollector.DataEntity() +// +// override val permissions = listOfNotNull( +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE else null +// ).toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// override fun isAvailable(): Boolean = +// context.packageName in NotificationManagerCompat.getEnabledListenerPackages(context) +// +// object NotificationTrigger: NotificationListenerService() { +// +// var instance: NotificationTrigger? = null +// var isListening = false +// var listener: ((DataEntity) -> Unit)? = null +// fun startListening() { +// isListening = true +// } +// +// fun stopListening() { +// isListening = false +// } +// +// override fun onCreate() { +// super.onCreate() +// instance = this +// } +// +// override fun onDestroy() { +// super.onDestroy() +// instance = null +// } +// +// override fun onNotificationPosted(sbn: StatusBarNotification) { +// handleNotf(sbn, "POSTED") +// } +// +// override fun onNotificationRemoved(sbn: StatusBarNotification) { +// handleNotf(sbn, "REMOVED") +// } +// +// private fun handleNotf(sbn: StatusBarNotification, eventType: String) { +// if (isListening) { +// listener?.invoke( +// DataEntity( +// System.currentTimeMillis(), +// sbn.packageName, +// eventType, +// sbn.notification.extras.getString("android.title") ?: "", +// sbn.notification.extras.getString("android.text") ?: "", +// sbn.notification.visibility, +// sbn.notification.category +// ) +// ) +// } +// } +// } +// +// +// override fun start() { +// NotificationTrigger.instance?.let { +// it.listener = listener +// it.startListening() +// } +// } +// +// override fun stop() { +// NotificationTrigger.instance?.stopListening() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ScreenCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ScreenCollector.kt index 6f323d6..016aaa0 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ScreenCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/ScreenCollector.kt @@ -1,46 +1,46 @@ -package kaist.iclab.tracker.collectors - -import android.content.Context -import android.content.Intent -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger - -class ScreenCollector( - override val context: Context -) : AbstractCollector(context) { - - data class DataEntity( - val timestamp: Long, - val type: String, - ) : AbstractCollector.DataEntity() - - override val permissions = listOfNotNull().toTypedArray() - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - override fun isAvailable(): Boolean = true - - - private val broadcastTrigger = SystemBroadcastTrigger( - context, - arrayOf( - Intent.ACTION_SCREEN_ON, - Intent.ACTION_SCREEN_OFF, - Intent.ACTION_USER_PRESENT - ) - ) { intent -> - listener?.invoke( - DataEntity( - System.currentTimeMillis(), - intent.action ?: "UNKNOWN" - ) - ) - } - - - override fun start() { - broadcastTrigger.register() - } - - override fun stop() { - broadcastTrigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.content.Context +//import android.content.Intent +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +// +//class ScreenCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// data class DataEntity( +// val timestamp: Long, +// val type: String, +// ) : AbstractCollector.DataEntity() +// +// override val permissions = listOfNotNull().toTypedArray() +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// override fun isAvailable(): Boolean = true +// +// +// private val broadcastTrigger = SystemBroadcastTrigger( +// context, +// arrayOf( +// Intent.ACTION_SCREEN_ON, +// Intent.ACTION_SCREEN_OFF, +// Intent.ACTION_USER_PRESENT +// ) +// ) { intent -> +// listener?.invoke( +// DataEntity( +// System.currentTimeMillis(), +// intent.action ?: "UNKNOWN" +// ) +// ) +// } +// +// +// override fun start() { +// broadcastTrigger.register() +// } +// +// override fun stop() { +// broadcastTrigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/UserInteractionCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/UserInteractionCollector.kt index d11e08f..e9a9776 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/UserInteractionCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/UserInteractionCollector.kt @@ -1,73 +1,73 @@ -package kaist.iclab.tracker.collectors - -import android.accessibilityservice.AccessibilityService -import android.content.Context -import android.net.TrafficStats -import android.view.KeyEvent -import android.view.accessibility.AccessibilityEvent -import kaist.iclab.tracker.collectors.LocationCollector.Config -import kaist.iclab.tracker.triggers.AlarmTrigger -import kotlinx.coroutines.sync.Mutex -import java.util.concurrent.TimeUnit - -class UserInteractionCollector( - override val context: Context -) : AbstractCollector(context) { - - - data class DataEntity( - val timestamp: Long, - val packageName: String, - val className: String, - val eventType: Int, - val text: String - ) : AbstractCollector.DataEntity() - - - override val permissions = listOfNotNull().toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - - override fun isAvailable(): Boolean = true - - object MyAccessibilityService : AccessibilityService() { - var listener: ((DataEntity) -> Unit)? = null - var isListening = false - override fun onAccessibilityEvent(event: AccessibilityEvent?) { - if (isListening) { - event?.let { - listener?.invoke( - DataEntity( - System.currentTimeMillis(), - event.packageName.toString(), - event.className.toString(), - event.eventType, - event.text.toString() - ) - ) - } - } - } - - override fun onInterrupt() {} - - fun startListening() { - isListening = true - } - - fun stopListening() { - isListening = false - } - - } - - override fun start() { - MyAccessibilityService.listener = listener - MyAccessibilityService.startListening() - } - - override fun stop() { - MyAccessibilityService.stopListening() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.accessibilityservice.AccessibilityService +//import android.content.Context +//import android.net.TrafficStats +//import android.view.KeyEvent +//import android.view.accessibility.AccessibilityEvent +//import kaist.iclab.tracker.collectors.LocationCollector.Config +//import kaist.iclab.tracker.triggers.AlarmTrigger +//import kotlinx.coroutines.sync.Mutex +//import java.util.concurrent.TimeUnit +// +//class UserInteractionCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +// +// data class DataEntity( +// val timestamp: Long, +// val packageName: String, +// val className: String, +// val eventType: Int, +// val text: String +// ) : AbstractCollector.DataEntity() +// +// +// override val permissions = listOfNotNull().toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// +// override fun isAvailable(): Boolean = true +// +// object MyAccessibilityService : AccessibilityService() { +// var listener: ((DataEntity) -> Unit)? = null +// var isListening = false +// override fun onAccessibilityEvent(event: AccessibilityEvent?) { +// if (isListening) { +// event?.let { +// listener?.invoke( +// DataEntity( +// System.currentTimeMillis(), +// event.packageName.toString(), +// event.className.toString(), +// event.eventType, +// event.text.toString() +// ) +// ) +// } +// } +// } +// +// override fun onInterrupt() {} +// +// fun startListening() { +// isListening = true +// } +// +// fun stopListening() { +// isListening = false +// } +// +// } +// +// override fun start() { +// MyAccessibilityService.listener = listener +// MyAccessibilityService.startListening() +// } +// +// override fun stop() { +// MyAccessibilityService.stopListening() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/WiFiScanCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/WiFiScanCollector.kt index 3183c3c..defbd2c 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/collectors/WiFiScanCollector.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/collectors/WiFiScanCollector.kt @@ -1,67 +1,67 @@ -package kaist.iclab.tracker.collectors - -import android.Manifest -import android.content.Context -import android.net.wifi.WifiManager -import android.os.Build -import kaist.iclab.tracker.triggers.SystemBroadcastTrigger - -class WiFiScanCollector( - override val context: Context -) : AbstractCollector(context) { - -// No Configuration! - - data class DataEntity( - val timestamp: Long, - val bssid: String, - val frequency: Int, - val level: Int - ) : AbstractCollector.DataEntity() - - override val permissions = listOfNotNull( - Manifest.permission.ACCESS_WIFI_STATE, - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else null - ).toTypedArray() - - override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() - - - override fun isAvailable(): Boolean { - return wifiManager.isWifiEnabled - } - - private val wifiManager: WifiManager by lazy { - context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager - } - - private val broadcastTrigger = SystemBroadcastTrigger( - context, - arrayOf( - WifiManager.SCAN_RESULTS_AVAILABLE_ACTION - ) - ) { - val results = wifiManager.scanResults - val timestamp = System.currentTimeMillis() - results.forEach { - listener?.invoke( - DataEntity( - timestamp, - it.BSSID, - it.frequency, - it.level, - ) - ) - } - } - - override fun start() { - broadcastTrigger.register() - } - - override fun stop() { - broadcastTrigger.unregister() - } -} \ No newline at end of file +//package kaist.iclab.tracker.collectors +// +//import android.Manifest +//import android.content.Context +//import android.net.wifi.WifiManager +//import android.os.Build +//import kaist.iclab.tracker.triggers.SystemBroadcastTrigger +// +//class WiFiScanCollector( +// override val context: Context +//) : AbstractCollector(context) { +// +//// No Configuration! +// +// data class DataEntity( +// val timestamp: Long, +// val bssid: String, +// val frequency: Int, +// val level: Int +// ) : AbstractCollector.DataEntity() +// +// override val permissions = listOfNotNull( +// Manifest.permission.ACCESS_WIFI_STATE, +// Manifest.permission.ACCESS_COARSE_LOCATION, +// Manifest.permission.ACCESS_FINE_LOCATION, +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else null +// ).toTypedArray() +// +// override val foregroundServiceTypes: Array = listOfNotNull().toTypedArray() +// +// +// override fun isAvailable(): Boolean { +// return wifiManager.isWifiEnabled +// } +// +// private val wifiManager: WifiManager by lazy { +// context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager +// } +// +// private val broadcastTrigger = SystemBroadcastTrigger( +// context, +// arrayOf( +// WifiManager.SCAN_RESULTS_AVAILABLE_ACTION +// ) +// ) { +// val results = wifiManager.scanResults +// val timestamp = System.currentTimeMillis() +// results.forEach { +// listener?.invoke( +// DataEntity( +// timestamp, +// it.BSSID, +// it.frequency, +// it.level, +// ) +// ) +// } +// } +// +// override fun start() { +// broadcastTrigger.register() +// } +// +// override fun stop() { +// broadcastTrigger.unregister() +// } +//} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/AbstractCollector.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/AbstractCollector.kt new file mode 100644 index 0000000..ba3710d --- /dev/null +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/AbstractCollector.kt @@ -0,0 +1,87 @@ +package kaist.iclab.tracker.controller + +import android.util.Log +import kaist.iclab.tracker.permission.PermissionManagerInterface +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +abstract class AbstractCollector< + T: CollectorConfig, + K: DataEntity>( + val permissionManager: PermissionManagerInterface +): CollectorInterface { + init { + initState() + initConfig() + } + + protected val TAG: String = this::class.simpleName ?: "UnnamedClass" + override val NAME: String = extractName(this::class.simpleName ?: "UnknownCollector") + + abstract val defaultConfig: T + private val _configFlow + get() = MutableStateFlow(defaultConfig) + override val configFlow: StateFlow + get() = _configFlow.asStateFlow() + + protected fun initConfig(){ + _configFlow.tryEmit(defaultConfig) + } + + @Suppress("UNCHECKED_CAST") + override fun updateConfig(config: CollectorConfig) { + if(_stateFlow.value.flag == CollectorState.FLAG.RUNNING) throw IllegalStateException("Cannot update config while running.") + try{ + _configFlow.tryEmit(config as T) + } catch (e: ClassCastException) { + Log.e(TAG, "Invalid config type: ${config::class.simpleName}") + } + + } + override fun resetConfig() { + if(_stateFlow.value.flag == CollectorState.FLAG.RUNNING) throw IllegalStateException("Cannot update config while running.") + _configFlow.tryEmit(defaultConfig) + } + + + + protected val _stateFlow = MutableStateFlow(CollectorState(CollectorState.FLAG.UNAVAILABLE, "Not initialized")) + override val stateFlow: StateFlow + get() = _stateFlow.asStateFlow() + protected fun initState() { + val availability = isAvailable() + if(!availability.status) _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.UNAVAILABLE, availability.reason)) + else if(!permissionManager.isPermissionsGranted(permissions)) _stateFlow.tryEmit( + CollectorState(CollectorState.FLAG.PERMISSION_REQUIRED)) + else _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.ENABLED)) + } + + /* Check whether the system allow to collect data + * In case of sensor malfunction or broken, it would not be available.*/ + abstract fun isAvailable(): Availability + + /* Request required permissions to collect data */ + override fun requestPermissions(onResult: ((Boolean) -> Unit)) { + permissionManager.request(permissions) { + val granted = permissions.all { permission -> it[permission] == true } + Log.d(TAG, "Permission granted: $granted") + if(granted) _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.ENABLED)) + else _stateFlow.tryEmit(CollectorState(CollectorState.FLAG.PERMISSION_REQUIRED, "Permission denied")) + onResult(granted) + } + } + + override var listener: ((DataEntity) -> Unit)? = null + private fun extractName(className: String): String { + // Replace "Collector" with an empty string + val tmp = className.replace("Collector", "") + + // Split the name into parts based on camel case and underscores + val parts = + tmp.split("(?=\\p{Upper})|_|(?<=\\p{Lower})(?=\\p{Upper})".toRegex()) + + // Join the parts and convert to uppercase + return parts.joinToString("_").uppercase() + } +} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/Availability.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/Availability.kt new file mode 100644 index 0000000..835ed79 --- /dev/null +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/Availability.kt @@ -0,0 +1,6 @@ +package kaist.iclab.tracker.controller + +data class Availability( + val status: Boolean, + val reason: String? +) \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorConfig.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorConfig.kt new file mode 100644 index 0000000..f5834b1 --- /dev/null +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorConfig.kt @@ -0,0 +1,3 @@ +package kaist.iclab.tracker.controller + +open class CollectorConfig \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerImpl.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerImpl.kt index 78408b6..0ef9a49 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerImpl.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerImpl.kt @@ -8,31 +8,59 @@ import android.os.Build import android.os.IBinder import android.util.Log import kaist.iclab.tracker.Tracker -import kaist.iclab.tracker.collectors.AbstractCollector import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.combineTransform +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge class CollectorControllerImpl( - private val context: Context + private val context: Context, + override val collectorMap: Map ) : CollectorControllerInterface { - private val serviceIntent = Intent(context, CollectorService::class.java) - override val collectors = mutableListOf() - private val _stateFlow = MutableSharedFlow(replay = 1) + private val _stateFlow = MutableStateFlow(false) + override val stateFlow: StateFlow + get() = _stateFlow.asStateFlow() + + override val collectorStateFlow = combine(collectorMap.map { (key, collector) -> + collector.stateFlow.map { state -> + key to state + } + }) { pairs -> pairs.toMap() } - override fun add(collector: AbstractCollector) { - collectors.add(collector) + override fun enableCollector(name: String) { + collectorMap[name]?.let { collector-> + collector.requestPermissions() { + if (it) { collector.enable() } + } + } } - override fun remove(collector: AbstractCollector) { - collectors.remove(collector) + override fun disableCollector(name: String) { + collectorMap[name]?.disable() } - override fun isRunningFlow(): Flow = _stateFlow.asSharedFlow() - override fun updateState(isRunning: Boolean) { - _stateFlow.tryEmit(isRunning) + override val configFlow = combine(collectorMap.map { (key, collector) -> + collector.configFlow.map { config -> + key to config + } + }) { pairs -> pairs.toMap() } + + override fun updateConfig(config: Map) { + collectorMap.forEach { (name, collector) -> + config[name]?.let { + collector.updateConfig(it) + } + } } + private val serviceIntent = Intent(context, CollectorService::class.java) + override fun start() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent) @@ -45,10 +73,10 @@ class CollectorControllerImpl( context.stopService(serviceIntent) } - class CollectorService: Service() { - val controller = Tracker.getCollectorController() + class CollectorService : Service() { + val controller = Tracker.getCollectorController() as CollectorControllerImpl val notfManager = Tracker.getNotfManager() - val collectors = controller.collectors + val collectorMap = controller.collectorMap override fun onBind(intent: Intent?): IBinder? = null override fun onDestroy() { stop() @@ -59,15 +87,15 @@ class CollectorControllerImpl( this, requiredForegroundServiceType() ) - controller.updateState(true) - collectors.forEach { collector -> + controller._stateFlow.tryEmit(true) + collectorMap.forEach { (_, collector) -> collector.start() } } fun stop() { - controller.updateState(false) - collectors.forEach { collector -> + controller._stateFlow.tryEmit(false) + collectorMap.forEach { (_, collector) -> collector.stop() } stopSelf() @@ -90,8 +118,8 @@ class CollectorControllerImpl( fun requiredForegroundServiceType(): Int { val serviceTypes = mutableSetOf() - collectors.forEach { - serviceTypes.addAll(it.foregroundServiceTypes) + collectorMap.forEach { (_, collector) -> + serviceTypes.addAll(collector.foregroundServiceTypes) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { serviceTypes.add(ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) @@ -103,39 +131,4 @@ class CollectorControllerImpl( } } } - -// object NotificationHandler { -// val SERVICE_CHANNEL_ID = "TRACKER_SERVICE" -// val SERVICE_CHANNEL_NUMBER = 1 -// -// val NOTF_TITLE = "Tracker Service" -// val NOTF_DESCRIPTION = "Tracker is running now" -// -// fun createServiceNotfChannel(context: Context) { -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { -// val channel = NotificationChannel( -// SERVICE_CHANNEL_ID, -// NOTF_TITLE, -// NotificationManager.IMPORTANCE_DEFAULT -// ).apply { -// description = NOTF_DESCRIPTION -// } -// // Register the channel with the system -// val notificationManager: NotificationManager = -// context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager -// notificationManager.createNotificationChannel(channel) -// } -// } -// -// fun createServiceNotf(service: Service): Notification { -// Log.d("NOTIFICATION_SERVICE", "Creating Notification") -// val builder = NotificationCompat.Builder(service, SERVICE_CHANNEL_ID) -// return builder -// .setSmallIcon(android.R.drawable.ic_dialog_info) -// .setContentTitle(NOTF_TITLE) -// .setContentText(NOTF_DESCRIPTION) -// .setOngoing(true) -// .build() -// } -// } } \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerInterface.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerInterface.kt index 6097c92..5c0cace 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerInterface.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorControllerInterface.kt @@ -1,19 +1,21 @@ package kaist.iclab.tracker.controller -import kaist.iclab.tracker.collectors.AbstractCollector -import kaist.iclab.tracker.permission.PermissionManagerInterface import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow interface CollectorControllerInterface { - val collectors: MutableList - - fun add(collector: AbstractCollector) - fun remove(collector: AbstractCollector) + val collectorMap: Map + val stateFlow: StateFlow fun start() fun stop() - fun isRunningFlow(): Flow - fun updateState(isRunning: Boolean) + + val collectorStateFlow: Flow> + + fun enableCollector(name: String) + fun disableCollector(name: String) + + + val configFlow : Flow> + fun updateConfig(config: Map) } \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorInterface.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorInterface.kt new file mode 100644 index 0000000..e0c21cc --- /dev/null +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorInterface.kt @@ -0,0 +1,30 @@ +package kaist.iclab.tracker.controller + +import kotlinx.coroutines.flow.StateFlow + +interface CollectorInterface { + val NAME: String + + val permissions: Array + val foregroundServiceTypes: Array + + val configFlow: StateFlow + fun updateConfig(config: CollectorConfig) + fun resetConfig() + + val stateFlow: StateFlow + + /* DISABLED => READY */ + fun enable() + /* READY => DISABLED */ + fun disable() + /* Request permission to collect data: PERMISSION_REQUIRED => READY */ + fun requestPermissions(onResult: ((Boolean) -> Unit)) + /* Start collector to collect data: READY => RUNNING */ + fun start() + /* Stop collector to stop collecting data: RUNNING => READY */ + fun stop() + + /* Based on the data, define action */ + var listener: ((DataEntity) -> Unit)? +} \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorState.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorState.kt new file mode 100644 index 0000000..ab9cb65 --- /dev/null +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/CollectorState.kt @@ -0,0 +1,14 @@ +package kaist.iclab.tracker.controller + +data class CollectorState( + val flag: FLAG, + val message: String? = null +) { + enum class FLAG { + UNAVAILABLE, // The collector is not available (e.g., sensor not found) + PERMISSION_REQUIRED, // The collector requires permission to collect data + DISABLED, // The collector is not ready for collection (e.g., user not turned on it) + ENABLED, // The collector is enabled for collection, but not running + RUNNING // The collector is running + } +} diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/controller/DataEntity.kt b/tracker-library/src/main/java/kaist/iclab/tracker/controller/DataEntity.kt new file mode 100644 index 0000000..1fec8a7 --- /dev/null +++ b/tracker-library/src/main/java/kaist/iclab/tracker/controller/DataEntity.kt @@ -0,0 +1,5 @@ +package kaist.iclab.tracker.controller + +open class DataEntity( + open val received: Long +) \ No newline at end of file diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerImpl.kt b/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerImpl.kt index e1b6574..20b1156 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerImpl.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerImpl.kt @@ -56,6 +56,10 @@ class PermissionManagerImpl( ) == PackageManager.PERMISSION_GRANTED } + override fun isPermissionsGranted(permissions: Array): Boolean { + return permissions.all { isPermissionGranted(it) } + } + /* * Request multiple permissions at once: * It will control all about rationale, priority order of permission diff --git a/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerInterface.kt b/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerInterface.kt index 0b57c3b..85395c9 100644 --- a/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerInterface.kt +++ b/tracker-library/src/main/java/kaist/iclab/tracker/permission/PermissionManagerInterface.kt @@ -4,6 +4,7 @@ interface PermissionManagerInterface { var onPermissionResult: PermissionResultCallback? fun attach(activity: PermissionActivity) fun isPermissionGranted(permission: String): Boolean + fun isPermissionsGranted(permissions: Array): Boolean fun request( permissions: Array, onResult: ((permissionResult: PermissionResult) -> Unit)? = null diff --git a/wearable/src/main/java/kaist/iclab/lab_galaxywatch_tracker/data/collector/WearableSensorCollector.kt b/wearable/src/main/java/kaist/iclab/lab_galaxywatch_tracker/data/collector/WearableSensorCollector.kt index 5da26d2..882d1f1 100644 --- a/wearable/src/main/java/kaist/iclab/lab_galaxywatch_tracker/data/collector/WearableSensorCollector.kt +++ b/wearable/src/main/java/kaist/iclab/lab_galaxywatch_tracker/data/collector/WearableSensorCollector.kt @@ -4,7 +4,7 @@ import android.Manifest import android.content.Context import android.os.Build import com.samsung.android.service.health.tracking.HealthTracker -import kaist.iclab.tracker.collectors.AbstractCollector +import kaist.iclab.tracker.controller.AbstractCollector import kaist.iclab.tracker.database.DatabaseInterface import kaist.iclab.tracker.permission.PermissionManagerInterface import kaist.iclab.lab_galaxywatch_tracker.data.source.HealthTrackerSource