Skip to content

Commit 00287e9

Browse files
Merge pull request #1349 from Kotlin/android_example
android example
2 parents f8c264e + 35cdcce commit 00287e9

39 files changed

+978
-0
lines changed

examples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
### Idea examples
44
* [plugin example](kotlin-dataframe-plugin-example) IDEA project with a
55
[Kotlin DataFrame Compiler Plugin](https://kotlin.github.io/dataframe/compiler-plugin.html) example.
6+
* [android example](android-example) A minimal Android project showcasing integration with Kotlin DataFrame.
7+
Also includes [Kotlin DataFrame Compiler Plugin](https://kotlin.github.io/dataframe/compiler-plugin.html).
68
* [movies](idea-examples/movies) Using extension properties [Access API](https://kotlin.github.io/dataframe/apilevels.html) to perform a data cleaning task
79
* [titanic](idea-examples/titanic)
810
* [youtube](idea-examples/youtube)

examples/android-example/.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/caches
5+
/.idea/libraries
6+
/.idea/modules.xml
7+
/.idea/workspace.xml
8+
/.idea/navEditor.xml
9+
/.idea/assetWizardSettings.xml
10+
.DS_Store
11+
/build
12+
/captures
13+
.externalNativeBuild
14+
.cxx
15+
local.properties

examples/android-example/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 📱 Android Example
2+
3+
A minimal Android project showcasing integration with **Kotlin DataFrame**.
4+
5+
<p align="center">
6+
<img src="screen.jpg" alt="App screenshot" height="320"/>
7+
</p>
8+
9+
It also includes the [Kotlin DataFrame Compiler Plugin](https://kotlin.github.io/dataframe/compiler-plugin.html).
10+
11+
> **Note:** The generated [extension properties](https://kotlin.github.io/dataframe/extensionpropertiesapi.html)
12+
> may not be highlighted correctly in current stable versions of Android Studio due to limited IDE support.
13+
> However, they work as expected at runtime and during compilation.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
plugins {
2+
alias(libs.plugins.android.application)
3+
alias(libs.plugins.kotlin.android)
4+
alias(libs.plugins.kotlin.compose)
5+
kotlin("plugin.dataframe") version "2.2.20-Beta1"
6+
}
7+
8+
android {
9+
namespace = "com.example.myapplication"
10+
compileSdk = 36
11+
12+
defaultConfig {
13+
applicationId = "com.example.myapplication"
14+
minSdk = 26
15+
targetSdk = 36
16+
versionCode = 1
17+
versionName = "1.0"
18+
19+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20+
}
21+
22+
buildTypes {
23+
release {
24+
isMinifyEnabled = false
25+
proguardFiles(
26+
getDefaultProguardFile("proguard-android-optimize.txt"),
27+
"proguard-rules.pro"
28+
)
29+
}
30+
}
31+
compileOptions {
32+
sourceCompatibility = JavaVersion.VERSION_1_8
33+
targetCompatibility = JavaVersion.VERSION_1_8
34+
}
35+
kotlinOptions {
36+
jvmTarget = "1.8"
37+
}
38+
buildFeatures {
39+
compose = true
40+
}
41+
packaging {
42+
resources {
43+
pickFirsts += listOf(
44+
"META-INF/AL2.0",
45+
"META-INF/LGPL2.1",
46+
"META-INF/ASL-2.0.txt",
47+
"META-INF/LICENSE.md",
48+
"META-INF/NOTICE.md",
49+
"META-INF/LGPL-3.0.txt",
50+
"META-INF/thirdparty-LICENSE",
51+
)
52+
excludes += listOf(
53+
"META-INF/kotlin-jupyter-libraries/libraries.json",
54+
"META-INF/{INDEX.LIST,DEPENDENCIES}",
55+
"{draftv3,draftv4}/schema",
56+
"arrow-git.properties",
57+
)
58+
}
59+
}
60+
}
61+
62+
dependencies {
63+
64+
implementation(libs.androidx.core.ktx)
65+
implementation(libs.androidx.lifecycle.runtime.ktx)
66+
implementation(libs.androidx.activity.compose)
67+
implementation(platform(libs.androidx.compose.bom))
68+
implementation(libs.androidx.ui)
69+
implementation(libs.androidx.ui.graphics)
70+
implementation(libs.androidx.ui.tooling.preview)
71+
implementation(libs.androidx.material3)
72+
testImplementation(libs.junit)
73+
androidTestImplementation(libs.androidx.junit)
74+
androidTestImplementation(libs.androidx.espresso.core)
75+
androidTestImplementation(platform(libs.androidx.compose.bom))
76+
androidTestImplementation(libs.androidx.ui.test.junit4)
77+
debugImplementation(libs.androidx.ui.tooling)
78+
debugImplementation(libs.androidx.ui.test.manifest)
79+
80+
// TODO update version
81+
// Core Kotlin DataFrame API & JSON IO.
82+
// See custom Gradle setup:
83+
// https://kotlin.github.io/dataframe/setupcustomgradle.html
84+
implementation("org.jetbrains.kotlinx:dataframe-core:1.0.0-dev-7831")
85+
implementation("org.jetbrains.kotlinx:dataframe-json:1.0.0-dev-7831")
86+
}
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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.example.myapplication
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("com.example.myapplication", appContext.packageName)
23+
}
24+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools" >
4+
5+
<application
6+
android:allowBackup="true"
7+
android:dataExtractionRules="@xml/data_extraction_rules"
8+
android:fullBackupContent="@xml/backup_rules"
9+
android:icon="@mipmap/ic_launcher"
10+
android:label="@string/app_name"
11+
android:roundIcon="@mipmap/ic_launcher_round"
12+
android:supportsRtl="true"
13+
android:theme="@style/Theme.MyApplication" >
14+
<activity
15+
android:name=".MainActivity"
16+
android:exported="true"
17+
android:label="@string/app_name"
18+
android:theme="@style/Theme.MyApplication" >
19+
<intent-filter>
20+
<action android:name="android.intent.action.MAIN" />
21+
22+
<category android:name="android.intent.category.LAUNCHER" />
23+
</intent-filter>
24+
</activity>
25+
</application>
26+
27+
</manifest>
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package com.example.myapplication
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.activity.enableEdgeToEdge
7+
import androidx.compose.foundation.background
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.Row
10+
import androidx.compose.foundation.layout.Spacer
11+
import androidx.compose.foundation.layout.fillMaxSize
12+
import androidx.compose.foundation.layout.height
13+
import androidx.compose.foundation.layout.padding
14+
import androidx.compose.foundation.lazy.LazyColumn
15+
import androidx.compose.foundation.lazy.items
16+
import androidx.compose.material3.MaterialTheme
17+
import androidx.compose.material3.Surface
18+
import androidx.compose.material3.Text
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.graphics.Color
22+
import androidx.compose.ui.tooling.preview.Preview
23+
import androidx.compose.ui.unit.dp
24+
import org.jetbrains.kotlinx.dataframe.DataFrame
25+
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
26+
import org.jetbrains.kotlinx.dataframe.api.cast
27+
import org.jetbrains.kotlinx.dataframe.api.dataFrameOf
28+
import org.jetbrains.kotlinx.dataframe.api.filter
29+
import org.jetbrains.kotlinx.dataframe.api.rows
30+
31+
@DataSchema
32+
data class Person(
33+
val age: Int,
34+
val name: String
35+
)
36+
37+
class MainActivity : ComponentActivity() {
38+
override fun onCreate(savedInstanceState: Bundle?) {
39+
super.onCreate(savedInstanceState)
40+
enableEdgeToEdge()
41+
42+
val df = dataFrameOf(
43+
"name" to listOf("Andrei", "Nikita", "Jolan"),
44+
"age" to listOf(22, 16, 37)
45+
).cast<Person>()
46+
47+
setContent {
48+
MaterialTheme {
49+
Surface(modifier = Modifier.fillMaxSize()) {
50+
DataFrameScreen(df)
51+
}
52+
}
53+
}
54+
}
55+
}
56+
57+
@Preview(showBackground = true)
58+
@Composable
59+
fun DefaultDataFrameScreenPreview() {
60+
val df = dataFrameOf(
61+
"name" to listOf("Andrei", "Nikita", "Jolan"),
62+
"age" to listOf(22, 16, 37)
63+
).cast<Person>()
64+
DataFrameScreen(df)
65+
}
66+
67+
@Composable
68+
fun DataFrameScreen(df: DataFrame<Person>) {
69+
Column(
70+
modifier = Modifier
71+
.fillMaxSize()
72+
.padding(top = 48.dp, start = 16.dp, end = 16.dp)
73+
) {
74+
Text(
75+
text = "Kotlin DataFrame on Android",
76+
style = MaterialTheme.typography.headlineSmall,
77+
modifier = Modifier.padding(bottom = 16.dp)
78+
)
79+
80+
Text(
81+
text = "df",
82+
modifier = Modifier
83+
.background(color = Color.LightGray)
84+
.padding(2.dp)
85+
)
86+
87+
DataFrameTable(df)
88+
89+
Text(
90+
text = "df.filter { age >= 20 }",
91+
modifier = Modifier
92+
.background(color = Color.LightGray)
93+
.padding(2.dp)
94+
)
95+
96+
DataFrameTable(df.filter { age >= 20 })
97+
}
98+
}
99+
100+
@Preview(showBackground = true)
101+
@Composable
102+
fun DefaultDataFrameTablePreview() {
103+
val df = dataFrameOf(
104+
"name" to listOf("Andrei", "Nikita", "Jolan"),
105+
"age" to listOf(22, 16, 37)
106+
).cast<Person>()
107+
DataFrameTable(df)
108+
}
109+
110+
@Composable
111+
fun DataFrameTable(df: DataFrame<*>) {
112+
val columnNames = df.columnNames()
113+
114+
// Header
115+
Row {
116+
for (name in columnNames) {
117+
Text(
118+
text = name,
119+
modifier = Modifier
120+
.weight(1f)
121+
.padding(4.dp),
122+
style = MaterialTheme.typography.labelLarge
123+
)
124+
}
125+
}
126+
127+
Spacer(Modifier.height(4.dp))
128+
129+
// Rows
130+
LazyColumn {
131+
items(df.rows().toList()) { row ->
132+
Row {
133+
for (cell in row.values()) {
134+
Text(
135+
text = cell.toString(),
136+
modifier = Modifier
137+
.weight(1f)
138+
.padding(4.dp)
139+
)
140+
}
141+
}
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)