feat: Complete analytics admin UI panel
CI / build (push) Waiting to run
CI / lint (push) Waiting to run

- Analytics toggle (enable/disable)
- Event count display
- Export JSON button (copies to clipboard)
- Export CSV button (copies to clipboard)
- Clear data button with confirmation dialog
- Persist analytics enabled state in DataStore
This commit is contained in:
2026-02-03 23:06:21 +00:00
parent 76531d33b0
commit 2978250086
2 changed files with 130 additions and 0 deletions
@@ -1,14 +1,21 @@
package farm.hobbs.kioskbrowser package farm.hobbs.kioskbrowser
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import farm.hobbs.kioskbrowser.analytics.AnalyticsManager import farm.hobbs.kioskbrowser.analytics.AnalyticsManager
import farm.hobbs.kioskbrowser.databinding.ActivityAdminBinding import farm.hobbs.kioskbrowser.databinding.ActivityAdminBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import android.text.InputType import android.text.InputType
import android.widget.EditText import android.widget.EditText
@@ -69,6 +76,7 @@ class AdminActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
loadCurrentSettings() loadCurrentSettings()
setupAnalyticsUI()
binding.btnSave.setOnClickListener { binding.btnSave.setOnClickListener {
saveSettings() saveSettings()
@@ -83,6 +91,69 @@ class AdminActivity : AppCompatActivity() {
finishAffinity() finishAffinity()
} }
} }
private fun setupAnalyticsUI() {
// Load current analytics state
updateAnalyticsStats()
// Analytics toggle
binding.switchAnalytics.isChecked = analytics.enabled
binding.switchAnalytics.setOnCheckedChangeListener { _, isChecked ->
analytics.enabled = isChecked
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit { prefs ->
prefs[booleanPreferencesKey("analytics_enabled")] = isChecked
}
}
}
// Export JSON
binding.btnExportJson.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
val json = analytics.exportToJson()
copyToClipboard("Analytics JSON", json)
Toast.makeText(this@AdminActivity, "JSON copied to clipboard", Toast.LENGTH_SHORT).show()
}
}
// Export CSV
binding.btnExportCsv.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
val csv = analytics.exportToCsv()
copyToClipboard("Analytics CSV", csv)
Toast.makeText(this@AdminActivity, "CSV copied to clipboard", Toast.LENGTH_SHORT).show()
}
}
// Clear analytics
binding.btnClearAnalytics.setOnClickListener {
AlertDialog.Builder(this)
.setTitle("Clear Analytics")
.setMessage("Delete all analytics data? This cannot be undone.")
.setPositiveButton("Delete") { _, _ ->
CoroutineScope(Dispatchers.Main).launch {
val count = analytics.clearAll()
Toast.makeText(this@AdminActivity, "Deleted $count events", Toast.LENGTH_SHORT).show()
updateAnalyticsStats()
}
}
.setNegativeButton("Cancel", null)
.show()
}
}
private fun updateAnalyticsStats() {
CoroutineScope(Dispatchers.Main).launch {
val count = analytics.getEventCount()
binding.tvAnalyticsStats.text = "Events: $count"
}
}
private fun copyToClipboard(label: String, text: String) {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(label, text)
clipboard.setPrimaryClip(clip)
}
private fun loadCurrentSettings() { private fun loadCurrentSettings() {
runBlocking { runBlocking {
@@ -69,6 +69,65 @@
android:inputType="numberPassword" /> android:inputType="numberPassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<!-- Usage Analytics Section -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Usage Analytics"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchAnalytics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Enable Analytics"
android:checked="true"
android:layout_marginBottom="8dp" />
<TextView
android:id="@+id/tvAnalyticsStats"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Events: 0"
android:textColor="#666666"
android:layout_marginBottom="12dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<Button
android:id="@+id/btnExportJson"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="4dp"
android:text="Export JSON"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
<Button
android:id="@+id/btnExportCsv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="4dp"
android:text="Export CSV"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
</LinearLayout>
<Button
android:id="@+id/btnClearAnalytics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clear Analytics Data"
android:backgroundTint="#FF5722"
android:layout_marginBottom="24dp" />
<!-- Digital Signage Section --> <!-- Digital Signage Section -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"