feat: Complete analytics admin UI panel
- 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:
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user