├── .circleci
└── config.yml
├── .github
└── workflows
│ └── pr.yml
├── .gitignore
├── CODEOWNERS
├── LICENSE
├── README.md
├── final
├── .gitignore
├── README.md
├── app
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── rocketreserver
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── graphql
│ │ │ ├── BookTrip.graphql
│ │ │ ├── CancelTrip.graphql
│ │ │ ├── LaunchDetails.graphql
│ │ │ ├── LaunchList.graphql
│ │ │ ├── Login.graphql
│ │ │ ├── TripsBooked.graphql
│ │ │ └── schema.graphqls
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── rocketreserver
│ │ │ │ ├── Apollo.kt
│ │ │ │ ├── LaunchDetails.kt
│ │ │ │ ├── LaunchList.kt
│ │ │ │ ├── Login.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── Navigation.kt
│ │ │ │ ├── TokenRepository.kt
│ │ │ │ └── ui
│ │ │ │ └── theme
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ └── ic_placeholder.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ │ └── xml
│ │ │ ├── backup_rules.xml
│ │ │ ├── data_extraction_rules.xml
│ │ │ └── network_security_config.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── rocketreserver
│ │ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
│ ├── libs.versions.toml
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
└── start
├── .gitignore
├── README.md
├── app
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── rocketreserver
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── kotlin
│ │ └── com
│ │ │ └── example
│ │ │ └── rocketreserver
│ │ │ ├── LaunchDetails.kt
│ │ │ ├── LaunchList.kt
│ │ │ ├── Login.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── Navigation.kt
│ │ │ ├── TokenRepository.kt
│ │ │ └── ui
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ └── ic_placeholder.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ ├── data_extraction_rules.xml
│ │ └── network_security_config.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── rocketreserver
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | secops: apollo/circleci-secops-orb@2.0.6
5 |
6 | workflows:
7 | security-scans:
8 | jobs:
9 | - secops/gitleaks:
10 | context:
11 | - platform-docker-ro
12 | - github-orb
13 | - secops-oidc
14 | git-base-revision: <<#pipeline.git.base_revision>><><>
15 | git-revision: << pipeline.git.revision >>
16 | - secops/semgrep:
17 | context:
18 | - secops-oidc
19 | - github-orb
20 | git-base-revision: <<#pipeline.git.base_revision>><><>
21 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | name: pr
2 |
3 | on:
4 | pull_request
5 |
6 | # Cancel any current or previous job from the same PR
7 | concurrency:
8 | group: ${{ github.head_ref }}
9 | cancel-in-progress: true
10 |
11 | jobs:
12 | build-app:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: set up JDK 17
17 | uses: actions/setup-java@v3
18 | with:
19 | distribution: 'temurin'
20 | java-version: 17
21 | - name: Build tutorial
22 | working-directory: ./final
23 | run: |
24 | ./gradlew assembleDebug
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Idea
2 | **/.idea/*
3 | !**/.idea/codeStyles
4 | !**/.idea/icon.png
5 | !**/.idea/runConfigurations
6 | !**/.idea/scopes
7 | *.iml
8 |
9 | .gradle
10 | /local.properties
11 | /.idea/caches
12 | /.idea/libraries
13 | /.idea/modules.xml
14 | /.idea/workspace.xml
15 | /.idea/navEditor.xml
16 | /.idea/assetWizardSettings.xml
17 | .DS_Store
18 | /build
19 | /captures
20 | .externalNativeBuild
21 | .cxx
22 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @martinbonnin @BoD
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Meteor Development Group, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The tutorial is now part of the Odyssey learning platform and moved to https://github.com/apollographql/apollo-kotlin-odyssey
2 |
3 | # Apollo Kotlin Tutorial
4 |
5 | Repository for the [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin) tutorial.
6 |
7 | The tutorial is available through our [documentation site](https://www.apollographql.com/docs/kotlin/tutorial/00-introduction/). This repository contains the corresponding code.
8 |
9 | - [start](./start): the starter project with the boilerplate and UI code already written but no Apollo Kotlin code.
10 | - [final](./final): the final state of the application with all functionality
11 |
12 | For copy errors in the tutorial, please file bugs against the main [`apollo-kotlin` repo](https://github.com/apollographql/apollo-kotlin). For broken code, please file issues on this repo.
13 |
--------------------------------------------------------------------------------
/final/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .gradle
3 | .DS_Store
4 | build
5 | captures
6 | .externalNativeBuild
7 | .cxx
8 | local.properties
9 |
--------------------------------------------------------------------------------
/final/README.md:
--------------------------------------------------------------------------------
1 | # Apollo Kotlin Tutorial
2 |
3 | Repository for the [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin) tutorial.
4 |
5 | This is the final state of the application with all functionality.
6 |
--------------------------------------------------------------------------------
/final/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.compose.compiler)
5 | alias(libs.plugins.apollo)
6 | }
7 |
8 | android {
9 | namespace = "com.example.rocketreserver"
10 | compileSdk = 34
11 |
12 | defaultConfig {
13 | applicationId = "com.example.rocketreserver"
14 | minSdk = 24
15 | targetSdk = 34
16 | versionCode = 1
17 | versionName = "1.0"
18 |
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | vectorDrawables {
21 | useSupportLibrary = true
22 | }
23 | }
24 |
25 | buildTypes {
26 | release {
27 | isMinifyEnabled = false
28 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
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 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
44 | }
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation(libs.androidx.core.ktx)
50 | implementation(libs.androidx.lifecycle.runtime.ktx)
51 | implementation(libs.androidx.activity.compose)
52 | implementation(platform(libs.androidx.compose.bom))
53 | implementation(libs.androidx.ui)
54 | implementation(libs.androidx.ui.graphics)
55 | implementation(libs.androidx.ui.tooling.preview)
56 | implementation(libs.androidx.material3)
57 | implementation(libs.androidx.navigation.compose)
58 | implementation(libs.androidx.security.crypto)
59 | implementation(libs.coil.compose)
60 |
61 | implementation(libs.apollo.runtime)
62 |
63 | testImplementation(libs.junit)
64 | androidTestImplementation(libs.androidx.junit)
65 | androidTestImplementation(libs.androidx.espresso.core)
66 | androidTestImplementation(platform(libs.androidx.compose.bom))
67 | androidTestImplementation(libs.androidx.ui.test.junit4)
68 | debugImplementation(libs.androidx.ui.tooling)
69 | debugImplementation(libs.androidx.ui.test.manifest)
70 | }
71 |
72 | apollo {
73 | service("service") {
74 | packageName.set("com.example.rocketreserver")
75 | introspection {
76 | endpointUrl.set("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
77 | schemaFile.set(file("src/main/graphql/schema.graphqls"))
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/final/app/proguard-rules.pro:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/final/app/src/androidTest/java/com/example/rocketreserver/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
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.rocketreserver", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/final/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/BookTrip.graphql:
--------------------------------------------------------------------------------
1 | mutation BookTrip($id: ID!) {
2 | bookTrips(launchIds: [$id]) {
3 | success
4 | message
5 | launches {
6 | id
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/CancelTrip.graphql:
--------------------------------------------------------------------------------
1 | mutation CancelTrip($id: ID!) {
2 | cancelTrip(launchId: $id) {
3 | success
4 | message
5 | launches {
6 | id
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/LaunchDetails.graphql:
--------------------------------------------------------------------------------
1 | query LaunchDetails($id: ID!) {
2 | launch(id: $id) {
3 | id
4 | site
5 | mission {
6 | name
7 | missionPatch(size: LARGE)
8 | }
9 | rocket {
10 | name
11 | type
12 | }
13 | isBooked
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/LaunchList.graphql:
--------------------------------------------------------------------------------
1 | query LaunchList($cursor: String) {
2 | launches(after: $cursor) {
3 | cursor
4 | launches {
5 | id
6 | site
7 | mission {
8 | name
9 | missionPatch(size: SMALL)
10 | }
11 | }
12 | hasMore
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/Login.graphql:
--------------------------------------------------------------------------------
1 | mutation Login($email: String!) {
2 | login(email: $email) {
3 | token
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/TripsBooked.graphql:
--------------------------------------------------------------------------------
1 | subscription TripsBooked {
2 | tripsBooked
3 | }
4 |
--------------------------------------------------------------------------------
/final/app/src/main/graphql/schema.graphqls:
--------------------------------------------------------------------------------
1 | type Query {
2 | launches("The number of results to show. Must be >= 1. Default = 20" pageSize: Int, "If you add a cursor here, it will only return results _after_ this cursor" after: String): LaunchConnection!
3 |
4 | launch(id: ID!): Launch
5 |
6 | me: User
7 |
8 | totalTripsBooked: Int
9 | }
10 |
11 | """
12 | Simple wrapper around our list of launches that contains a cursor to the
13 | last item in the list. Pass this cursor to the launches query to fetch results
14 | after these.
15 | """
16 | type LaunchConnection {
17 | cursor: String!
18 |
19 | hasMore: Boolean!
20 |
21 | launches: [Launch]!
22 | }
23 |
24 | type Launch {
25 | id: ID!
26 |
27 | site: String
28 |
29 | mission: Mission
30 |
31 | rocket: Rocket
32 |
33 | isBooked: Boolean!
34 | }
35 |
36 | type Mission {
37 | name: String
38 |
39 | missionPatch(size: PatchSize): String
40 | }
41 |
42 | enum PatchSize {
43 | SMALL
44 |
45 | LARGE
46 | }
47 |
48 | type Rocket {
49 | id: ID!
50 |
51 | name: String
52 |
53 | type: String
54 | }
55 |
56 | type User {
57 | id: ID!
58 |
59 | email: String!
60 |
61 | profileImage: String
62 |
63 | trips: [Launch]!
64 |
65 | token: String
66 | }
67 |
68 | type Mutation {
69 | bookTrips(launchIds: [ID]!): TripUpdateResponse!
70 |
71 | cancelTrip(launchId: ID!): TripUpdateResponse!
72 |
73 | login(email: String): User
74 | }
75 |
76 | type TripUpdateResponse {
77 | success: Boolean!
78 |
79 | message: String
80 |
81 | launches: [Launch]
82 | }
83 |
84 | type Subscription {
85 | tripsBooked: Int
86 | }
87 |
88 | """
89 | The `Upload` scalar type represents a file upload.
90 | """
91 | scalar Upload
92 |
93 | schema {
94 | query: Query
95 | mutation: Mutation
96 | subscription: Subscription
97 | }
98 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/Apollo.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import android.util.Log
4 | import com.apollographql.apollo.ApolloClient
5 | import com.apollographql.apollo.network.okHttpClient
6 | import kotlinx.coroutines.delay
7 | import okhttp3.Interceptor
8 | import okhttp3.OkHttpClient
9 | import okhttp3.Response
10 |
11 | private class AuthorizationInterceptor() : Interceptor {
12 | override fun intercept(chain: Interceptor.Chain): Response {
13 | val request = chain.request().newBuilder()
14 | .apply {
15 | TokenRepository.getToken()?.let { token ->
16 | addHeader("Authorization", token)
17 | }
18 | }
19 | .build()
20 | return chain.proceed(request)
21 | }
22 | }
23 |
24 | val apolloClient = ApolloClient.Builder()
25 | .serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
26 | .webSocketServerUrl("wss://apollo-fullstack-tutorial.herokuapp.com/graphql")
27 | .okHttpClient(
28 | OkHttpClient.Builder()
29 | .addInterceptor(AuthorizationInterceptor())
30 | .build()
31 | )
32 | .webSocketReopenWhen { throwable, attempt ->
33 | Log.d("Apollo", "WebSocket got disconnected, reopening after a delay", throwable)
34 | delay(attempt * 1000)
35 | true
36 | }
37 |
38 | .build()
39 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/LaunchDetails.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import android.util.Log
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.Spacer
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.fillMaxWidth
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.layout.size
12 | import androidx.compose.material3.Button
13 | import androidx.compose.material3.CircularProgressIndicator
14 | import androidx.compose.material3.LocalContentColor
15 | import androidx.compose.material3.MaterialTheme
16 | import androidx.compose.material3.Text
17 | import androidx.compose.runtime.Composable
18 | import androidx.compose.runtime.LaunchedEffect
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.runtime.mutableStateOf
21 | import androidx.compose.runtime.remember
22 | import androidx.compose.runtime.rememberCoroutineScope
23 | import androidx.compose.runtime.setValue
24 | import androidx.compose.ui.Alignment
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.res.painterResource
27 | import androidx.compose.ui.tooling.preview.Preview
28 | import androidx.compose.ui.unit.dp
29 | import coil.compose.AsyncImage
30 | import com.apollographql.apollo.exception.ApolloNetworkException
31 | import com.example.rocketreserver.LaunchDetailsState.Loading
32 | import com.example.rocketreserver.LaunchDetailsState.Success
33 | import com.example.rocketreserver.LaunchDetailsState.Error
34 | import kotlinx.coroutines.launch
35 |
36 | private sealed interface LaunchDetailsState {
37 | object Loading : LaunchDetailsState
38 | data class Error(val message: String) : LaunchDetailsState
39 | data class Success(val data: LaunchDetailsQuery.Data) : LaunchDetailsState
40 | }
41 |
42 | @Composable
43 | fun LaunchDetails(launchId: String, navigateToLogin: () -> Unit) {
44 | var state by remember { mutableStateOf(Loading) }
45 | LaunchedEffect(Unit) {
46 | val response = apolloClient.query(LaunchDetailsQuery(launchId)).execute()
47 | state = when {
48 | response.errors.orEmpty().isNotEmpty() -> {
49 | // GraphQL error
50 | Error(response.errors!!.first().message)
51 | }
52 | response.exception is ApolloNetworkException -> {
53 | // Network error
54 | Error("Please check your network connectivity.")
55 | }
56 | response.data != null -> {
57 | // data (never partial)
58 | Success(response.data!!)
59 | }
60 | else -> {
61 | // Another fetch error, maybe a cache miss?
62 | // Or potentially a non-compliant server returning data: null without an error
63 | Error("Oh no... An error happened.")
64 | }
65 | }
66 | }
67 | when (val s = state) {
68 | Loading -> Loading()
69 | is Error -> ErrorMessage(s.message)
70 | is Success -> LaunchDetails(s.data, navigateToLogin)
71 | }
72 | }
73 |
74 | @Composable
75 | private fun LaunchDetails(
76 | data: LaunchDetailsQuery.Data,
77 | navigateToLogin: () -> Unit,
78 | ) {
79 | Column(
80 | modifier = Modifier
81 | .fillMaxSize()
82 | .padding(16.dp)
83 | ) {
84 | Row(verticalAlignment = Alignment.CenterVertically) {
85 | // Mission patch
86 | AsyncImage(
87 | modifier = Modifier.size(160.dp, 160.dp),
88 | model = data.launch?.mission?.missionPatch,
89 | placeholder = painterResource(R.drawable.ic_placeholder),
90 | error = painterResource(R.drawable.ic_placeholder),
91 | contentDescription = "Mission patch"
92 | )
93 |
94 | Spacer(modifier = Modifier.size(16.dp))
95 |
96 | Column {
97 | // Mission name
98 | Text(
99 | style = MaterialTheme.typography.headlineMedium,
100 | text = data.launch?.mission?.name ?: ""
101 | )
102 |
103 | // Rocket name
104 | Text(
105 | modifier = Modifier.padding(top = 8.dp),
106 | style = MaterialTheme.typography.headlineSmall,
107 | text = data.launch?.rocket?.name?.let { "🚀 $it" } ?: "",
108 | )
109 |
110 | // Site
111 | Text(
112 | modifier = Modifier.padding(top = 8.dp),
113 | style = MaterialTheme.typography.titleMedium,
114 | text = data.launch?.site ?: "",
115 | )
116 | }
117 | }
118 | // Book button
119 | var loading by remember { mutableStateOf(false) }
120 | val scope = rememberCoroutineScope()
121 | var isBooked by remember { mutableStateOf(data.launch?.isBooked == true) }
122 | Button(
123 | modifier = Modifier
124 | .padding(top = 32.dp)
125 | .fillMaxWidth(),
126 | enabled = !loading,
127 | onClick = {
128 | loading = true
129 | scope.launch {
130 | val ok = onBookButtonClick(
131 | launchId = data.launch?.id ?: "",
132 | isBooked = isBooked,
133 | navigateToLogin = navigateToLogin
134 | )
135 | if (ok) {
136 | isBooked = !isBooked
137 | }
138 | loading = false
139 | }
140 | }
141 | ) {
142 | if (loading) {
143 | SmallLoading()
144 | } else {
145 | Text(text = if (!isBooked) "Book now" else "Cancel booking")
146 | }
147 | }
148 | }
149 | }
150 |
151 | private suspend fun onBookButtonClick(
152 | launchId: String,
153 | isBooked: Boolean,
154 | navigateToLogin: () -> Unit
155 | ): Boolean {
156 | if (TokenRepository.getToken() == null) {
157 | navigateToLogin()
158 | return false
159 | }
160 | val mutation = if (isBooked) {
161 | CancelTripMutation(id = launchId)
162 | } else {
163 | BookTripMutation(id = launchId)
164 | }
165 | val response = apolloClient.mutation(mutation).execute()
166 | return if (response.data != null) {
167 | true
168 | } else {
169 | if (response.exception != null) {
170 | Log.w("LaunchDetails", "Failed to book/cancel trip", response.exception)
171 | false
172 | } else {
173 | Log.w("LaunchDetails", "Failed to book/cancel trip: ${response.errors!![0].message}")
174 | false
175 | }
176 | }
177 | }
178 |
179 | @Composable
180 | private fun ErrorMessage(text: String) {
181 | Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
182 | Text(text = text)
183 | }
184 | }
185 |
186 | @Composable
187 | private fun Loading() {
188 | Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
189 | CircularProgressIndicator()
190 | }
191 | }
192 |
193 | @Composable
194 | private fun SmallLoading() {
195 | CircularProgressIndicator(
196 | modifier = Modifier.size(24.dp),
197 | color = LocalContentColor.current,
198 | strokeWidth = 2.dp,
199 | )
200 | }
201 |
202 | @Preview(showBackground = true)
203 | @Composable
204 | private fun LaunchDetailsPreview() {
205 | LaunchDetails(launchId = "42", navigateToLogin = {})
206 | }
207 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/LaunchList.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | package com.example.rocketreserver
4 |
5 | import androidx.compose.foundation.clickable
6 | import androidx.compose.foundation.layout.Box
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.foundation.layout.size
11 | import androidx.compose.foundation.lazy.LazyColumn
12 | import androidx.compose.foundation.lazy.items
13 | import androidx.compose.material3.CircularProgressIndicator
14 | import androidx.compose.material3.ExperimentalMaterial3Api
15 | import androidx.compose.material3.ListItem
16 | import androidx.compose.material3.Text
17 | import androidx.compose.runtime.Composable
18 | import androidx.compose.runtime.LaunchedEffect
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.runtime.mutableStateOf
21 | import androidx.compose.runtime.remember
22 | import androidx.compose.runtime.setValue
23 | import androidx.compose.ui.Alignment
24 | import androidx.compose.ui.Modifier
25 | import androidx.compose.ui.res.painterResource
26 | import androidx.compose.ui.unit.dp
27 | import coil.compose.AsyncImage
28 | import com.apollographql.apollo.api.ApolloResponse
29 | import com.apollographql.apollo.api.Optional
30 |
31 | @Composable
32 | fun LaunchList(onLaunchClick: (launchId: String) -> Unit) {
33 | var cursor: String? by remember { mutableStateOf(null) }
34 | var response: ApolloResponse? by remember { mutableStateOf(null) }
35 | var launchList by remember { mutableStateOf(emptyList()) }
36 | LaunchedEffect(cursor) {
37 | response = apolloClient.query(LaunchListQuery(Optional.present(cursor))).execute()
38 | launchList = launchList + response?.data?.launches?.launches?.filterNotNull().orEmpty()
39 | }
40 |
41 | LazyColumn(modifier = Modifier.fillMaxSize()) {
42 | items(launchList) { launch ->
43 | LaunchItem(launch = launch, onClick = onLaunchClick)
44 | }
45 |
46 | item {
47 | if (response?.data?.launches?.hasMore == true) {
48 | LoadingItem()
49 | cursor = response?.data?.launches?.cursor
50 | }
51 | }
52 | }
53 | }
54 |
55 | @Composable
56 | private fun LaunchItem(launch: LaunchListQuery.Launch, onClick: (launchId: String) -> Unit) {
57 | ListItem(
58 | modifier = Modifier.clickable { onClick(launch.id) },
59 | headlineContent = {
60 | // Mission name
61 | Text(text = launch.mission?.name ?: "")
62 | },
63 | supportingContent = {
64 | // Site
65 | Text(text = launch.site ?: "")
66 | },
67 | leadingContent = {
68 | // Mission patch
69 | AsyncImage(
70 | modifier = Modifier.size(68.dp, 68.dp),
71 | model = launch.mission?.missionPatch,
72 | placeholder = painterResource(R.drawable.ic_placeholder),
73 | error = painterResource(R.drawable.ic_placeholder),
74 | contentDescription = "Mission patch"
75 | )
76 | }
77 | )
78 | }
79 |
80 | @Composable
81 | private fun LoadingItem() {
82 | Box(
83 | contentAlignment = Alignment.Center,
84 | modifier = Modifier
85 | .fillMaxWidth()
86 | .padding(16.dp)
87 | ) {
88 | CircularProgressIndicator()
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/Login.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | package com.example.rocketreserver
4 |
5 | import android.util.Log
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.foundation.layout.size
11 | import androidx.compose.foundation.text.KeyboardOptions
12 | import androidx.compose.material3.Button
13 | import androidx.compose.material3.CircularProgressIndicator
14 | import androidx.compose.material3.ExperimentalMaterial3Api
15 | import androidx.compose.material3.LocalContentColor
16 | import androidx.compose.material3.MaterialTheme
17 | import androidx.compose.material3.OutlinedTextField
18 | import androidx.compose.material3.Text
19 | import androidx.compose.runtime.Composable
20 | import androidx.compose.runtime.getValue
21 | import androidx.compose.runtime.mutableStateOf
22 | import androidx.compose.runtime.remember
23 | import androidx.compose.runtime.rememberCoroutineScope
24 | import androidx.compose.runtime.setValue
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.text.input.KeyboardType
27 | import androidx.compose.ui.text.style.TextAlign
28 | import androidx.compose.ui.tooling.preview.Preview
29 | import androidx.compose.ui.unit.dp
30 | import kotlinx.coroutines.launch
31 |
32 | @Composable
33 | fun Login(navigateBack: () -> Unit) {
34 | Column(
35 | modifier = Modifier
36 | .fillMaxSize()
37 | .padding(16.dp)
38 | ) {
39 | // Title
40 | Text(
41 | modifier = Modifier.fillMaxWidth(),
42 | textAlign = TextAlign.Center,
43 | style = MaterialTheme.typography.headlineMedium,
44 | text = "Login"
45 | )
46 |
47 | // Email
48 | var email by remember { mutableStateOf("") }
49 | OutlinedTextField(
50 | modifier = Modifier
51 | .padding(top = 16.dp)
52 | .fillMaxWidth(),
53 | label = { Text("Email") },
54 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
55 | value = email,
56 | onValueChange = { email = it }
57 | )
58 |
59 | // Submit button
60 | var loading by remember { mutableStateOf(false) }
61 | val scope = rememberCoroutineScope()
62 | Button(
63 | modifier = Modifier
64 | .padding(top = 32.dp)
65 | .fillMaxWidth(),
66 | enabled = !loading,
67 | onClick = {
68 | loading = true
69 | scope.launch {
70 | val ok = login(email)
71 | loading = false
72 | if (ok) navigateBack()
73 | }
74 | }
75 | ) {
76 | if (loading) {
77 | Loading()
78 | } else {
79 | Text(text = "Submit")
80 | }
81 | }
82 | }
83 | }
84 |
85 | private suspend fun login(email: String): Boolean {
86 | val response = apolloClient.mutation(LoginMutation(email = email)).execute()
87 | val data = response.data
88 | return if (data != null) {
89 | if (data.login?.token != null) {
90 | TokenRepository.setToken(data.login.token)
91 | true
92 | } else {
93 | Log.w("Login", "Failed to login: no token returned by the backend")
94 | false
95 | }
96 | } else {
97 | if (response.exception != null) {
98 | Log.w("Login", "Failed to login", response.exception)
99 | false
100 | } else {
101 | Log.w("Login", "Failed to login: ${response.errors!![0].message}")
102 | false
103 | }
104 | }
105 | }
106 |
107 | @Composable
108 | private fun Loading() {
109 | CircularProgressIndicator(
110 | modifier = Modifier.size(24.dp),
111 | color = LocalContentColor.current,
112 | strokeWidth = 2.dp,
113 | )
114 | }
115 |
116 | @Preview(showBackground = true)
117 | @Composable
118 | private fun LoginPreview() {
119 | Login(navigateBack = { })
120 | }
121 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/MainActivity.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | package com.example.rocketreserver
4 |
5 | import android.os.Bundle
6 | import androidx.activity.ComponentActivity
7 | import androidx.activity.compose.setContent
8 | import androidx.compose.foundation.layout.Box
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.material3.ExperimentalMaterial3Api
11 | import androidx.compose.material3.Scaffold
12 | import androidx.compose.material3.SnackbarDuration
13 | import androidx.compose.material3.SnackbarHost
14 | import androidx.compose.material3.SnackbarHostState
15 | import androidx.compose.material3.Text
16 | import androidx.compose.material3.TopAppBar
17 | import androidx.compose.runtime.Composable
18 | import androidx.compose.runtime.LaunchedEffect
19 | import androidx.compose.runtime.collectAsState
20 | import androidx.compose.runtime.getValue
21 | import androidx.compose.runtime.remember
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.res.stringResource
24 | import androidx.navigation.compose.NavHost
25 | import androidx.navigation.compose.composable
26 | import androidx.navigation.compose.rememberNavController
27 | import com.apollographql.apollo.api.ApolloResponse
28 | import com.example.rocketreserver.ui.theme.RocketReserverTheme
29 |
30 | class MainActivity : ComponentActivity() {
31 | override fun onCreate(savedInstanceState: Bundle?) {
32 | super.onCreate(savedInstanceState)
33 | TokenRepository.init(this)
34 | setContent {
35 | RocketReserverTheme {
36 | val snackbarHostState = remember { SnackbarHostState() }
37 | val tripBookedFlow = remember { apolloClient.subscription(TripsBookedSubscription()).toFlow() }
38 | val tripBookedResponse: ApolloResponse? by tripBookedFlow.collectAsState(initial = null)
39 | LaunchedEffect(tripBookedResponse) {
40 | if (tripBookedResponse == null) return@LaunchedEffect
41 | val message = when (tripBookedResponse!!.data?.tripsBooked) {
42 | null -> "Subscription error"
43 | -1 -> "Trip cancelled"
44 | else -> "Trip booked! 🚀"
45 | }
46 | snackbarHostState.showSnackbar(
47 | message = message,
48 | duration = SnackbarDuration.Short
49 | )
50 | }
51 |
52 | Scaffold(
53 | topBar = { TopAppBar({ Text(stringResource(R.string.app_name)) }) },
54 | snackbarHost = { SnackbarHost(snackbarHostState) },
55 | ) { paddingValues ->
56 | Box(Modifier.padding(paddingValues)) {
57 | MainNavHost()
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | @Composable
66 | private fun MainNavHost() {
67 | val navController = rememberNavController()
68 | NavHost(navController, startDestination = NavigationDestinations.LAUNCH_LIST) {
69 | composable(route = NavigationDestinations.LAUNCH_LIST) {
70 | LaunchList(
71 | onLaunchClick = { launchId ->
72 | navController.navigate("${NavigationDestinations.LAUNCH_DETAILS}/$launchId")
73 | }
74 | )
75 | }
76 |
77 | composable(route = "${NavigationDestinations.LAUNCH_DETAILS}/{${NavigationArguments.LAUNCH_ID}}") { navBackStackEntry ->
78 | LaunchDetails(
79 | launchId = navBackStackEntry.arguments!!.getString(NavigationArguments.LAUNCH_ID)!!,
80 | navigateToLogin = {
81 | navController.navigate(NavigationDestinations.LOGIN)
82 | }
83 | )
84 | }
85 |
86 | composable(route = NavigationDestinations.LOGIN) {
87 | Login(
88 | navigateBack = {
89 | navController.popBackStack()
90 | }
91 | )
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/Navigation.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | object NavigationDestinations {
4 | const val LAUNCH_LIST = "launchList"
5 | const val LAUNCH_DETAILS = "launchDetails"
6 | const val LOGIN = "login"
7 | }
8 |
9 | object NavigationArguments {
10 | const val LAUNCH_ID = "launchId"
11 | }
12 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/TokenRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.security.crypto.EncryptedSharedPreferences
6 | import androidx.security.crypto.MasterKey
7 |
8 | object TokenRepository {
9 | private const val KEY_TOKEN = "TOKEN"
10 |
11 | private lateinit var preferences: SharedPreferences
12 |
13 | fun init(context: Context) {
14 | val masterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
15 | .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
16 | .build()
17 |
18 | preferences = EncryptedSharedPreferences.create(
19 | context,
20 | "secret_shared_prefs",
21 | masterKey,
22 | EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
23 | EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
24 | )
25 | }
26 |
27 | fun getToken(): String? {
28 | return preferences.getString(KEY_TOKEN, null)
29 | }
30 |
31 | fun setToken(token: String) {
32 | preferences.edit().apply {
33 | putString(KEY_TOKEN, token)
34 | apply()
35 | }
36 | }
37 |
38 | fun removeToken() {
39 | preferences.edit().apply {
40 | remove(KEY_TOKEN)
41 | apply()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun RocketReserverTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/final/app/src/main/kotlin/com/example/rocketreserver/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/final/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/final/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/final/app/src/main/res/drawable/ic_placeholder.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/final/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
--------------------------------------------------------------------------------
/final/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Rocket Reserver
3 |
--------------------------------------------------------------------------------
/final/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/final/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/final/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/final/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/final/app/src/test/java/com/example/rocketreserver/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/final/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.jetbrains.kotlin.android) apply false
5 | alias(libs.plugins.compose.compiler) apply false
6 | }
7 |
--------------------------------------------------------------------------------
/final/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. For more details, visit
16 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
17 | # org.gradle.parallel=true
18 |
19 | # AndroidX package structure to make it clearer which packages are bundled with the
20 | # Android operating system, and which are packaged with your app's APK
21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
22 | android.useAndroidX=true
23 | # Kotlin code style for this project: "official" or "obsolete":
24 | kotlin.code.style=official
25 |
26 | # Enables namespacing of each library's R class so that its R class includes only the
27 | # resources declared in the library itself and none from the library's dependencies,
28 | # thereby reducing the size of the R class for that library
29 | android.nonTransitiveRClass=true
30 |
--------------------------------------------------------------------------------
/final/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.4.1"
3 | kotlin = "2.0.0"
4 | coreKtx = "1.13.1"
5 | junit = "5.0-SNAPSHOT"
6 | androidxTestJunit = "1.1.5"
7 | espressoCore = "3.5.1"
8 | lifecycleRuntimeKtx = "2.8.0"
9 | activityCompose = "1.9.0"
10 | composeBom = "2024.05.00"
11 | navigationCompose = "2.7.7"
12 | securityCrypto = "1.1.0-alpha06"
13 | coil = "2.6.0"
14 | apollo = "4.0.1"
15 |
16 | [libraries]
17 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
18 | junit = { group = "junit", name = "junit", version.ref = "junit" }
19 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxTestJunit" }
20 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
21 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
22 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
23 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
24 | androidx-ui = { group = "androidx.compose.ui", name = "ui" }
25 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
26 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
27 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
28 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
29 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
30 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
31 | androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
32 | androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" }
33 | coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
34 | apollo-runtime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo"}
35 |
36 | [plugins]
37 | android-application = { id = "com.android.application", version.ref = "agp" }
38 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
39 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
40 | apollo = { id = "com.apollographql.apollo", version.ref = "apollo" }
41 |
--------------------------------------------------------------------------------
/final/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/final/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/final/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/final/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/final/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo. 1>&2
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 | echo. 1>&2
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 | echo location of your Java installation. 1>&2
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo. 1>&2
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 | echo. 1>&2
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 | echo location of your Java installation. 1>&2
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/final/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | val javaVersion: String = System.getProperty("java.version")
2 | if (javaVersion.substringBefore(".").toInt() < 11) {
3 | throw GradleException("Java 11 or higher is required to build this project. You are using Java $javaVersion.")
4 | }
5 |
6 | pluginManagement {
7 | repositories {
8 | google {
9 | content {
10 | includeGroupByRegex("com\\.android.*")
11 | includeGroupByRegex("com\\.google.*")
12 | includeGroupByRegex("androidx.*")
13 | }
14 | }
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 | }
19 |
20 | dependencyResolutionManagement {
21 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
22 | repositories {
23 | google()
24 | mavenCentral()
25 | }
26 | }
27 |
28 | rootProject.name = "Rocket Reserver"
29 | include(":app")
30 |
--------------------------------------------------------------------------------
/start/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .gradle
3 | .DS_Store
4 | build
5 | captures
6 | .externalNativeBuild
7 | .cxx
8 | local.properties
9 |
--------------------------------------------------------------------------------
/start/README.md:
--------------------------------------------------------------------------------
1 | # Apollo Kotlin Tutorial
2 |
3 | Repository for the [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin) tutorial.
4 |
5 | This is the starter project with the boilerplate and UI code already written but no Apollo Kotlin code.
6 |
--------------------------------------------------------------------------------
/start/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.compose.compiler)
5 | }
6 |
7 | android {
8 | namespace = "com.example.rocketreserver"
9 | compileSdk = 34
10 |
11 | defaultConfig {
12 | applicationId = "com.example.rocketreserver"
13 | minSdk = 24
14 | targetSdk = 34
15 | versionCode = 1
16 | versionName = "1.0"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | vectorDrawables {
20 | useSupportLibrary = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | isMinifyEnabled = false
27 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
28 | }
29 | }
30 | compileOptions {
31 | sourceCompatibility = JavaVersion.VERSION_1_8
32 | targetCompatibility = JavaVersion.VERSION_1_8
33 | }
34 | kotlinOptions {
35 | jvmTarget = "1.8"
36 | }
37 | buildFeatures {
38 | compose = true
39 | }
40 | packaging {
41 | resources {
42 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
43 | }
44 | }
45 | }
46 |
47 | dependencies {
48 | implementation(libs.androidx.core.ktx)
49 | implementation(libs.androidx.lifecycle.runtime.ktx)
50 | implementation(libs.androidx.activity.compose)
51 | implementation(platform(libs.androidx.compose.bom))
52 | implementation(libs.androidx.ui)
53 | implementation(libs.androidx.ui.graphics)
54 | implementation(libs.androidx.ui.tooling.preview)
55 | implementation(libs.androidx.material3)
56 | implementation(libs.androidx.navigation.compose)
57 | implementation(libs.androidx.security.crypto)
58 | implementation(libs.coil.compose)
59 |
60 |
61 | testImplementation(libs.junit)
62 | androidTestImplementation(libs.androidx.junit)
63 | androidTestImplementation(libs.androidx.espresso.core)
64 | androidTestImplementation(platform(libs.androidx.compose.bom))
65 | androidTestImplementation(libs.androidx.ui.test.junit4)
66 | debugImplementation(libs.androidx.ui.tooling)
67 | debugImplementation(libs.androidx.ui.test.manifest)
68 | }
69 |
--------------------------------------------------------------------------------
/start/app/proguard-rules.pro:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/start/app/src/androidTest/java/com/example/rocketreserver/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
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.rocketreserver", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/start/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/LaunchDetails.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.Spacer
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.fillMaxWidth
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.layout.size
12 | import androidx.compose.material3.Button
13 | import androidx.compose.material3.CircularProgressIndicator
14 | import androidx.compose.material3.LocalContentColor
15 | import androidx.compose.material3.MaterialTheme
16 | import androidx.compose.material3.Text
17 | import androidx.compose.runtime.Composable
18 | import androidx.compose.ui.Alignment
19 | import androidx.compose.ui.Modifier
20 | import androidx.compose.ui.res.painterResource
21 | import androidx.compose.ui.tooling.preview.Preview
22 | import androidx.compose.ui.unit.dp
23 |
24 | @Composable
25 | fun LaunchDetails(launchId: String) {
26 | Column(
27 | modifier = Modifier
28 | .fillMaxSize()
29 | .padding(16.dp)
30 | ) {
31 | Row(verticalAlignment = Alignment.CenterVertically) {
32 | // Mission patch
33 | Image(
34 | modifier = Modifier.size(160.dp, 160.dp),
35 | painter = painterResource(R.drawable.ic_placeholder),
36 | contentDescription = "Mission patch"
37 | )
38 |
39 | Spacer(modifier = Modifier.size(16.dp))
40 |
41 | Column {
42 | // Mission name
43 | Text(
44 | style = MaterialTheme.typography.headlineMedium,
45 | text = "Launch $launchId"
46 | )
47 |
48 | // Rocket name
49 | Text(
50 | modifier = Modifier.padding(top = 8.dp),
51 | style = MaterialTheme.typography.headlineSmall,
52 | text = "Rocket name",
53 | )
54 |
55 | // Site
56 | Text(
57 | modifier = Modifier.padding(top = 8.dp),
58 | style = MaterialTheme.typography.titleMedium,
59 | text = "Site..."
60 | )
61 | }
62 | }
63 |
64 | // Book button
65 | Button(
66 | modifier = Modifier
67 | .padding(top = 32.dp)
68 | .fillMaxWidth(),
69 | onClick = { /*TODO*/ }
70 | ) {
71 | Text(text = "Book now")
72 | }
73 | }
74 | }
75 |
76 | @Composable
77 | private fun ErrorMessage(text: String) {
78 | Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
79 | Text(text = text)
80 | }
81 | }
82 |
83 | @Composable
84 | private fun Loading() {
85 | Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
86 | CircularProgressIndicator()
87 | }
88 | }
89 |
90 | @Composable
91 | private fun SmallLoading() {
92 | CircularProgressIndicator(
93 | modifier = Modifier.size(24.dp),
94 | color = LocalContentColor.current,
95 | strokeWidth = 2.dp,
96 | )
97 | }
98 |
99 | @Preview(showBackground = true)
100 | @Composable
101 | private fun LaunchDetailsPreview() {
102 | LaunchDetails(launchId = "42")
103 | }
104 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/LaunchList.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | package com.example.rocketreserver
4 |
5 | import androidx.compose.foundation.Image
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.Box
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.fillMaxWidth
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.layout.size
12 | import androidx.compose.foundation.lazy.LazyColumn
13 | import androidx.compose.material3.CircularProgressIndicator
14 | import androidx.compose.material3.ExperimentalMaterial3Api
15 | import androidx.compose.material3.ListItem
16 | import androidx.compose.material3.Text
17 | import androidx.compose.runtime.Composable
18 | import androidx.compose.ui.Alignment
19 | import androidx.compose.ui.Modifier
20 | import androidx.compose.ui.res.painterResource
21 | import androidx.compose.ui.unit.dp
22 |
23 | @Composable
24 | fun LaunchList(onLaunchClick: (launchId: String) -> Unit) {
25 | LazyColumn(modifier = Modifier.fillMaxSize()) {
26 | items(20) {
27 | LaunchItem(launchId = it.toString(), onClick = onLaunchClick)
28 | }
29 | }
30 | }
31 |
32 | @Composable
33 | private fun LaunchItem(launchId: String, onClick: (launchId: String) -> Unit) {
34 | ListItem(
35 | modifier = Modifier.clickable { onClick(launchId) },
36 | headlineContent = {
37 | // Mission name
38 | Text(text = "Launch $launchId")
39 | },
40 | supportingContent = {
41 | // Site
42 | Text(text = "Site...")
43 | },
44 | leadingContent = {
45 | // Mission patch
46 | Image(
47 | modifier = Modifier.size(68.dp, 68.dp),
48 | painter = painterResource(R.drawable.ic_placeholder),
49 | contentDescription = "Mission patch"
50 | )
51 | }
52 | )
53 | }
54 |
55 | @Composable
56 | private fun LoadingItem() {
57 | Box(
58 | contentAlignment = Alignment.Center,
59 | modifier = Modifier
60 | .fillMaxWidth()
61 | .padding(16.dp)
62 | ) {
63 | CircularProgressIndicator()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/Login.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | package com.example.rocketreserver
4 |
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.foundation.layout.fillMaxWidth
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.foundation.layout.size
10 | import androidx.compose.foundation.text.KeyboardOptions
11 | import androidx.compose.material3.Button
12 | import androidx.compose.material3.CircularProgressIndicator
13 | import androidx.compose.material3.ExperimentalMaterial3Api
14 | import androidx.compose.material3.LocalContentColor
15 | import androidx.compose.material3.MaterialTheme
16 | import androidx.compose.material3.OutlinedTextField
17 | import androidx.compose.material3.Text
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.runtime.mutableStateOf
21 | import androidx.compose.runtime.remember
22 | import androidx.compose.runtime.setValue
23 | import androidx.compose.ui.Modifier
24 | import androidx.compose.ui.text.input.KeyboardType
25 | import androidx.compose.ui.text.style.TextAlign
26 | import androidx.compose.ui.tooling.preview.Preview
27 | import androidx.compose.ui.unit.dp
28 |
29 | @Composable
30 | fun Login() {
31 | Column(
32 | modifier = Modifier
33 | .fillMaxSize()
34 | .padding(16.dp)
35 | ) {
36 | // Title
37 | Text(
38 | modifier = Modifier.fillMaxWidth(),
39 | textAlign = TextAlign.Center,
40 | style = MaterialTheme.typography.headlineMedium,
41 | text = "Login"
42 | )
43 |
44 | // Email
45 | var email by remember { mutableStateOf("") }
46 | OutlinedTextField(
47 | modifier = Modifier
48 | .padding(top = 16.dp)
49 | .fillMaxWidth(),
50 | label = { Text("Email") },
51 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
52 | value = email,
53 | onValueChange = { email = it }
54 | )
55 |
56 | // Submit button
57 | Button(
58 | modifier = Modifier
59 | .padding(top = 32.dp)
60 | .fillMaxWidth(),
61 | onClick = { /*TODO*/ }
62 | ) {
63 | Text(text = "Submit")
64 | }
65 | }
66 | }
67 |
68 | @Composable
69 | private fun Loading() {
70 | CircularProgressIndicator(
71 | modifier = Modifier.size(24.dp),
72 | color = LocalContentColor.current,
73 | strokeWidth = 2.dp,
74 | )
75 | }
76 |
77 | @Preview(showBackground = true)
78 | @Composable
79 | private fun LoginPreview() {
80 | Login()
81 | }
82 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/MainActivity.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | package com.example.rocketreserver
4 |
5 | import android.os.Bundle
6 | import androidx.activity.ComponentActivity
7 | import androidx.activity.compose.setContent
8 | import androidx.compose.foundation.layout.Box
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.material3.ExperimentalMaterial3Api
11 | import androidx.compose.material3.Scaffold
12 | import androidx.compose.material3.Text
13 | import androidx.compose.material3.TopAppBar
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.res.stringResource
17 | import androidx.navigation.compose.NavHost
18 | import androidx.navigation.compose.composable
19 | import androidx.navigation.compose.rememberNavController
20 | import com.example.rocketreserver.ui.theme.RocketReserverTheme
21 |
22 | class MainActivity : ComponentActivity() {
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 | TokenRepository.init(this)
26 | setContent {
27 | RocketReserverTheme {
28 | Scaffold(topBar = { TopAppBar({ Text(stringResource(R.string.app_name)) }) }) { paddingValues ->
29 | Box(Modifier.padding(paddingValues)) {
30 | MainNavHost()
31 | }
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
38 | @Composable
39 | private fun MainNavHost() {
40 | val navController = rememberNavController()
41 | NavHost(navController, startDestination = NavigationDestinations.LAUNCH_LIST) {
42 | composable(route = NavigationDestinations.LAUNCH_LIST) {
43 | LaunchList(
44 | onLaunchClick = { launchId ->
45 | navController.navigate("${NavigationDestinations.LAUNCH_DETAILS}/$launchId")
46 | }
47 | )
48 | }
49 |
50 | composable(route = "${NavigationDestinations.LAUNCH_DETAILS}/{${NavigationArguments.LAUNCH_ID}}") { navBackStackEntry ->
51 | LaunchDetails(launchId = navBackStackEntry.arguments!!.getString(NavigationArguments.LAUNCH_ID)!!)
52 | }
53 |
54 | composable(route = NavigationDestinations.LOGIN) {
55 | Login()
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/Navigation.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | object NavigationDestinations {
4 | const val LAUNCH_LIST = "launchList"
5 | const val LAUNCH_DETAILS = "launchDetails"
6 | const val LOGIN = "login"
7 | }
8 |
9 | object NavigationArguments {
10 | const val LAUNCH_ID = "launchId"
11 | }
12 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/TokenRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.security.crypto.EncryptedSharedPreferences
6 | import androidx.security.crypto.MasterKey
7 |
8 | object TokenRepository {
9 | private const val KEY_TOKEN = "TOKEN"
10 |
11 | private lateinit var preferences: SharedPreferences
12 |
13 | fun init(context: Context) {
14 | val masterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
15 | .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
16 | .build()
17 |
18 | preferences = EncryptedSharedPreferences.create(
19 | context,
20 | "secret_shared_prefs",
21 | masterKey,
22 | EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
23 | EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
24 | )
25 | }
26 |
27 | fun getToken(): String? {
28 | return preferences.getString(KEY_TOKEN, null)
29 | }
30 |
31 | fun setToken(token: String) {
32 | preferences.edit().apply {
33 | putString(KEY_TOKEN, token)
34 | apply()
35 | }
36 | }
37 |
38 | fun removeToken() {
39 | preferences.edit().apply {
40 | remove(KEY_TOKEN)
41 | apply()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun RocketReserverTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/start/app/src/main/kotlin/com/example/rocketreserver/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/start/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/start/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/start/app/src/main/res/drawable/ic_placeholder.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/start/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
--------------------------------------------------------------------------------
/start/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Rocket Reserver
3 |
--------------------------------------------------------------------------------
/start/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/start/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/start/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/start/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/start/app/src/test/java/com/example/rocketreserver/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.rocketreserver
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/start/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.jetbrains.kotlin.android) apply false
5 | alias(libs.plugins.compose.compiler) apply false
6 | }
7 |
--------------------------------------------------------------------------------
/start/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. For more details, visit
16 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
17 | # org.gradle.parallel=true
18 |
19 | # AndroidX package structure to make it clearer which packages are bundled with the
20 | # Android operating system, and which are packaged with your app's APK
21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
22 | android.useAndroidX=true
23 | # Kotlin code style for this project: "official" or "obsolete":
24 | kotlin.code.style=official
25 |
26 | # Enables namespacing of each library's R class so that its R class includes only the
27 | # resources declared in the library itself and none from the library's dependencies,
28 | # thereby reducing the size of the R class for that library
29 | android.nonTransitiveRClass=true
30 |
--------------------------------------------------------------------------------
/start/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.4.1"
3 | kotlin = "2.0.0"
4 | coreKtx = "1.13.1"
5 | junit = "5.0-SNAPSHOT"
6 | androidxTestJunit = "1.1.5"
7 | espressoCore = "3.5.1"
8 | lifecycleRuntimeKtx = "2.8.0"
9 | activityCompose = "1.9.0"
10 | composeBom = "2024.05.00"
11 | navigationCompose = "2.7.7"
12 | securityCrypto = "1.1.0-alpha06"
13 | coil = "2.6.0"
14 |
15 | [libraries]
16 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
17 | junit = { group = "junit", name = "junit", version.ref = "junit" }
18 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxTestJunit" }
19 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
20 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
21 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
22 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
23 | androidx-ui = { group = "androidx.compose.ui", name = "ui" }
24 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
25 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
26 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
27 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
28 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
29 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
30 | androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
31 | androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" }
32 | coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
33 |
34 | [plugins]
35 | android-application = { id = "com.android.application", version.ref = "agp" }
36 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
37 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
38 |
39 |
--------------------------------------------------------------------------------
/start/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-kotlin-tutorial/3fa8d8f47e2e12406ce9c3019fd0f768258f8e2c/start/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/start/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/start/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/start/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo. 1>&2
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 | echo. 1>&2
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 | echo location of your Java installation. 1>&2
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo. 1>&2
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 | echo. 1>&2
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 | echo location of your Java installation. 1>&2
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/start/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | val javaVersion: String = System.getProperty("java.version")
2 | if (javaVersion.substringBefore(".").toInt() < 11) {
3 | throw GradleException("Java 11 or higher is required to build this project. You are using Java $javaVersion.")
4 | }
5 |
6 | pluginManagement {
7 | repositories {
8 | google {
9 | content {
10 | includeGroupByRegex("com\\.android.*")
11 | includeGroupByRegex("com\\.google.*")
12 | includeGroupByRegex("androidx.*")
13 | }
14 | }
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 | }
19 |
20 | dependencyResolutionManagement {
21 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
22 | repositories {
23 | google()
24 | mavenCentral()
25 | }
26 | }
27 |
28 | rootProject.name = "Rocket Reserver"
29 | include(":app")
30 |
--------------------------------------------------------------------------------