From 5f1bdf02358fa42f131f0ea84d2320828b75810f Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:10:53 +0530 Subject: [PATCH 01/32] Entities are defined for storing application logs --- .../android/storage/entity/NetworkStatus.kt | 5 +++++ .../storage/entity/NetworkStatusLog.kt | 16 ++++++++++++++ .../android/storage/entity/SourceStatusLog.kt | 21 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatus.kt create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatusLog.kt create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/entity/SourceStatusLog.kt diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatus.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatus.kt new file mode 100644 index 00000000..b6c11c23 --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatus.kt @@ -0,0 +1,5 @@ +package org.radarbase.android.storage.entity + +enum class NetworkStatus { + DISCONNECTED, CONNECTED_WIFI, CONNECTED_CELLULAR +} \ No newline at end of file diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatusLog.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatusLog.kt new file mode 100644 index 00000000..0cfa83db --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/NetworkStatusLog.kt @@ -0,0 +1,16 @@ +package org.radarbase.android.storage.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "network_status_log", + indices = [Index(value = ["time"])] +) +data class NetworkStatusLog( + @PrimaryKey(autoGenerate = true) val id: Long, + val time: Long, + @ColumnInfo("connection_state") val connectionState: NetworkStatus +) \ No newline at end of file diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/SourceStatusLog.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/SourceStatusLog.kt new file mode 100644 index 00000000..fb39b5b6 --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/entity/SourceStatusLog.kt @@ -0,0 +1,21 @@ +package org.radarbase.android.storage.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import org.radarbase.android.source.SourceStatusListener + +@Entity( + tableName = "source_status_log", + indices = [ + Index(value = ["time"]), + Index(value = ["plugin"]) + ] +) +data class SourceStatusLog( + @PrimaryKey(autoGenerate = true) val id: Long, + val time: Long, + val plugin: String, + @ColumnInfo("source_status") val sourceStatus: SourceStatusListener.Status +) \ No newline at end of file From 613a21e9a90f8081bf5d67d4529e015cb5047689 Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:11:57 +0530 Subject: [PATCH 02/32] Created data access objects for query access --- .../radarbase/android/storage/dao/BaseDao.kt | 63 ++++++++++ .../android/storage/dao/NetworkStatusDao.kt | 102 +++++++++++++++ .../android/storage/dao/SourceStatusDao.kt | 119 ++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/dao/BaseDao.kt create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/dao/NetworkStatusDao.kt create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/dao/SourceStatusDao.kt diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/BaseDao.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/BaseDao.kt new file mode 100644 index 00000000..c08afeff --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/BaseDao.kt @@ -0,0 +1,63 @@ +package org.radarbase.android.storage.dao + +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Update + +/** + * A generic base Data Access Object (DAO) interface providing standard CRUD operations. + * + * This interface defines common methods for adding, updating, and deleting entities of type [T]. + * Implementing DAOs can extend this interface to inherit these operations. + * + * @param T the type of entity for which the operations are provided. + */ +@Suppress("unused") +interface BaseDao { + + /** + * Inserts the given [data] into the database. + * + * If a conflict occurs (e.g. a record with the same primary key already exists), + * the existing record will be replaced. + * + * @param data the entity to be inserted. + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(data: T) + + /** + * Inserts the given [rows] into the database. + * + * If a conflict occurs, the existing records will be replaced. + * + * @param rows a variable number of entities to be inserted. + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(vararg rows: T) + + /** + * Updates the given [data] in the database. + * + * @param data the entity with updated values. + */ + @Update + fun update(data: T) + + /** + * Deletes the specified [row] from the database. + * + * @param row the entity to be deleted. + */ + @Delete + fun delete(row: T) + + /** + * Deletes the specified [rows] from the database. + * + * @param rows a variable number of entities to be deleted. + */ + @Delete + fun deleteAll(vararg rows: T) +} diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/NetworkStatusDao.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/NetworkStatusDao.kt new file mode 100644 index 00000000..69894469 --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/NetworkStatusDao.kt @@ -0,0 +1,102 @@ +package org.radarbase.android.storage.dao + +import androidx.paging.PagingSource +import androidx.room.Dao +import androidx.room.Query +import org.radarbase.android.storage.entity.NetworkStatusLog +import org.radarbase.android.storage.entity.SourceStatusLog + +/** + * Data Access Object (DAO) for performing operations on the [NetworkStatusLog] table. + * + * This DAO extends [BaseDao] to provide standard CRUD operations and adds methods + * specific to managing network status logs, such as retrieving logs within a time range, + * fetching logs older than a given timestamp, and deleting logs based on time or count constraints. + */ +@Suppress("unused") +@Dao +abstract class NetworkStatusDao : BaseDao { + + /** + * Retrieves all network status log records from the database. + * + * @return a list of all [NetworkStatusLog] entries. + */ + @Query("SELECT * FROM network_status_log") + abstract suspend fun loadAllNetworkLogs(): List + + /** + * Retrieves network status log records with a [NetworkStatusLog.time] value between the specified [from] and [to] timestamps. + * + * @param from the start timestamp (inclusive). + * @param to the end timestamp (inclusive). + * @return a list of [NetworkStatusLog] entries within the specified time range. + */ + @Query("SELECT * FROM network_status_log WHERE time BETWEEN :from AND :to") + abstract suspend fun loadNetworkLogsBetweenTimestamps( + from: Long, + to: Long + ): List + + /** + * Retrieves network status log records with a [NetworkStatusLog.time] value less than the specified [time]. + * + * This method is useful for fetching logs that are older than the given timestamp, + * for example, when you want to delete logs older than 7 days. + * + * @param time the timestamp threshold, logs with a time less than this value are considered old. + * @return a list of [NetworkStatusLog] entries with a [time] value less than the specified threshold. + */ + @Query("SELECT * FROM network_status_log WHERE time < :time") + abstract suspend fun loadNetworkLogsOlderThan(time: Long): List + + /** + * Returns a [PagingSource] for loading [NetworkStatusLog] records in pages. + * + * The [PagingSource] loads data in chunks (pages) as needed when the user scrolls, + * rather than loading all records at once. The records are sorted by [NetworkStatusLog.time] in descending order. + * + * @return a [PagingSource] that loads pages of [SourceStatusLog] items. + */ + @Query("SELECT * FROM network_status_log ORDER BY time ASC") + abstract fun pagingSource(): PagingSource + + /** + * Deletes network status log records with a [NetworkStatusLog.time] value between the specified [from] and [to] timestamps. + * + * @param from the start timestamp (inclusive) for deletion. + * @param to the end timestamp (inclusive) for deletion. + */ + @Query("DELETE FROM network_status_log WHERE time BETWEEN :from AND :to") + abstract suspend fun deleteNetworkLogsBetweenTimestamps(from: Long, to: Long) + + /** + * Deletes the network status log record with the specified [id]. + * + * @param id the unique identifier of the network log to delete. + */ + @Query("DELETE FROM network_status_log WHERE id = :id") + abstract suspend fun deleteNetworkLogById(id: Long) + + /** + * Deletes the oldest network status log records so that the total number of records does not exceed [count]. + * + * Calculates how many records are in excess by subtracting [count] from the total number + * of records in the table. It then deletes that many oldest records. + * + * For example, if the table has 100 records and [count] is set to 80, then the 20 oldest records will be deleted. + * + * @param count the maximum number of recent records to retain. + */ + @Query( + """ + DELETE FROM network_status_log + WHERE id IN ( + SELECT id FROM network_status_log + ORDER BY time ASC + LIMIT (SELECT COUNT(*) - :count FROM network_status_log) + ) + """ + ) + abstract suspend fun deleteNetworkLogsCountGreaterThan(count: Long) +} \ No newline at end of file diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/SourceStatusDao.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/SourceStatusDao.kt new file mode 100644 index 00000000..c5d1473d --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/dao/SourceStatusDao.kt @@ -0,0 +1,119 @@ +package org.radarbase.android.storage.dao + +import androidx.paging.PagingSource +import androidx.room.Dao +import androidx.room.Query +import org.radarbase.android.storage.entity.SourceStatusLog + +/** + * Data Access Object (DAO) for performing operations on the [SourceStatusLog] table. + * + * This DAO extends [BaseDao] to provide basic CRUD operations and adds additional queries + * specific to [SourceStatusLog] such as fetching records within a time range and deleting + * records based on specific criteria. + */ +@Suppress("unused") +@Dao +abstract class SourceStatusDao : BaseDao { + + /** + * Retrieves all the source status log records from the database. + * + * @return a list of all [SourceStatusLog] entries. + */ + @Query("SELECT * FROM source_status_log") + abstract suspend fun loadAllStatuses(): List + + /** + * Retrieves source status log records that have a [SourceStatusLog.time] value between the given [from] and [to] timestamps. + * + * @param from the start timestamp (inclusive). + * @param to the end timestamp (inclusive). + * @return a list of [SourceStatusLog] entries that fall within the specified time range. + */ + @Query("SELECT * FROM source_status_log WHERE time BETWEEN :from AND :to") + abstract suspend fun loadStatusesBetweenTimestamps(from: Long, to: Long): List + + /** + * Retrieves records with a [SourceStatusLog.time] value less than the specified [time]. + * + * This can be used to fetch records that are older than the given timestamp, + * for instance, to delete logs older than 7 days. + * + * @param time the timestamp threshold, logs with a time less than this value are considered old. + * @return a list of [SourceStatusLog] entries with a [time] value less than the specified threshold. + */ + @Query("SELECT * FROM source_status_log WHERE time < :time") + abstract suspend fun loadStatusesOlderThan(time: Long): List + + /** + * Retrieves a list of distinct plugin. + * + * @return a list of unique plugin names. + */ + @Query("SELECT DISTINCT plugin FROM source_status_log") + abstract suspend fun loadDistinctPlugins(): List + + /** + * Retrieves all [SourceStatusLog] records that match the specified plugin name. + * + * This query filters the records in the source_status_log table based on the value of the + * [SourceStatusLog.plugin]. + * + * @param pluginName the name of the plugin to filter by. + * @return a list of [SourceStatusLog] entries corresponding to the specified plugin. + */ + @Query("SELECT * FROM source_status_log WHERE plugin = :pluginName") + abstract suspend fun loadStatusesByPluginName(pluginName: String): List + + /** + * Returns a [PagingSource] for loading [SourceStatusLog] records in pages. + * + * The [PagingSource] loads data in chunks (pages) as needed when the user scrolls, + * rather than loading all records at once. The records are sorted by [SourceStatusLog.time] in descending order. + * + * @return a [PagingSource] that loads pages of [SourceStatusLog] items. + */ + @Query("SELECT * FROM source_status_log WHERE plugin = :pluginName ORDER BY time ASC") + abstract fun pagingSourceByPluginName(pluginName: String): PagingSource + + /** + * Deletes source status log records that have a [SourceStatusLog.time] value between the specified [from] and [to] timestamps. + * + * @param from the start timestamp (inclusive) for deletion. + * @param to the end timestamp (inclusive) for deletion. + */ + @Query("DELETE FROM source_status_log WHERE time BETWEEN :from AND :to") + abstract suspend fun deleteStatusesBetweenTimestamps(from: Long, to: Long) + + /** + * Deletes the source status log record with the specified [id]. + * + * @param id the unique identifier of the status log to delete. + */ + @Query("DELETE FROM source_status_log WHERE id = :id") + abstract suspend fun deleteStatusById(id: Long) + + /** + * Deletes the oldest source status log records to ensure that the total number of records does not exceed [count]. + * + * Calculates the number of records that exceed the retention count and then deletes that many + * oldest records based on the [SourceStatusLog.time]. + * + * For example: if there are 100 records and [count] is set to 80, then the 20 oldest records (based on [SourceStatusLog.time]) + * will be deleted. + * + * @param count the maximum number of recent records to retain. + */ + @Query( + """ + DELETE FROM source_status_log + WHERE id IN ( + SELECT id FROM source_status_log + ORDER BY time ASC + LIMIT (SELECT COUNT(*) - :count FROM source_status_log) + ) +""" + ) + abstract suspend fun deleteStatusesCountGreaterThan(count: Long) +} \ No newline at end of file From 2bde96b680f5d2cc0ad117ac69123805d887c854 Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:12:22 +0530 Subject: [PATCH 03/32] Added converters for storing enums --- .../android/storage/converter/Converters.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/converter/Converters.kt diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/converter/Converters.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/converter/Converters.kt new file mode 100644 index 00000000..3faa146c --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/converter/Converters.kt @@ -0,0 +1,22 @@ +package org.radarbase.android.storage.converter + +import androidx.room.TypeConverter +import org.radarbase.android.source.SourceStatusListener +import org.radarbase.android.storage.entity.NetworkStatus + +@Suppress("unused") +class Converters { + @TypeConverter + fun fromSourceStatus(status: SourceStatusListener.Status): String = status.name + + @TypeConverter + fun toSourceStatus(statusName: String): SourceStatusListener.Status = + SourceStatusListener.Status.valueOf(statusName) + + @TypeConverter + fun fromNetworkStatus(status: NetworkStatus): String = status.name + + @TypeConverter + fun toNetworkStatus(statusName: String): NetworkStatus = + NetworkStatus.valueOf(statusName) +} From 4182fdbd3d26bb1f8151bc76ceda5c2c4214f908 Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:12:49 +0530 Subject: [PATCH 04/32] Created a room database subclass --- .../storage/db/RadarApplicationDatabase.kt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 radar-commons-android/src/main/java/org/radarbase/android/storage/db/RadarApplicationDatabase.kt diff --git a/radar-commons-android/src/main/java/org/radarbase/android/storage/db/RadarApplicationDatabase.kt b/radar-commons-android/src/main/java/org/radarbase/android/storage/db/RadarApplicationDatabase.kt new file mode 100644 index 00000000..6883434d --- /dev/null +++ b/radar-commons-android/src/main/java/org/radarbase/android/storage/db/RadarApplicationDatabase.kt @@ -0,0 +1,81 @@ +package org.radarbase.android.storage.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import org.radarbase.android.storage.converter.Converters +import org.radarbase.android.storage.dao.NetworkStatusDao +import org.radarbase.android.storage.dao.SourceStatusDao +import org.radarbase.android.storage.entity.NetworkStatusLog +import org.radarbase.android.storage.entity.SourceStatusLog + +/** + * Database for the Radar Application. + * + * This database manages two entities: + * - [SourceStatusLog]: Logs related to source status. + * - [NetworkStatusLog]: Logs related to network connection status. + * + * Access to these entities is provided via the [sourceStatusDao] and [networkStatusDao] methods. + * + * ## Migrations + * + * The database currently uses destructive migration ([RoomDatabase::fallbackToDestructiveMigration]). + * For version 1 this is acceptable, but in the future proper migration paths should be defined + * to preserve user data. Automated migrations are supported when [Database.exportSchema] is set to true. + * + * @see Migrating Room Databases + */ +@Database( + version = 1, + entities = [SourceStatusLog::class, NetworkStatusLog::class], + exportSchema = true +) +@TypeConverters(Converters::class) +@Suppress("unused") +abstract class RadarApplicationDatabase : RoomDatabase() { + + /** + * Provides access to the DAO for source status log operations. + * + * @return an instance of [SourceStatusDao]. + */ + abstract fun sourceStatusDao(): SourceStatusDao + + /** + * Provides access to the DAO for network status log operations. + * + * @return an instance of [NetworkStatusDao]. + */ + abstract fun networkStatusDao(): NetworkStatusDao + + companion object { + @Volatile + private var INSTANCE: RadarApplicationDatabase? = null + + /** + * Retrieves the singleton instance of [RadarApplicationDatabase]. + * + * The database is built using a destructive migration strategy ([RoomDatabase::fallbackToDestructiveMigration]). + * In the future, migration paths can be added with [RoomDatabase::addMigrations] to preserve user data. + * + * @param context the application context. + * @return the singleton instance of [RadarApplicationDatabase]. + */ + fun getInstance(context: Context): RadarApplicationDatabase { + return INSTANCE ?: synchronized(this) { + INSTANCE ?: Room.databaseBuilder( + context.applicationContext, + RadarApplicationDatabase::class.java, + "radar_app_db" + ) + // Using fallbackToDestructiveMigration() for now since this is version 1. + // In the future, add proper migration paths using .addMigrations(MIGRATION_1_2, ...) + .fallbackToDestructiveMigration() + .build().also { INSTANCE = it } + } + } + } +} From 588871e5586473e396abdb9508ce2e0a6e3d93e4 Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:14:42 +0530 Subject: [PATCH 05/32] Action defined for starting ApplicationMetricsActivity for application plugin --- .../monitor/application/ApplicationStatusProvider.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ApplicationStatusProvider.kt b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ApplicationStatusProvider.kt index 4febe454..85040212 100644 --- a/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ApplicationStatusProvider.kt +++ b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ApplicationStatusProvider.kt @@ -16,9 +16,11 @@ package org.radarbase.monitor.application +import android.content.Intent import org.radarbase.android.BuildConfig import org.radarbase.android.RadarService import org.radarbase.android.source.SourceProvider +import org.radarbase.monitor.application.ui.ApplicationMetricsActivity open class ApplicationStatusProvider(radarService: RadarService) : SourceProvider(radarService) { override val description: String? @@ -46,4 +48,9 @@ open class ApplicationStatusProvider(radarService: RadarService) : SourceProvide override val sourceModel: String = "pRMT" override val version: String = BuildConfig.VERSION_NAME + + override val actions: List + get() = listOf(Action(radarService.getString(R.string.start_app_metrics_activity)){ + startActivity(Intent(this, ApplicationMetricsActivity::class.java)) + }) } From fdd47052883de6bc939dfe35762caac8aa4ebe7b Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:15:58 +0530 Subject: [PATCH 06/32] Created activity for showing application metrics --- .../ui/ApplicationMetricsActivity.kt | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/ApplicationMetricsActivity.kt diff --git a/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/ApplicationMetricsActivity.kt b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/ApplicationMetricsActivity.kt new file mode 100644 index 00000000..a5c4e445 --- /dev/null +++ b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/ApplicationMetricsActivity.kt @@ -0,0 +1,116 @@ +package org.radarbase.monitor.application.ui + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.radarbase.android.storage.db.RadarApplicationDatabase +import org.radarbase.monitor.application.R +import org.radarbase.monitor.application.databinding.ActivityApplicationStatusBinding +import org.radarbase.monitor.application.ui.adapter.NetworkStatusPagingAdapter +import org.radarbase.monitor.application.ui.adapter.SourceStatusPagingAdapter +import org.radarbase.monitor.application.ui.adapter.StringAdapter +import org.radarbase.monitor.application.ui.viewmodel.ApplicationMetricsViewModel +import org.radarbase.monitor.application.ui.viewmodel.factory.ApplicationMetricsViewModelFactory +import org.radarbase.monitor.application.utils.AppRepository +import org.radarbase.monitor.application.utils.LogNamesHolder + +class ApplicationMetricsActivity : AppCompatActivity() { + private lateinit var binding: ActivityApplicationStatusBinding + private lateinit var database: RadarApplicationDatabase + private lateinit var repository: AppRepository + private val viewModel: ApplicationMetricsViewModel by viewModels { ApplicationMetricsViewModelFactory(repository) } + + + private var sourceAdapter: SourceStatusPagingAdapter? = null + private var networkAdapter: NetworkStatusPagingAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + binding = ActivityApplicationStatusBinding.inflate(layoutInflater) + setContentView(binding.root) + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.clAppPlugin)) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + insets + } + database = RadarApplicationDatabase.getInstance(applicationContext) + repository = AppRepository(database) + + setUpScrollView() + } + + private fun setUpScrollView() { + binding.rvAppPlugin.layoutManager = LinearLayoutManager(this) + + binding.rvAppPlugin.adapter = StringAdapter( + this, listOf( + LogNamesHolder.APPLICATION_PLUGIN_STATUS.alias, + LogNamesHolder.APPLICATION_NETWORK_STATUS.alias + ) + ) { topicClicked -> + when (topicClicked) { + LogNamesHolder.APPLICATION_PLUGIN_STATUS.alias -> { + lifecycleScope.launch { + val plugins = withContext(Dispatchers.IO) { + viewModel.loadDistinctPluginsFromSourceLogs() + } + binding.rvAppPlugin.adapter = StringAdapter( + this@ApplicationMetricsActivity, + plugins + ) { pluginName -> + if (sourceAdapter == null) { + sourceAdapter = SourceStatusPagingAdapter( + this@ApplicationMetricsActivity, + ) { + // Take no action when clicked on source status logs + } + networkAdapter = null + binding.rvAppPlugin.adapter = sourceAdapter + + lifecycleScope.launch { + viewModel.getSourceStatusForPagingData(pluginName) + .collectLatest { pagingData -> + sourceAdapter?.submitData(pagingData) + } + } + } + } + } + } + + LogNamesHolder.APPLICATION_NETWORK_STATUS.alias -> { + networkAdapter = NetworkStatusPagingAdapter( + this@ApplicationMetricsActivity, + ) { + // No action should be taken when clicked on network status logs + } + + sourceAdapter = null + binding.rvAppPlugin.adapter = networkAdapter + + lifecycleScope.launch { + viewModel.networkStatusPagingData.collectLatest { pagingData -> + if (binding.rvAppPlugin.adapter == networkAdapter) { + networkAdapter?.submitData(pagingData) + } + } + } + } + + else -> { + // Do Nothing + } + } + } + } +} \ No newline at end of file From dbdca2746ea6a0d0d0ed44debdbda0fef4424c28 Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:16:45 +0530 Subject: [PATCH 07/32] Created recyclerview adapter for showing string list --- .../application/ui/adapter/StringAdapter.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/StringAdapter.kt diff --git a/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/StringAdapter.kt b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/StringAdapter.kt new file mode 100644 index 00000000..d99af48d --- /dev/null +++ b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/StringAdapter.kt @@ -0,0 +1,41 @@ +package org.radarbase.monitor.application.ui.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.radarbase.monitor.application.databinding.ClickableStringRowBinding + +class StringAdapter( + private val context: Context, + private val contents: List, + private val itemClickAction: (String) -> Unit +) : RecyclerView.Adapter() { + + inner class StringViewHolder(val binding: ClickableStringRowBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bindView(item: String) { + binding.tvStringInfo.text = item + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StringViewHolder { + val viewBinding = ClickableStringRowBinding.inflate( + LayoutInflater.from(context), + parent, + false + ) + + return StringViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: StringViewHolder, position: Int) { + val content = contents[position] + holder.bindView(content) + holder.binding.clStringItem.setOnClickListener { + content.apply(itemClickAction) + } + } + + override fun getItemCount(): Int = contents.size +} \ No newline at end of file From 1bf36dd17226ea811da6c789d823ae0acc9aa86c Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:17:30 +0530 Subject: [PATCH 08/32] Implemented paging-data-adapters for showing network and plugin logs --- .../ui/adapter/NetworkStatusPagingAdapter.kt | 63 +++++++++++++++++++ .../ui/adapter/SourceStatusPagingAdapter.kt | 63 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/NetworkStatusPagingAdapter.kt create mode 100644 plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/SourceStatusPagingAdapter.kt diff --git a/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/NetworkStatusPagingAdapter.kt b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/NetworkStatusPagingAdapter.kt new file mode 100644 index 00000000..e341df55 --- /dev/null +++ b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/NetworkStatusPagingAdapter.kt @@ -0,0 +1,63 @@ +package org.radarbase.monitor.application.ui.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.paging.PagingDataAdapter +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import org.radarbase.android.storage.entity.NetworkStatusLog +import org.radarbase.monitor.application.R +import org.radarbase.monitor.application.databinding.ItemLogRowBinding +import org.radarbase.monitor.application.utils.dateTimeFromInstant + +class NetworkStatusPagingAdapter( + private val context: Context, + private val onClickAction: (NetworkStatusLog) -> Unit +) : PagingDataAdapter( + DIFF_CALLBACK +) { + + inner class NetworkStatusViewHolder(private val binding: ItemLogRowBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(log: NetworkStatusLog) { + binding.tvStatusInfo.text = context.getString( + R.string.network_status_info, + log.connectionState.name, + dateTimeFromInstant(log.time) + ) + + binding.root.setOnClickListener { + log.apply(onClickAction) + } + } + } + + override fun onBindViewHolder(holder: NetworkStatusViewHolder, position: Int) { + val log = getItem(position) ?: return + holder.bind(log) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NetworkStatusViewHolder { + val binding = ItemLogRowBinding.inflate(LayoutInflater.from(context), parent, false) + return NetworkStatusViewHolder(binding) + } + + companion object { + private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: NetworkStatusLog, + newItem: NetworkStatusLog + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: NetworkStatusLog, + newItem: NetworkStatusLog + ): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/SourceStatusPagingAdapter.kt b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/SourceStatusPagingAdapter.kt new file mode 100644 index 00000000..b11cf266 --- /dev/null +++ b/plugins/radar-android-application-status/src/main/java/org/radarbase/monitor/application/ui/adapter/SourceStatusPagingAdapter.kt @@ -0,0 +1,63 @@ +package org.radarbase.monitor.application.ui.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.paging.PagingDataAdapter +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import org.radarbase.android.storage.entity.SourceStatusLog +import org.radarbase.monitor.application.R +import org.radarbase.monitor.application.databinding.ItemLogRowBinding +import org.radarbase.monitor.application.utils.dateTimeFromInstant + +class SourceStatusPagingAdapter( + private val context: Context, + private val onClickAction: (SourceStatusLog) -> Unit +) : PagingDataAdapter( + DIFF_CALLBACK +) { + + inner class SourceStatusViewHolder(private val binding: ItemLogRowBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(log: SourceStatusLog) { + binding.tvStatusInfo.text = context.getString( + R.string.source_status_info, + log.sourceStatus.name, + dateTimeFromInstant(log.time) + ) + + binding.root.setOnClickListener { + log.apply(onClickAction) + } + } + } + + override fun onBindViewHolder(holder: SourceStatusViewHolder, position: Int) { + val log = getItem(position) ?: return + holder.bind(log) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SourceStatusViewHolder { + val binding = ItemLogRowBinding.inflate(LayoutInflater.from(context), parent, false) + return SourceStatusViewHolder(binding) + } + + companion object { + private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: SourceStatusLog, + newItem: SourceStatusLog + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: SourceStatusLog, + newItem: SourceStatusLog + ): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file From 181d6edcecab4b3665b59900cabfbfe0609a34ea Mon Sep 17 00:00:00 2001 From: this-Aditya Date: Mon, 17 Feb 2025 13:18:24 +0530 Subject: [PATCH 09/32] Layout files added for application metrics activity --- .../layout/activity_application_status.xml | 19 ++++++++ .../main/res/layout/clickable_string_row.xml | 39 +++++++++++++++ .../src/main/res/layout/item_log_row.xml | 48 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 plugins/radar-android-application-status/src/main/res/layout/activity_application_status.xml create mode 100644 plugins/radar-android-application-status/src/main/res/layout/clickable_string_row.xml create mode 100644 plugins/radar-android-application-status/src/main/res/layout/item_log_row.xml diff --git a/plugins/radar-android-application-status/src/main/res/layout/activity_application_status.xml b/plugins/radar-android-application-status/src/main/res/layout/activity_application_status.xml new file mode 100644 index 00000000..57b53017 --- /dev/null +++ b/plugins/radar-android-application-status/src/main/res/layout/activity_application_status.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/plugins/radar-android-application-status/src/main/res/layout/clickable_string_row.xml b/plugins/radar-android-application-status/src/main/res/layout/clickable_string_row.xml new file mode 100644 index 00000000..016dab07 --- /dev/null +++ b/plugins/radar-android-application-status/src/main/res/layout/clickable_string_row.xml @@ -0,0 +1,39 @@ + + + + + + + + + \ No newline at end of file diff --git a/plugins/radar-android-application-status/src/main/res/layout/item_log_row.xml b/plugins/radar-android-application-status/src/main/res/layout/item_log_row.xml new file mode 100644 index 00000000..5510ab1f --- /dev/null +++ b/plugins/radar-android-application-status/src/main/res/layout/item_log_row.xml @@ -0,0 +1,48 @@ + + + + + + + +