Skip to content

Commit a2e2652

Browse files
authored
Merge pull request #756 from AppDevNext/GanttChart
Gantt chart
2 parents 1ed654c + e34cf3e commit a2e2652

12 files changed

+467
-2
lines changed

app/src/androidTest/kotlin/info/appdev/chartexample/StartTest.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ class StartTest {
7575

7676
@After
7777
fun cleanUp() {
78-
Intents.release()
78+
// release() may have already been called by the last loop iteration
79+
try {
80+
Intents.release()
81+
} catch (_: IllegalStateException) { /* not initialised, nothing to do */ }
7982
// Clean up test timber tree
8083
Timber.uprootAll()
8184
}
@@ -249,12 +252,17 @@ class StartTest {
249252
if (!compose)
250253
doClickTest(index, contentClass, contentItem)
251254

252-
//Thread.sleep(100)
253255
Espresso.pressBack()
254256

255257
// Wait for MainActivity to be visible again
256258
composeTestRule.waitForIdle()
257259
Thread.sleep(200) // Small delay for back navigation
260+
261+
// Reset intent recording for next iteration; otherwise intents accumulate
262+
// across the loop and Intents.intended() can no longer find a fresh
263+
// unverified match for the current activity.
264+
Intents.release()
265+
Intents.init()
258266
} catch (e: Exception) {
259267
Timber.e("#$index/'${contentClass.simpleName}'->'$optionMenu' ${e.message}", e)
260268
onView(ViewMatchers.isRoot())
@@ -264,6 +272,16 @@ class StartTest {
264272
.replace(" ", "")
265273
)
266274
})
275+
// Navigate back so subsequent iterations start from MainActivity
276+
try {
277+
Espresso.pressBack()
278+
composeTestRule.waitForIdle()
279+
} catch (_: Exception) { /* already on MainActivity */ }
280+
// Reset intents so the next iteration starts clean
281+
try {
282+
Intents.release()
283+
Intents.init()
284+
} catch (_: Exception) { /* ignore if already released */ }
267285
}
268286
}
269287
}
@@ -278,6 +296,7 @@ class StartTest {
278296
contentItem.clazz == HorizontalBarFullComposeActivity::class.java ||
279297
contentItem.clazz == MultiLineComposeActivity::class.java ||
280298
contentItem.clazz == GradientActivity::class.java ||
299+
// contentItem.clazz == TimeIntervalChartActivity::class.java ||
281300
contentItem.clazz == TimeLineActivity::class.java
282301
) {
283302
// These charts have less clickable area, so skip further clicks

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<activity android:name="info.appdev.chartexample.HalfPieChartActivity" />
6161
<activity android:name="info.appdev.chartexample.SpecificPositionsLineChartActivity" />
6262
<activity android:name="info.appdev.chartexample.TimeLineActivity" />
63+
<activity android:name="info.appdev.chartexample.TimeIntervalChartActivity" />
6364
</application>
6465

6566
</manifest>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package info.appdev.chartexample
2+
3+
import android.graphics.Color
4+
import android.os.Bundle
5+
import info.appdev.chartexample.databinding.ActivityTimeIntervalChartBinding
6+
import info.appdev.chartexample.notimportant.DemoBase
7+
import info.appdev.charting.data.EntryFloat
8+
import info.appdev.charting.data.GanttChartData
9+
import info.appdev.charting.data.GanttTask
10+
import info.appdev.charting.highlight.Highlight
11+
import info.appdev.charting.listener.OnChartValueSelectedListener
12+
13+
/**
14+
* Demo activity showing Gantt-style timeline visualization.
15+
* Each horizontal bar represents a task with start time and duration.
16+
*/
17+
class TimeIntervalChartActivity : DemoBase(), OnChartValueSelectedListener {
18+
19+
private lateinit var binding: ActivityTimeIntervalChartBinding
20+
21+
override fun onCreate(savedInstanceState: Bundle?) {
22+
super.onCreate(savedInstanceState)
23+
binding = ActivityTimeIntervalChartBinding.inflate(layoutInflater)
24+
setContentView(binding.root)
25+
26+
// Create Gantt chart data
27+
val ganttData = GanttChartData()
28+
29+
// Add sample project tasks
30+
ganttData.addTask(GanttTask("Design", 0f, 50f, Color.rgb(255, 107, 107))) // Red: 0-50
31+
ganttData.addTask(GanttTask("Dev", 40f, 100f, Color.rgb(66, 165, 245))) // Blue: 40-140
32+
ganttData.addTask(GanttTask("Testing", 120f, 40f, Color.rgb(76, 175, 80), hatched = true)) // Green: 120-160
33+
ganttData.addTask(GanttTask("Launch", 150f, 20f, Color.rgb(255, 193, 7))) // Yellow: 150-170
34+
ganttData.minTime = 10f
35+
ganttData.maxTime = 200f
36+
// Set data and render
37+
binding.chart1.setData(ganttData)
38+
}
39+
40+
override fun saveToGallery() = Unit
41+
42+
override fun onValueSelected(entryFloat: EntryFloat, highlight: Highlight) = Unit
43+
44+
override fun onNothingSelected() = Unit
45+
46+
}

app/src/main/kotlin/info/appdev/chartexample/notimportant/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import info.appdev.chartexample.ScrollViewActivity
7575
import info.appdev.chartexample.SpecificPositionsLineChartActivity
7676
import info.appdev.chartexample.StackedBarActivity
7777
import info.appdev.chartexample.StackedBarActivityNegative
78+
import info.appdev.chartexample.TimeIntervalChartActivity
7879
import info.appdev.chartexample.TimeLineActivity
7980
import info.appdev.chartexample.compose.HorizontalBarComposeActivity
8081
import info.appdev.chartexample.compose.HorizontalBarFullComposeActivity
@@ -219,6 +220,7 @@ class MainActivity : ComponentActivity() {
219220
add(ContentItem("Demonstrate and fix issues"))
220221
add(ContentItem("Gradient", "Show a gradient edge case", GradientActivity::class.java))
221222
add(ContentItem("Timeline", "Show a time line with Unix timestamp", TimeLineActivity::class.java))
223+
add(ContentItem("Timeinterval", "Grantt chart", TimeIntervalChartActivity::class.java))
222224
}
223225
}
224226
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:layout_width="match_parent"
4+
android:layout_height="match_parent">
5+
6+
<info.appdev.charting.charts.GanttChart
7+
android:id="@+id/chart1"
8+
android:layout_width="match_parent"
9+
android:layout_height="match_parent"
10+
android:layout_above="@+id/seekBarX"/>
11+
12+
<SeekBar
13+
android:id="@+id/seekBarY"
14+
android:layout_width="match_parent"
15+
android:layout_height="wrap_content"
16+
android:layout_alignParentBottom="true"
17+
android:layout_alignParentLeft="true"
18+
android:layout_margin="8dp"
19+
android:layout_toLeftOf="@+id/tvYMax"
20+
android:layout_marginRight="5dp"
21+
android:max="100"
22+
android:paddingBottom="12dp" />
23+
24+
<SeekBar
25+
android:id="@+id/seekBarX"
26+
android:layout_width="match_parent"
27+
android:layout_height="wrap_content"
28+
android:layout_above="@+id/seekBarY"
29+
android:layout_margin="8dp"
30+
android:layout_marginBottom="35dp"
31+
android:layout_toLeftOf="@+id/tvXMax"
32+
android:layout_marginRight="5dp"
33+
android:max="100"
34+
android:paddingBottom="12dp" />
35+
36+
<TextView
37+
android:id="@+id/tvXMax"
38+
android:layout_width="50dp"
39+
android:layout_height="wrap_content"
40+
android:layout_alignBottom="@+id/seekBarX"
41+
android:layout_alignParentRight="true"
42+
android:text="@string/dash"
43+
android:layout_marginBottom="15dp"
44+
android:layout_marginRight="10dp"
45+
android:gravity="right"
46+
android:textAppearance="?android:attr/textAppearanceMedium" />
47+
48+
<TextView
49+
android:id="@+id/tvYMax"
50+
android:layout_width="50dp"
51+
android:layout_height="wrap_content"
52+
android:layout_alignBottom="@+id/seekBarY"
53+
android:layout_alignParentRight="true"
54+
android:text="@string/dash"
55+
android:layout_marginBottom="15dp"
56+
android:layout_marginRight="10dp"
57+
android:gravity="right"
58+
android:textAppearance="?android:attr/textAppearanceMedium" />
59+
60+
</RelativeLayout>

0 commit comments

Comments
 (0)