Compare commits
31 commits
Author | SHA1 | Date | |
---|---|---|---|
9f8c113ed8 | |||
54bb769774 | |||
472aca6f7f | |||
6a4eae24cf | |||
f74f6840ef | |||
81fab5a147 | |||
319f344145 | |||
cdff3c2046 | |||
440136b278 | |||
883950315b | |||
b8e52049ad | |||
57c51b74ed | |||
468af55af4 | |||
65905bf3c9 | |||
bb7401461f | |||
d0fee3c868 | |||
eccf2870f9 | |||
4dfe87aa53 | |||
c96d070c00 | |||
38c6206f5b | |||
785e6f2c78 | |||
4cce0a5108 | |||
fe36344cd4 | |||
a41ee75b03 | |||
de8fe102bf | |||
ec6fe692a3 | |||
bd9163b2e1 | |||
ed227bf2b6 | |||
063ec7632f | |||
3d2cc0a62c | |||
0feaf9e92b |
7 changed files with 116 additions and 53 deletions
4
.github/workflows/build.yaml
vendored
4
.github/workflows/build.yaml
vendored
|
@ -12,10 +12,10 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
@ -11,10 +11,10 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
45
README.md
45
README.md
|
@ -1,14 +1,39 @@
|
||||||
This is a Kotlin Multiplatform project targeting Android, iOS.
|
# Bob -- The Handy Feedback App
|
||||||
|
|
||||||
* `/composeApp` is for code that will be shared across your Compose Multiplatform applications.
|
This project is a simple Kotlin Multiplatform Application that allows users to enter free-text and
|
||||||
It contains several subfolders:
|
submit the content to a server over HTTP. It also includes a form with sentiment selection and
|
||||||
- `commonMain` is for code that’s common for all targets.
|
displays a Snackbar whenever the submit button is pressed.
|
||||||
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name.
|
|
||||||
For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app,
|
|
||||||
`iosMain` would be the right folder for such calls.
|
|
||||||
|
|
||||||
* `/iosApp` contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform,
|
## Features
|
||||||
you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
|
|
||||||
|
|
||||||
|
- Free-text input form
|
||||||
|
- Sentiment selection using FilterChips
|
||||||
|
- Submit content to a server using Retrofit
|
||||||
|
- Display Snackbar on form submission
|
||||||
|
|
||||||
Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html)…
|
## Technologies Used
|
||||||
|
|
||||||
|
- Kotlin
|
||||||
|
- Jetpack Compose
|
||||||
|
- Retrofit
|
||||||
|
- Gradle
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. Download the latest release from
|
||||||
|
the [releases page](https://git.brmartin.co.uk/bob/mobile-application/releases)
|
||||||
|
2. Install the application following the on-screen instructions.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
1. Run the application on an Android emulator or a physical device.
|
||||||
|
2. Select a sentiment using the FilterChips.
|
||||||
|
3. Enter your text in the provided text field.
|
||||||
|
4. Press the submit button to send the content to the server.
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
- `composeApp/src/commonMain/kotlin/uk/sky/bob/application/App.kt`: Main Compose UI and form
|
||||||
|
submission logic.
|
||||||
|
|
|
@ -1,25 +1,38 @@
|
||||||
package uk.sky.bob.application
|
package uk.sky.bob.application
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.Button
|
import androidx.compose.material.Button
|
||||||
|
import androidx.compose.material.ChipDefaults
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.FilterChip
|
import androidx.compose.material.FilterChip
|
||||||
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Scaffold
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.TextField
|
import androidx.compose.material.TextField
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Send
|
||||||
|
import androidx.compose.material.rememberScaffoldState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@ -27,38 +40,60 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||||
@Preview
|
@Preview
|
||||||
fun App() {
|
fun App() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
val scaffoldState = rememberScaffoldState()
|
||||||
Row {
|
val scope = rememberCoroutineScope()
|
||||||
val state = remember { mutableStateOf(Sentiment.HAPPY) }
|
|
||||||
for (emotion in Sentiment.entries) {
|
|
||||||
FilterChip(
|
|
||||||
onClick = { state.value = emotion },
|
|
||||||
selected = state.value == emotion,
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
leadingIcon = { Text(emotion.leadingIcon) },
|
|
||||||
) {
|
|
||||||
Text(emotion.friendlyName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Scaffold(scaffoldState = scaffoldState) {
|
||||||
var text by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
Box(
|
||||||
|
modifier = Modifier.fillMaxHeight(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
val sentiment = remember { mutableStateOf(Sentiment.HAPPY) }
|
||||||
|
var comment by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
TextFieldValue("")
|
TextFieldValue("")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Row(modifier = Modifier.padding(8.dp)) {
|
||||||
|
for (emotion in Sentiment.entries) {
|
||||||
|
FilterChip(
|
||||||
|
onClick = { sentiment.value = emotion },
|
||||||
|
selected = sentiment.value == emotion,
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
leadingIcon = { Text(emotion.leadingIcon, fontSize = 20.sp) },
|
||||||
|
colors = ChipDefaults.outlinedFilterChipColors(),
|
||||||
|
border = ChipDefaults.outlinedBorder,
|
||||||
|
) {
|
||||||
|
Text(emotion.friendlyName, fontSize = 20.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(modifier = Modifier.padding(8.dp)) {
|
||||||
TextField(
|
TextField(
|
||||||
value = text,
|
value = comment,
|
||||||
onValueChange = { text = it },
|
onValueChange = { comment = it },
|
||||||
label = { Text("Your comment") },
|
label = { Text("Your comment") },
|
||||||
maxLines = 3,
|
modifier = Modifier.height(100.dp).fillMaxWidth().padding(8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row(modifier = Modifier.padding(8.dp)) {
|
||||||
Button(onClick = { /* Handle submit */ }) {
|
Button(onClick = {
|
||||||
Text("Submit")
|
sentiment.value = Sentiment.HAPPY
|
||||||
|
comment = TextFieldValue("")
|
||||||
|
scope.launch {
|
||||||
|
delay(1000)
|
||||||
|
scaffoldState.snackbarHostState.showSnackbar("Feedback sent")
|
||||||
|
}
|
||||||
|
}, modifier = Modifier.padding(8.dp)) {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
[versions]
|
[versions]
|
||||||
agp = "8.5.2"
|
agp = "8.10.1"
|
||||||
android-compileSdk = "34"
|
android-compileSdk = "35"
|
||||||
android-minSdk = "24"
|
android-minSdk = "24"
|
||||||
android-targetSdk = "34"
|
android-targetSdk = "34"
|
||||||
androidx-activityCompose = "1.9.3"
|
androidx-activityCompose = "1.10.1"
|
||||||
androidx-appcompat = "1.7.0"
|
androidx-appcompat = "1.7.1"
|
||||||
androidx-constraintlayout = "2.2.0"
|
androidx-constraintlayout = "2.2.1"
|
||||||
androidx-core-ktx = "1.15.0"
|
androidx-core-ktx = "1.16.0"
|
||||||
androidx-espresso-core = "3.6.1"
|
androidx-espresso-core = "3.6.1"
|
||||||
androidx-lifecycle = "2.8.4"
|
androidx-lifecycle = "2.9.1"
|
||||||
androidx-material = "1.12.0"
|
androidx-material = "1.12.0"
|
||||||
androidx-test-junit = "1.2.1"
|
androidx-test-junit = "1.2.1"
|
||||||
compose-multiplatform = "1.7.0"
|
compose-multiplatform = "1.7.3"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kotlin = "2.1.0"
|
kotlin = "2.1.21"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
3
renovate.json
Normal file
3
renovate.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue