Skip to content

Commit ef5ff72

Browse files
authored
Merge pull request #111 from getditto/rr/tool-entry-point
Tools Entry Point
2 parents d6b9373 + 5f9d438 commit ef5ff72

File tree

22 files changed

+597
-172
lines changed

22 files changed

+597
-172
lines changed

DittoDiskUsage/src/main/java/live/ditto/dittodiskusage/usecase/GetDiskUsageMetrics.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,21 @@ import live.ditto.dittodiskusage.TOTAL_SIZE
99
import live.ditto.dittodiskusage.FIVE_HUNDRED_MEGABYTES_IN_BYTES
1010
import live.ditto.healthmetrics.HealthMetric
1111

12-
class GetDiskUsageMetrics() {
12+
class GetDiskUsageMetrics {
1313
val metricName: String = METRIC_NAME
1414
var unhealthySizeInBytes: Int = FIVE_HUNDRED_MEGABYTES_IN_BYTES
1515

1616
fun execute(currentState: DiskUsageState): HealthMetric {
1717

18-
val dittoStoreSize: Int = currentState.children.first { shortRelativePath(it.relativePath) == DITTO_STORE}.sizeInBytes
19-
val dittoReplicationSize: Int = currentState.children.first { shortRelativePath(it.relativePath) == DITTO_REPLICATION}.sizeInBytes
18+
val dittoStoreSize: Int =
19+
currentState.children.first { shortRelativePath(it.relativePath) == DITTO_STORE }.sizeInBytes
20+
val dittoReplicationSize: Int = currentState.children.firstOrNull {
21+
shortRelativePath(it.relativePath) == DITTO_REPLICATION }?.sizeInBytes ?: 0
2022

2123
val isHealthy = healthCheckSize(dittoStoreSize, dittoReplicationSize)
2224

2325
val details = mutableMapOf<String, String>().apply {
24-
for(child in currentState.children) {
26+
for (child in currentState.children) {
2527
this[shortRelativePath(child.relativePath)] = child.size
2628
}
2729
this[ROOT_PATH] = currentState.rootPath

DittoToolsViewer/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

DittoToolsViewer/build.gradle.kts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// libs. will show an IDE error. This is a bug with Android Studio/IntelliJ. This issue is
2+
// is tracked here: https://youtrack.jetbrains.com/issue/KTIJ-19369
3+
// Workaround is to suppress the error until the issue linked above is fixed
4+
@Suppress("DSL_SCOPE_VIOLATION")
5+
plugins {
6+
alias(libs.plugins.com.android.library)
7+
alias(libs.plugins.org.jetbrains.kotlin.android)
8+
}
9+
10+
extra["libraryGroupId"] = "live.ditto"
11+
extra["libraryArtifactId"] = "dittotoolsviewer"
12+
extra["libraryVersion"] = "1.0.0"
13+
14+
apply {
15+
from("${rootProject.projectDir}/gradle/deploy.gradle")
16+
from("${rootProject.projectDir}/gradle/android-common.gradle")
17+
}
18+
19+
android {
20+
namespace = "live.ditto.dittotoolsviewer"
21+
}
22+
23+
dependencies {
24+
25+
implementation(libs.core.ktx)
26+
implementation(libs.androidx.appcompat.appcompat)
27+
implementation(libs.material)
28+
29+
implementation(platform(libs.androidx.compose.composeBom))
30+
implementation(libs.androidx.compose.ui.ui)
31+
implementation(libs.androidx.compose.ui.uiToolingPreview)
32+
implementation(libs.androidx.navigation.navigationCompose)
33+
implementation(libs.androidx.compose.material3.material3)
34+
35+
implementation(libs.live.ditto.ditto)
36+
implementation(libs.live.ditto.databrowser)
37+
implementation(libs.live.ditto.exportlogs)
38+
implementation(libs.live.ditto.presenceviewer)
39+
implementation(libs.live.ditto.diskusage)
40+
implementation(libs.live.ditto.health)
41+
implementation(libs.live.ditto.heartbeat)
42+
implementation(libs.live.ditto.presencedegradationreporter)
43+
implementation(libs.live.ditto.healthmetrics)
44+
implementation(libs.live.ditto.exporter)
45+
46+
testImplementation(libs.junit.junit)
47+
48+
androidTestImplementation(libs.androidx.test.ext.junit)
49+
androidTestImplementation(libs.espresso.core)
50+
}

DittoToolsViewer/consumer-rules.pro

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
</manifest>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package live.ditto.dittotoolsviewer.presentation
2+
3+
import androidx.compose.foundation.layout.PaddingValues
4+
import androidx.compose.foundation.layout.padding
5+
import androidx.compose.material.icons.Icons
6+
import androidx.compose.material.icons.filled.Build
7+
import androidx.compose.material3.BottomAppBar
8+
import androidx.compose.material3.Button
9+
import androidx.compose.material3.ExtendedFloatingActionButton
10+
import androidx.compose.material3.Icon
11+
import androidx.compose.material3.Scaffold
12+
import androidx.compose.material3.Text
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.res.stringResource
16+
import androidx.compose.ui.tooling.preview.Preview
17+
import androidx.navigation.NavHostController
18+
import androidx.navigation.compose.NavHost
19+
import androidx.navigation.compose.composable
20+
import androidx.navigation.compose.rememberNavController
21+
import ditto.live.dittopresenceviewer.DittoPresenceViewer
22+
import live.ditto.Ditto
23+
import live.ditto.dittodatabrowser.DittoDataBrowser
24+
import live.ditto.dittodiskusage.DittoDiskUsage
25+
import live.ditto.dittoexportlogs.ExportLogs
26+
import live.ditto.dittotoolsviewer.R
27+
import live.ditto.dittotoolsviewer.presentation.navigation.Screens
28+
import live.ditto.dittotoolsviewer.presentation.viewmodel.ToolsViewerViewModel
29+
import live.ditto.health.HealthScreen
30+
import live.ditto.presencedegradationreporter.PresenceDegradationReporterScreen
31+
32+
/**
33+
* A Composable that you can include in your app that will give a single entry point for all Ditto
34+
* Tools.
35+
*
36+
* @param modifier an optional modifier if you need to adjust the layout to fit the view
37+
* @param ditto your instance of [Ditto] that is required
38+
* @param onExitTools an optional lambda function that will be called whenever a user taps the
39+
* "Exit Tools" button. Use this to do any back navigation or dismissal/hiding of the Tools Viewer
40+
*/
41+
@Composable
42+
fun DittoToolsViewer(
43+
modifier: Modifier = Modifier,
44+
ditto: Ditto,
45+
onExitTools: () -> Unit = { }
46+
) {
47+
DittoToolsViewerScaffold(
48+
modifier = modifier,
49+
ditto = ditto,
50+
onExitTools = onExitTools
51+
)
52+
}
53+
54+
@Composable
55+
private fun DittoToolsViewerScaffold(
56+
modifier: Modifier,
57+
ditto: Ditto,
58+
onExitTools: () -> Unit,
59+
viewModel: ToolsViewerViewModel = ToolsViewerViewModel()
60+
) {
61+
62+
val navController = rememberNavController()
63+
64+
Scaffold(
65+
modifier = modifier,
66+
bottomBar = {
67+
BottomAppBar(
68+
actions = {
69+
Button(onClick = { onExitTools() }) {
70+
Text(text = "Exit Tools")
71+
}
72+
},
73+
floatingActionButton = {
74+
MenuFloatingActionButton {
75+
if (navController.currentDestination?.route != Screens.MainScreen.route) {
76+
navController.popBackStack()
77+
}
78+
}
79+
}
80+
)
81+
}
82+
) { contentPadding ->
83+
ToolsViewerContent(
84+
navController = navController,
85+
viewModel = viewModel,
86+
contentPadding = contentPadding,
87+
ditto = ditto
88+
)
89+
}
90+
}
91+
92+
@Composable
93+
private fun ToolsViewerContent(
94+
navController: NavHostController,
95+
viewModel: ToolsViewerViewModel,
96+
contentPadding: PaddingValues,
97+
ditto: Ditto,
98+
) {
99+
ToolsViewerNavHost(
100+
navController = navController,
101+
contentPadding = contentPadding,
102+
ditto = ditto,
103+
toolMenuItems = viewModel.toolsMenuItems()
104+
)
105+
}
106+
107+
@Composable
108+
private fun ToolsViewerNavHost(
109+
navController: NavHostController,
110+
contentPadding: PaddingValues,
111+
ditto: Ditto,
112+
toolMenuItems: List<ToolMenuItem>
113+
) {
114+
NavHost(
115+
modifier = Modifier.padding(contentPadding),
116+
navController = navController,
117+
startDestination = Screens.MainScreen.route,
118+
) {
119+
composable(Screens.MainScreen.route) {
120+
ToolsMenu(
121+
navController = navController,
122+
menuItems = toolMenuItems,
123+
)
124+
}
125+
composable(Screens.PresenceViewerScreen.route) {
126+
DittoPresenceViewer(ditto = ditto)
127+
}
128+
composable(Screens.DataBrowserScreen.route) {
129+
DittoDataBrowser(ditto = ditto)
130+
}
131+
composable(Screens.ExportLogsScreen.route) {
132+
ExportLogs(
133+
onDismiss = {
134+
navController.popBackStack()
135+
}
136+
)
137+
}
138+
composable(Screens.DiskUsageScreen.route) {
139+
DittoDiskUsage(ditto = ditto)
140+
}
141+
composable(Screens.HealthScreen.route) {
142+
HealthScreen()
143+
}
144+
composable(Screens.HeartbeatScreen.route) {
145+
HeartbeatScreen(ditto = ditto)
146+
}
147+
composable(Screens.PresenceDegradationReporterScreen.route) {
148+
PresenceDegradationReporterScreen(ditto = ditto)
149+
}
150+
}
151+
}
152+
153+
@Composable
154+
private fun MenuFloatingActionButton(onClick: () -> Unit) {
155+
ExtendedFloatingActionButton(
156+
onClick = { onClick() },
157+
icon = { Icon(Icons.Filled.Build, stringResource(R.string.tools_menu_content_description)) },
158+
text = { Text(text = stringResource(R.string.tools_menu)) }
159+
)
160+
}
161+
162+
@Preview
163+
@Composable
164+
private fun MenuFloatingActionButtonPreview() {
165+
MenuFloatingActionButton(
166+
onClick = { }
167+
)
168+
}

app/src/main/java/live/ditto/dittotoolsapp/HeartbeatView.kt renamed to DittoToolsViewer/src/main/java/live/ditto/dittotoolsviewer/presentation/HeartbeatScreen.kt

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package live.ditto.dittotoolsapp
1+
package live.ditto.dittotoolsviewer.presentation
22

33
import android.os.Build
44
import androidx.annotation.RequiresApi
@@ -9,6 +9,7 @@ import androidx.compose.runtime.*
99
import androidx.compose.ui.Alignment
1010
import androidx.compose.ui.Modifier
1111
import androidx.compose.ui.graphics.Color
12+
import androidx.compose.ui.res.stringResource
1213
import androidx.compose.ui.unit.dp
1314
import kotlinx.coroutines.CoroutineScope
1415
import kotlinx.coroutines.Dispatchers
@@ -19,12 +20,13 @@ import live.ditto.healthmetrics.HealthMetricProvider
1920
import live.ditto.dittoheartbeat.DittoHeartbeatConfig
2021
import live.ditto.dittoheartbeat.DittoHeartbeatInfo
2122
import live.ditto.dittoheartbeat.startHeartbeat
23+
import live.ditto.dittotoolsviewer.R
2224
import java.util.*
2325

2426

2527
@RequiresApi(Build.VERSION_CODES.O)
2628
@Composable
27-
fun ShowHeartbeatData(ditto: Ditto) {
29+
fun HeartbeatScreen(ditto: Ditto) {
2830

2931
var heartbeatInfo by remember { mutableStateOf<DittoHeartbeatInfo?>(null) }
3032
val healthMetricProviders: MutableList<HealthMetricProvider> = mutableListOf()
@@ -85,7 +87,8 @@ fun HeartbeatInfoCard(heartbeatInfo: DittoHeartbeatInfo) {
8587
val connection = entry.value
8688
if (connection is Map<*, *>) { // Check if connection is a Map
8789
@Suppress("UNCHECKED_CAST")
88-
val typedConnection = connection as Map<String, Any> // Type cast connection to Map<String, Any>
90+
val typedConnection =
91+
connection as Map<String, Any> // Type cast connection to Map<String, Any>
8992
ConnectionInfo(connection = typedConnection)
9093
}
9194
}
@@ -97,23 +100,34 @@ fun HeartbeatInfoCard(heartbeatInfo: DittoHeartbeatInfo) {
97100
@Composable
98101
fun HeartbeatHeader(heartbeatInfo: DittoHeartbeatInfo) {
99102
Column {
100-
Text("ID: ${heartbeatInfo.id}")
101-
Text("SDK: ${heartbeatInfo.sdk}")
102-
Text("Last Updated: ${heartbeatInfo.lastUpdated}")
103-
Text("remotePeersCount: ${heartbeatInfo.presenceSnapshotDirectlyConnectedPeersCount}", color = Color.Black)
104-
Text("Peer key: ${heartbeatInfo.peerKey}")
103+
Text(stringResource(R.string.heartbeat_id_label, heartbeatInfo.id))
104+
Text(stringResource(R.string.heartbeat_sdk_label, heartbeatInfo.sdk))
105+
Text(stringResource(R.string.heartbeat_last_updated_label, heartbeatInfo.lastUpdated))
106+
Text(
107+
text = stringResource(
108+
R.string.heartbeat_remotepeerscount_label,
109+
heartbeatInfo.presenceSnapshotDirectlyConnectedPeersCount
110+
),
111+
color = Color.Black
112+
)
113+
Text(stringResource(R.string.heartbeat_peer_key_label, heartbeatInfo.peerKey))
105114
}
106115
}
107116

108117
@Composable
109118
fun ConnectionInfo(connection: Map<String, Any>) {
110119
Column {
111-
Text("\nConnection: ${connection["deviceName"]}")
112-
Text("SDK: ${connection["sdk"]}")
113-
Text(text = if (connection["isConnectedToDittoCloud"] as Boolean) "Online" else "Offline")
114-
Text("BT: ${connection["bluetooth"]}")
115-
Text("P2PWifi: ${connection["p2pWifi"]}")
116-
Text("LAN: ${connection["lan"]}")
120+
Text(stringResource(R.string.connection_info_connection, connection["deviceName"] ?: ""))
121+
Text(stringResource(R.string.connection_info_sdk, connection["sdk"] ?: ""))
122+
val isConnectedToDittoCloudString = if (connection["isConnectedToDittoCloud"] as Boolean) {
123+
stringResource(R.string.connection_info_online)
124+
} else stringResource(
125+
R.string.connection_info_offline
126+
)
127+
Text(isConnectedToDittoCloudString)
128+
Text(stringResource(R.string.connection_info_bt, connection["bluetooth"] ?: ""))
129+
Text(stringResource(R.string.connection_info_p2pwifi, connection["p2pWifi"] ?: ""))
130+
Text(stringResource(R.string.connection_info_lan, connection["lan"] ?: ""))
117131
}
118132
}
119133

0 commit comments

Comments
 (0)