diff --git a/app/src/androidTest/kotlin/info/appdev/chartexample/StartTest.kt b/app/src/androidTest/kotlin/info/appdev/chartexample/StartTest.kt
index 2c127e96d..e43d6e245 100644
--- a/app/src/androidTest/kotlin/info/appdev/chartexample/StartTest.kt
+++ b/app/src/androidTest/kotlin/info/appdev/chartexample/StartTest.kt
@@ -28,6 +28,7 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import info.appdev.chartexample.compose.HorizontalBarComposeActivity
import info.appdev.chartexample.compose.HorizontalBarFullComposeActivity
import info.appdev.chartexample.compose.MultiLineComposeActivity
+import info.appdev.chartexample.compose.TimeIntervalComposeActivity
import info.appdev.chartexample.fragments.ViewPagerSimpleChartDemo
import info.appdev.chartexample.notimportant.ContentItem
import info.appdev.chartexample.notimportant.DemoBase
@@ -42,6 +43,7 @@ import org.junit.Test
import org.junit.rules.TestName
import org.junit.runner.RunWith
import timber.log.Timber
+import kotlin.jvm.java
@RunWith(AndroidJUnit4::class)
@@ -294,6 +296,7 @@ class StartTest {
contentItem.clazz == LineChartTimeActivity::class.java ||
contentItem.clazz == HorizontalBarComposeActivity::class.java ||
contentItem.clazz == HorizontalBarFullComposeActivity::class.java ||
+ contentItem.clazz == TimeIntervalComposeActivity::class.java ||
contentItem.clazz == MultiLineComposeActivity::class.java ||
contentItem.clazz == GradientActivity::class.java ||
// contentItem.clazz == TimeIntervalChartActivity::class.java ||
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a8f2a241b..a6acf71d3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -61,6 +61,7 @@
+
diff --git a/app/src/main/kotlin/info/appdev/chartexample/compose/TimeIntervalComposeActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/compose/TimeIntervalComposeActivity.kt
new file mode 100644
index 000000000..d6f619cb3
--- /dev/null
+++ b/app/src/main/kotlin/info/appdev/chartexample/compose/TimeIntervalComposeActivity.kt
@@ -0,0 +1,184 @@
+package info.appdev.chartexample.compose
+
+import android.graphics.Color
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.unit.dp
+import info.appdev.chartexample.notimportant.DemoBaseCompose
+import info.appdev.charting.compose.GanttChart
+import info.appdev.charting.data.GanttChartData
+import info.appdev.charting.data.GanttTask
+
+/**
+ * Demo activity showing Gantt-style timeline visualization using Compose.
+ */
+class TimeIntervalComposeActivity : DemoBaseCompose() {
+
+ // Full list of sample tasks
+ private val allTasks = listOf(
+ GanttTask("Design", 0f, 50f, Color.rgb(255, 107, 107)),
+ GanttTask("Dev", 40f, 100f, Color.rgb(66, 165, 245)),
+ GanttTask("Testing", 120f, 40f, Color.rgb(76, 175, 80), hatched = true),
+ GanttTask("Launch", 150f, 20f, Color.rgb(255, 193, 7)),
+ )
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MaterialTheme {
+ GanttChartScreen(onViewGithub = { viewGithub() })
+ }
+ }
+ }
+
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Composable
+ fun GanttChartScreen(onViewGithub: () -> Unit) {
+ var showMenu by remember { mutableStateOf(false) }
+ // X: controls visible max time (range 50–300)
+ var seekBarXValue by remember { mutableFloatStateOf(190f) }
+ // Y: controls how many tasks are shown (1–allTasks.size)
+ var seekBarYValue by remember { mutableFloatStateOf(allTasks.size.toFloat()) }
+
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text("TimeIntervalChart") },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ titleContentColor = MaterialTheme.colorScheme.onPrimary
+ ),
+ actions = {
+ Box {
+ IconButton(
+ onClick = { showMenu = true },
+ modifier = Modifier.testTag("menuButton")
+ ) {
+ Icon(
+ Icons.Default.MoreVert,
+ contentDescription = "Menu",
+ tint = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+ DropdownMenu(
+ expanded = showMenu,
+ onDismissRequest = { showMenu = false },
+ modifier = Modifier.testTag("dropdownMenu")
+ ) {
+ DropdownMenuItem(
+ text = { Text("View on GitHub") },
+ onClick = { showMenu = false; onViewGithub() },
+ modifier = Modifier.testTag("menuItem_View on GitHub")
+ )
+ }
+ }
+ }
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ .background(androidx.compose.ui.graphics.Color.White)
+ ) {
+ val ganttData = remember(seekBarXValue, seekBarYValue) {
+ val maxTime = seekBarXValue
+ val taskCount = seekBarYValue.toInt().coerceAtLeast(1)
+ GanttChartData().apply {
+ allTasks.take(taskCount).forEach { addTask(it) }
+ minTime = 0f
+ this.maxTime = maxTime
+ }
+ }
+
+ GanttChart(
+ data = ganttData,
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+ )
+
+ // SeekBar X – controls visible time range
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "X:",
+ modifier = Modifier.padding(end = 8.dp),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Slider(
+ value = seekBarXValue,
+ onValueChange = { seekBarXValue = it },
+ valueRange = 1f..200f,
+ modifier = Modifier.weight(1f)
+ )
+ Text(
+ text = seekBarXValue.toInt().toString(),
+ modifier = Modifier.padding(start = 8.dp),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+
+ // SeekBar Y – controls number of tasks shown
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "Y:",
+ modifier = Modifier.padding(end = 8.dp),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Slider(
+ value = seekBarYValue,
+ onValueChange = { seekBarYValue = it },
+ valueRange = 1f..allTasks.size.toFloat(),
+ steps = allTasks.size - 2,
+ modifier = Modifier.weight(1f)
+ )
+ Text(
+ text = seekBarYValue.toInt().toString(),
+ modifier = Modifier.padding(start = 8.dp),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/info/appdev/chartexample/notimportant/MainActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/notimportant/MainActivity.kt
index ccae0d21c..85de01258 100644
--- a/app/src/main/kotlin/info/appdev/chartexample/notimportant/MainActivity.kt
+++ b/app/src/main/kotlin/info/appdev/chartexample/notimportant/MainActivity.kt
@@ -80,6 +80,7 @@ import info.appdev.chartexample.TimeLineActivity
import info.appdev.chartexample.compose.HorizontalBarComposeActivity
import info.appdev.chartexample.compose.HorizontalBarFullComposeActivity
import info.appdev.chartexample.compose.MultiLineComposeActivity
+import info.appdev.chartexample.compose.TimeIntervalComposeActivity
import info.appdev.chartexample.fragments.ViewPagerSimpleChartDemo
import info.appdev.charting.utils.Utils
import info.appdev.charting.utils.initUtils
@@ -217,10 +218,13 @@ class MainActivity : ComponentActivity() {
add(ComposeItem("HorizontalFullCompose", "Render bar chart horizontally full compose.", HorizontalBarFullComposeActivity::class.java).toDemoBase())
add(ComposeItem("MultiLineCompose", "Show multiple data sets in compose.", MultiLineComposeActivity::class.java).toDemoBase())
+ add(ContentItem("Timeinterval"))
+ add(ContentItem("Timeinterval", "Grantt chart", TimeIntervalChartActivity::class.java))
+ add(ComposeItem("Timeinterval compose", "Grantt chart compose", TimeIntervalComposeActivity::class.java).toDemoBase())
+
add(ContentItem("Demonstrate and fix issues"))
add(ContentItem("Gradient", "Show a gradient edge case", GradientActivity::class.java))
add(ContentItem("Timeline", "Show a time line with Unix timestamp", TimeLineActivity::class.java))
- add(ContentItem("Timeinterval", "Grantt chart", TimeIntervalChartActivity::class.java))
}
}
}
diff --git a/chartLibCompose/src/main/kotlin/info/appdev/charting/compose/GanttChartComposable.kt b/chartLibCompose/src/main/kotlin/info/appdev/charting/compose/GanttChartComposable.kt
new file mode 100644
index 000000000..00b2f622c
--- /dev/null
+++ b/chartLibCompose/src/main/kotlin/info/appdev/charting/compose/GanttChartComposable.kt
@@ -0,0 +1,35 @@
+package info.appdev.charting.compose
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.viewinterop.AndroidView
+import info.appdev.charting.charts.GanttChart
+import info.appdev.charting.data.GanttChartData
+
+/**
+ * A Composable wrapper for [GanttChart].
+ *
+ * @param data The Gantt chart data to display
+ * @param modifier The modifier to be applied to the chart
+ */
+@Composable
+fun GanttChart(
+ data: GanttChartData?,
+ modifier: Modifier = Modifier,
+) {
+ val context = LocalContext.current
+
+ val chart = remember { GanttChart(context) }
+
+ AndroidView(
+ factory = { chart },
+ modifier = modifier.fillMaxSize(),
+ update = { ganttChart ->
+ ganttChart.setData(data)
+ }
+ )
+}
+
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-1SampleClick.png b/screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-1SampleClick.png
similarity index 100%
rename from screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-1SampleClick.png
rename to screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-1SampleClick.png
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-click.png b/screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-click.png
similarity index 100%
rename from screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-click.png
rename to screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-click.png
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-click2020.png b/screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-click2020.png
similarity index 100%
rename from screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-click2020.png
rename to screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-click2020.png
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-click7070.png b/screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-click7070.png
similarity index 100%
rename from screenshotsToCompare9/StartTest_smokeTestStart-46-TimeIntervalChartActivity-Timeinterval-click7070.png
rename to screenshotsToCompare9/StartTest_smokeTestStart-44-TimeIntervalChartActivity-Timeinterval-click7070.png
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-45-TimeIntervalComposeActivity-Timeintervalcompose-1SampleClick.png b/screenshotsToCompare9/StartTest_smokeTestStart-45-TimeIntervalComposeActivity-Timeintervalcompose-1SampleClick.png
new file mode 100644
index 000000000..b1d1ddacc
Binary files /dev/null and b/screenshotsToCompare9/StartTest_smokeTestStart-45-TimeIntervalComposeActivity-Timeintervalcompose-1SampleClick.png differ
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-44-GradientActivity-Gradient-1SampleClick.png b/screenshotsToCompare9/StartTest_smokeTestStart-47-GradientActivity-Gradient-1SampleClick.png
similarity index 100%
rename from screenshotsToCompare9/StartTest_smokeTestStart-44-GradientActivity-Gradient-1SampleClick.png
rename to screenshotsToCompare9/StartTest_smokeTestStart-47-GradientActivity-Gradient-1SampleClick.png
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-45-TimeLineActivity-Timeline-1SampleClick.png b/screenshotsToCompare9/StartTest_smokeTestStart-48-TimeLineActivity-Timeline-1SampleClick.png
similarity index 100%
rename from screenshotsToCompare9/StartTest_smokeTestStart-45-TimeLineActivity-Timeline-1SampleClick.png
rename to screenshotsToCompare9/StartTest_smokeTestStart-48-TimeLineActivity-Timeline-1SampleClick.png