Initial commit

This commit is contained in:
2025-06-12 23:20:21 +02:00
parent 1656e706a0
commit 714cdb6795
122 changed files with 3335 additions and 916 deletions

1
app/.gitignore vendored
View File

@ -1 +0,0 @@
/build

View File

@ -1,76 +1,71 @@
import java.io.FileInputStream
import java.util.Properties
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
id("com.google.devtools.ksp")
id("com.google.dagger.hilt.android")
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.flights.android.application.compose)
alias(libs.plugins.flights.android.application.hilt)
}
android {
namespace = "com.ryanair.androidchallenge"
compileSdk = 35
namespace = "dev.adriankuta.flights"
defaultConfig {
applicationId = "com.ryanair.androidchallenge"
versionCode = 1
versionName = "1.0"
multiDexEnabled = true
minSdk = 28
targetSdk = 35
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
applicationId = "dev.adriankuta.flights"
versionCode = 10
versionName = "0.0.1-${versionCode}"
//signingConfig = signingConfigs.getByName("debug")
}
/*val keystorePropertiesFile = rootProject.file("keystore.properties")
val keystoreProperties = Properties()
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
}
}*/
buildTypes {
getByName("debug") {
debug {
signingConfig = signingConfigs.getByName("debug")
isDebuggable = true
}
release {
//signingConfig = signingConfigs.getByName("release")
isDebuggable = true
isMinifyEnabled = false
isShrinkResources = false
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
buildFeatures {
viewBinding = true
compose = true
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
val composeBom = platform("androidx.compose:compose-bom:2025.04.01")
implementation(composeBom)
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-core")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation(projects.model.repository)
implementation(projects.model.data.room)
implementation(projects.model.data.simple)
implementation("androidx.activity:activity-compose:1.10.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0")
implementation(projects.ui.designsystem)
implementation(projects.ui.home)
implementation("androidx.core:core-ktx:1.16.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.0")
// Dependency Injection
implementation("com.google.dagger:hilt-android:2.56.2")
ksp("com.google.dagger:hilt-compiler:2.56.2")
// Network
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2")
implementation("com.squareup.moshi:moshi:1.15.2")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
}
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.app.update.ktx)
}

View File

@ -0,0 +1,33 @@
# Exceptions for compose. See https://detekt.dev/docs/introduction/compose
naming:
FunctionNaming:
functionPattern: '[a-zA-Z][a-zA-Z0-9]*'
TopLevelPropertyNaming:
constantPattern: '[A-Z][A-Za-z0-9]*'
complexity:
LongParameterList:
ignoreAnnotated: [ 'Composable' ]
TooManyFunctions:
ignoreAnnotatedFunctions: [ 'Preview' ]
style:
MagicNumber:
ignorePropertyDeclaration: true
ignoreCompanionObjectPropertyDeclaration: true
ignoreAnnotated: [ 'Composable' ]
UnusedPrivateMember:
ignoreAnnotated: [ 'Composable' ]
# Deviations from defaults
formatting:
TrailingCommaOnCallSite:
active: true
autoCorrect: true
useTrailingCommaOnCallSite: true
TrailingCommaOnDeclarationSite:
active: true
autoCorrect: true
useTrailingCommaOnDeclarationSite: true

205
app/lint-baseline.xml Normal file
View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.10.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.10.1)" variant="all" version="8.10.1">
<issue
id="UnusedAttribute"
message="Attribute `endX` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:endX=&quot;85.84757&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="10"
column="17"/>
</issue>
<issue
id="UnusedAttribute"
message="Attribute `endY` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:endY=&quot;92.4963&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="11"
column="17"/>
</issue>
<issue
id="UnusedAttribute"
message="Attribute `startX` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:startX=&quot;42.9492&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="12"
column="17"/>
</issue>
<issue
id="UnusedAttribute"
message="Attribute `startY` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:startY=&quot;49.59793&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="13"
column="17"/>
</issue>
<issue
id="UnusedAttribute"
message="Attribute `offset` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:offset=&quot;0.0&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="17"
column="21"/>
</issue>
<issue
id="UnusedAttribute"
message="Attribute `offset` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:offset=&quot;1.0&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="20"
column="21"/>
</issue>
<issue
id="UnusedAttribute"
message="Attribute `fillType` is only used in API level 24 and higher (current min is 21)"
errorLine1=" android:fillType=&quot;nonZero&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="26"
column="9"/>
</issue>
<issue
id="Aligned16KB"
message="The native library `arm64-v8a/libmockkjvmtiagent.so` (from `io.mockk:mockk-agent-android:1.14.2`) is not 16 KB aligned">
<location
file="$GRADLE_USER_HOME/caches/8.11.1/transforms/9ee3fe20033b4dd897c7dfcf7c303d16/transformed/mockk-agent-android-1.14.2/jni/arm64-v8a/libmockkjvmtiagent.so"/>
</issue>
<issue
id="Aligned16KB"
message="The native library `arm64-v8a/libmockkjvmtiagent.so` (from `io.mockk:mockk-agent-android:1.14.2`) is not 16 KB aligned">
<location
file="$GRADLE_USER_HOME/caches/8.11.1/transforms/9ee3fe20033b4dd897c7dfcf7c303d16/transformed/mockk-agent-android-1.14.2/jni/arm64-v8a/libmockkjvmtiagent.so"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.purple_200` appears to be unused"
errorLine1=" &lt;color name=&quot;purple_200&quot;>#FFBB86FC&lt;/color>"
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="3"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.purple_500` appears to be unused"
errorLine1=" &lt;color name=&quot;purple_500&quot;>#FF6200EE&lt;/color>"
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="4"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.purple_700` appears to be unused"
errorLine1=" &lt;color name=&quot;purple_700&quot;>#FF3700B3&lt;/color>"
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="5"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.teal_200` appears to be unused"
errorLine1=" &lt;color name=&quot;teal_200&quot;>#FF03DAC5&lt;/color>"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="6"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.teal_700` appears to be unused"
errorLine1=" &lt;color name=&quot;teal_700&quot;>#FF018786&lt;/color>"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="7"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.black` appears to be unused"
errorLine1=" &lt;color name=&quot;black&quot;>#FF000000&lt;/color>"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="8"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.color.white` appears to be unused"
errorLine1=" &lt;color name=&quot;white&quot;>#FFFFFFFF&lt;/color>"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/res/values/colors.xml"
line="9"
column="12"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.drawable.ic_launcher_foreground` appears to be unused"
errorLine1="&lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
errorLine2="^">
<location
file="src/main/res/drawable/ic_launcher_foreground.xml"
line="1"
column="1"/>
</issue>
<issue
id="MonochromeLauncherIcon"
message="The application adaptive icon is missing a monochrome tag"
errorLine1="&lt;adaptive-icon xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;>"
errorLine2="^">
<location
file="src/main/res/mipmap-anydpi-v26/ic_launcher.xml"
line="2"
column="1"/>
</issue>
<issue
id="MonochromeLauncherIcon"
message="The application adaptive roundIcon is missing a monochrome tag"
errorLine1="&lt;adaptive-icon xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;>"
errorLine2="^">
<location
file="src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml"
line="2"
column="1"/>
</issue>
</issues>

View File

@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
@ -18,4 +18,7 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-keep class dev.adriankuta.flights.data.dto.* { *; }
-keep class dev.adriankuta.flights.data.model.* { *; }

View File

@ -0,0 +1,15 @@
package dev.adriankuta.flights
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber
import timber.log.Timber.DebugTree
@HiltAndroidApp
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Timber.plant(DebugTree())
}
}

View File

@ -1,21 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ryanair.androidchallenge">
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".core.App"
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight">
android:theme="@style/Theme.Flights"
tools:targetApi="33">
<activity
android:name=".ui.MainActivity"
android:exported="true">
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.Flights">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
</manifest>

View File

@ -0,0 +1,28 @@
Adam Małysz
Kubuś Puchatek
Prosiaczek
Harry Potter
SpongeBob
Miś Uszatek
Patryk (SpongeBob)
Pan krab (SpongeBob)
Brzydkie kaczątko
Alvin i Wiewiórki
Szymon (Alvin i wiewiórki)
Papa Smerf
Smerfetka
Gargamel (smerfy)
Ważniak (smerfy)
Roszpunka
Myszka Miki
Król Lew
Stitch
Elsa (kraina lodu)
Anna (kraina lodu)
Olaf (kraina lodu)
Mała syrenka
Tygrys (Kubuś Puchatek)
Kłapouchy (Kubuś Puchatek)
Maleństwo (Kubuś Puchatek)
Królik (Kubuś Puchatek)
Krzyś (Kubuś Puchatek)

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 KiB

View File

@ -1,57 +0,0 @@
package com.ryanair.androidchallenge.di
import com.ryanair.androidchallenge.repository.airports.api.AirportService
import com.ryanair.androidchallenge.repository.airports.api.RoutesService
import com.ryanair.androidchallenge.repository.flights.api.FlightService
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {
@Singleton
@Provides
fun provideMoshi() = Moshi.Builder().build()
@Singleton
@Provides
fun provideHttpLoggingInterceptor() = HttpLoggingInterceptor().apply {
setLevel(HttpLoggingInterceptor.Level.BODY)
}
@Singleton
@Provides
fun providesOkHttpClient(loggingInterceptor: HttpLoggingInterceptor) =
OkHttpClient.Builder().also { builder ->
builder.addInterceptor(loggingInterceptor)
}.build()
@Singleton
@Provides
fun provideRetrofit(client: OkHttpClient, moshi: Moshi): Retrofit = Retrofit.Builder()
// TODO("add URL provided in the task instructions")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(client)
.build()
@Singleton
@Provides
fun provideAirportService(retrofit: Retrofit): AirportService = retrofit.create(AirportService::class.java)
@Singleton
@Provides
fun provideRoutesService(retrofit: Retrofit): RoutesService = retrofit.create(RoutesService::class.java)
@Singleton
@Provides
fun provideFlightService(retrofit: Retrofit): FlightService = retrofit.create(FlightService::class.java)
}

View File

@ -1,12 +0,0 @@
package com.ryanair.androidchallenge.repository.airports.api
import com.ryanair.androidchallenge.repository.airports.model.AirportResponse
import retrofit2.http.GET
import retrofit2.http.Path
interface AirportService {
@GET("/views/locate/5/airports/{language}/active")
suspend fun getAirports(@Path("language") languageCode: String): List<AirportResponse>
}

View File

@ -1,19 +0,0 @@
package com.ryanair.androidchallenge.repository.airports.api
import com.ryanair.androidchallenge.repository.airports.model.RouteResponse
import retrofit2.http.GET
import retrofit2.http.Path
interface RoutesService {
@GET("/views/locate/5/routes/{language}")
suspend fun getAllRoutes(
@Path("language") languageCode: String,
): List<RouteResponse>
@GET("/views/locate/5/routes/{language}/airport/{departure}")
suspend fun getRoutes(
@Path("language") languageCode: String,
@Path("departure") departureAirportCode: String,
): List<RouteResponse>
}

View File

@ -1,51 +0,0 @@
package com.ryanair.androidchallenge.repository.airports.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class AirportResponse(
@Json(name = "code") val code: String?,
@Json(name = "name") val name: String?,
@Json(name = "seoName") val seoName: String?,
@Json(name = "base") val isBase: Boolean?,
@Json(name = "timeZone") val timeZone: String?,
@Json(name = "city") val city: City?,
@Json(name = "macCity") val macCity: MacCity?,
@Json(name = "region") val region: Region?,
@Json(name = "country") val country: Country?,
@Json(name = "coordinates") val coordinates: Coordinates?
) {
@JsonClass(generateAdapter = true)
data class City(
@Json(name = "code") val code: String?,
@Json(name = "name") val name: String?
)
@JsonClass(generateAdapter = true)
data class MacCity(
@Json(name = "code") val code: String?,
@Json(name = "macCode") val macCode: String?,
@Json(name = "name") val name: String?
)
@JsonClass(generateAdapter = true)
data class Region(
@Json(name = "code") val code: String?,
@Json(name = "name") val name: String?
)
@JsonClass(generateAdapter = true)
data class Country(
@Json(name = "code") val code: String?,
@Json(name = "name") val name: String?,
@Json(name = "currency") val currencyCode: String?
)
@JsonClass(generateAdapter = true)
data class Coordinates(
@Json(name = "latitude") val latitude: Double?,
@Json(name = "longitude") val longitude: Double?
)
}

View File

@ -1,50 +0,0 @@
package com.ryanair.androidchallenge.repository.airports.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RouteResponse(
@Json(name = "departureAirport") val departureAirport: Airport.Departure?,
@Json(name = "arrivalAirport") val arrivalAirport: Airport.Arrival?,
@Json(name = "connectingAirport") val connectingAirport: Airport.Connecting?
) {
sealed interface Airport {
@Json(name = "code")
val code: String?
@Json(name = "name")
val name: String?
@Json(name = "macCity")
val macCity: MacCity?
@JsonClass(generateAdapter = true)
data class Departure(
override val code: String?,
override val name: String?,
override val macCity: MacCity?,
) : Airport
@JsonClass(generateAdapter = true)
data class Arrival(
override val code: String?,
override val name: String?,
override val macCity: MacCity?
) : Airport
@JsonClass(generateAdapter = true)
data class Connecting(
override val code: String?,
override val name: String?,
override val macCity: MacCity?
) : Airport
@JsonClass(generateAdapter = true)
data class MacCity(
@Json(name = "macCode") val macCode: String?
)
}
}

View File

@ -1,26 +0,0 @@
package com.ryanair.androidchallenge.repository.flights.api
import com.ryanair.androidchallenge.repository.flights.model.FlightResponse
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.Query
interface FlightService {
// example query: v4/en-gb/Availability?dateout=2025-09-17&origin=WRO&destination=DUB&adt=1&ToUs=AGREED
@Headers(
"client: android",
"client-version: 3.300.0"
)
@GET("v4/en-gb/Availability")
suspend fun getFlights(
@Query("dateout") date: String,
@Query("origin") origin: String,
@Query("destination") destination: String,
@Query("adt") adult: Int,
@Query("teen") teen: Int,
@Query("chd") child: Int,
@Query("inf") inf: Int = 0,
@Query("ToUs") toUs: String = "AGREED"
): FlightResponse
}

View File

@ -1,56 +0,0 @@
package com.ryanair.androidchallenge.repository.flights.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class FlightResponse(
@Json(name = "currency") val currency: String?,
@Json(name = "currPrecision") val currPrecision: Int?,
@Json(name = "trips") val trips: List<Trip>?
)
@JsonClass(generateAdapter = true)
data class Trip(
@Json(name = "dates") val dates: List<TripDate>?,
@Json(name = "origin") val origin: String?,
@Json(name = "destination") val destination: String?
)
@JsonClass(generateAdapter = true)
data class TripDate(
@Json(name = "dateOut") val dateOut: String?,
@Json(name = "flights") val flights: List<TripFlight>?
)
@JsonClass(generateAdapter = true)
data class TripFlight(
@Json(name = "faresLeft") val faresLeft: Int?,
@Json(name = "regularFare") val regularFare: RegularFare?,
@Json(name = "flightNumber") val flightNumber: String?,
@Json(name = "time") val dateTimes: List<String>?,
@Json(name = "duration") val duration: String?,
@Json(name = "segments") val segments: List<Segment>?,
@Json(name = "operatedBy") val operatedBy: String?,
)
@JsonClass(generateAdapter = true)
data class RegularFare(
@Json(name = "fares") val fares: List<TripFare>?
)
@JsonClass(generateAdapter = true)
data class TripFare(
@Json(name = "type") val type: String?,
@Json(name = "amount") val amount: Double?,
@Json(name = "count") val count: Int?
)
@JsonClass(generateAdapter = true)
data class Segment(
@Json(name = "origin") val origin: String?,
@Json(name = "destination") val destination: String?,
@Json(name = "flightNumber") val flightNumber: String?,
@Json(name = "time") val dateTimes: List<String>?,
@Json(name = "duration") val duration: String?
)

View File

@ -1,21 +0,0 @@
package com.ryanair.androidchallenge.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.ryanair.androidchallenge.ui.search.SearchScreen
import com.ryanair.androidchallenge.ui.theme.AndroidChallengeTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AndroidChallengeTheme {
SearchScreen()
}
}
}
}

View File

@ -1,118 +0,0 @@
package com.ryanair.androidchallenge.ui.search
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.ryanair.androidchallenge.ui.theme.Theme
@Composable
internal fun SearchScreen() {
Column(
modifier = Modifier
.padding(Theme.spacing.spacing16)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
Column(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { },
border = BorderStroke(Theme.spacing.spacing1, Theme.colors.primary),
colors = CardDefaults.cardColors().copy(containerColor = Theme.colors.background)
) {
Box(modifier = Modifier.padding(Theme.spacing.spacing8)) {
Column {
Text(text = "From")
Text(text = "", maxLines = 3)
}
}
}
Spacer(modifier = Modifier.height(Theme.spacing.spacing16))
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { },
border = BorderStroke(Theme.spacing.spacing1, Theme.colors.primary),
colors = CardDefaults.cardColors().copy(containerColor = Theme.colors.background)
) {
Box(modifier = Modifier.padding(Theme.spacing.spacing8)) {
Column {
Text(text = "To")
Text(text = "", maxLines = 3)
}
}
}
Spacer(modifier = Modifier.height(Theme.spacing.spacing16))
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { },
border = BorderStroke(Theme.spacing.spacing1, Theme.colors.primary),
colors = CardDefaults.cardColors().copy(containerColor = Theme.colors.background)
) {
Box(modifier = Modifier.padding(Theme.spacing.spacing8)) {
Column {
Text(text = "Departure date")
Text(text = "", maxLines = 3)
}
}
}
Spacer(modifier = Modifier.height(Theme.spacing.spacing16))
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { },
border = BorderStroke(Theme.spacing.spacing1, Theme.colors.primary),
colors = CardDefaults.cardColors().copy(containerColor = Theme.colors.background)
) {
Box(modifier = Modifier.padding(Theme.spacing.spacing8)) {
Column {
Text(text = "Passengers")
Text(text = "", maxLines = 3)
}
}
}
}
Button(modifier = Modifier
.fillMaxWidth(),
enabled = false,
onClick = { /*TODO*/ }) {
Row {
Icon(
imageVector = Icons.Rounded.Search,
contentDescription = ""
)
Text(text = "Search")
}
}
}
}

View File

@ -1,23 +0,0 @@
package com.ryanair.androidchallenge.ui.theme
import androidx.compose.runtime.Stable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
object Palette {
val Purple80 = Color(0xFFD0BCFF)
val Purple40 = Color(0xFF6650a4)
val White = Color(0xFFFFFFFF)
}
@Stable
data class ColorScheme(
val primary: Color = Palette.Purple80,
val primaryAccent: Color = Palette.Purple40,
val background: Color = Palette.White,
)
val LocalColorScheme = staticCompositionLocalOf { ColorScheme() }

View File

@ -1,37 +0,0 @@
package com.ryanair.androidchallenge.ui.theme
import androidx.compose.runtime.Stable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Stable
data class Spacing(
val spacing0: Dp = 0.dp,
val spacing1: Dp = 1.dp,
val spacing2: Dp = 2.dp,
val spacing4: Dp = 4.dp,
val spacing6: Dp = 6.dp,
val spacing8: Dp = 8.dp,
val spacing10: Dp = 10.dp,
val spacing12: Dp = 12.dp,
val spacing14: Dp = 14.dp,
val spacing16: Dp = 16.dp,
val spacing18: Dp = 18.dp,
val spacing20: Dp = 20.dp,
val spacing22: Dp = 22.dp,
val spacing24: Dp = 24.dp,
val spacing26: Dp = 26.dp,
val spacing28: Dp = 28.dp,
val spacing30: Dp = 30.dp,
val spacing32: Dp = 32.dp,
val spacing36: Dp = 36.dp,
val spacing38: Dp = 38.dp,
val spacing40: Dp = 40.dp,
val spacing42: Dp = 42.dp,
val spacing48: Dp = 48.dp,
val spacing56: Dp = 56.dp,
val spacing66: Dp = 66.dp,
)
val LocalSpacing = staticCompositionLocalOf { Spacing() }

View File

@ -1,38 +0,0 @@
package com.ryanair.androidchallenge.ui.theme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
@Composable
fun AndroidChallengeTheme(
colorScheme: ColorScheme = Theme.colors,
typography: Typography = Theme.typography,
spacing: Spacing = Theme.spacing,
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalColorScheme provides colorScheme,
LocalTypography provides typography,
LocalSpacing provides spacing
) {
content()
}
}
object Theme {
val colors: ColorScheme
@Composable
@ReadOnlyComposable
get() = LocalColorScheme.current
val typography: Typography
@Composable
@ReadOnlyComposable
get() = LocalTypography.current
val spacing: Spacing
@Composable
@ReadOnlyComposable
get() = LocalSpacing.current
}

View File

@ -1,51 +0,0 @@
package com.ryanair.androidchallenge.ui.theme
import androidx.compose.runtime.Stable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
@Stable
data class Typography(
val titleSmall: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
lineHeight = 20.sp,
),
val titleLarge: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
lineHeight = 20.sp,
),
val subtitleSmall: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
lineHeight = 16.sp,
),
val subtitleLarge: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
lineHeight = 20.sp,
),
val bodySmall: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontSize = 10.sp,
fontWeight = FontWeight.Normal,
lineHeight = 12.sp,
),
val bodyLarge: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
lineHeight = 20.sp,
)
)
val LocalTypography = staticCompositionLocalOf { Typography() }

View File

@ -0,0 +1,23 @@
package dev.adriankuta.flights
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import dev.adriankuta.flights.ui.home.navigation.HomeRoute
import dev.adriankuta.flights.ui.home.navigation.homeScreen
@Composable
fun FlightsNavGraph(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
) {
NavHost(
navController = navController,
startDestination = HomeRoute,
modifier = modifier,
) {
homeScreen()
}
}

View File

@ -0,0 +1,85 @@
package dev.adriankuta.flights
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.adriankuta.flights.ui.FlightsApp
import dev.adriankuta.flights.ui.designsystem.theme.FlightsTheme
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
var isLoading: Boolean by mutableStateOf(true)
lifecycleScope.launch {
@Suppress("MagicNumber")
delay(500)
isLoading = false
}
splashScreen.setKeepOnScreenCondition {
isLoading
}
enableEdgeToEdge()
setContent {
val darkTheme = shouldUseDarkTheme()
// Update the edge to edge configuration to match the theme
// This is the same parameters as the default enableEdgeToEdge call, but we manually
// resolve whether or not to show dark theme using uiState, since it can be different
// than the configuration's dark theme value based on the user preference.
DisposableEffect(darkTheme) {
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(
android.graphics.Color.TRANSPARENT,
android.graphics.Color.TRANSPARENT,
) { darkTheme },
navigationBarStyle = SystemBarStyle.auto(
lightScrim,
darkScrim,
) { darkTheme },
)
onDispose {}
}
FlightsTheme {
FlightsApp()
}
}
}
}
/**
* Returns `true` if dark theme should be used, as a function of the [uiState] and the
* current system context.
*/
@Composable
private fun shouldUseDarkTheme(): Boolean = isSystemInDarkTheme()
/**
* The default light scrim, as defined by androidx and the platform:
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=35-38;drc=27e7d52e8604a080133e8b842db10c89b4482598
*/
private val lightScrim = android.graphics.Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
/**
* The default dark scrim, as defined by androidx and the platform:
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=40-44;drc=27e7d52e8604a080133e8b842db10c89b4482598
*/
private val darkScrim = android.graphics.Color.argb(0x80, 0x1b, 0x1b, 0x1b)

View File

@ -0,0 +1,25 @@
package dev.adriankuta.flights.ui
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import dev.adriankuta.flights.FlightsNavGraph
import dev.adriankuta.flights.ui.designsystem.theme.Elevation
@Composable
fun FlightsApp(
modifier: Modifier = Modifier,
) {
Surface(
tonalElevation = Elevation.Surface,
modifier = modifier,
) {
Scaffold(
snackbarHost = { InAppUpdates() },
) { paddingValues ->
FlightsNavGraph(Modifier.padding(paddingValues))
}
}
}

View File

@ -0,0 +1,93 @@
package dev.adriankuta.flights.ui
import androidx.activity.compose.LocalActivity
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.InstallException
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.coroutines.suspendCoroutine
@Composable
fun InAppUpdates(
modifier: Modifier = Modifier,
@AppUpdateType updateType: Int = AppUpdateType.FLEXIBLE,
) {
val activity = LocalActivity.current ?: return
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val appUpdateManager = remember { AppUpdateManagerFactory.create(requireNotNull(activity)) }
val flexibleUpdateListener = remember {
InstallStateUpdatedListener { state ->
if (state.installStatus() == InstallStatus.DOWNLOADED) {
// After the update is downloaded, show a notification
// and request user confirmation to restart the app.
scope.launch {
val result = snackbarHostState.showSnackbar(
message = "An update has just been downloaded",
actionLabel = "Reload",
)
when (result) {
SnackbarResult.Dismissed -> Unit
SnackbarResult.ActionPerformed -> {
appUpdateManager.completeUpdate()
}
}
}
}
}
}
LaunchedEffect(Unit) {
try {
val appUpdateInfo = appUpdateManager.checkUpdateInfo()
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
appUpdateInfo.isUpdateTypeAllowed(updateType)
) {
appUpdateManager.startUpdateFlow(
appUpdateInfo,
activity,
AppUpdateOptions.newBuilder(updateType).build(),
)
}
} catch (e: InstallException) {
Timber.w(e)
}
}
DisposableEffect(appUpdateManager) {
appUpdateManager.registerListener(flexibleUpdateListener)
onDispose {
appUpdateManager.unregisterListener(flexibleUpdateListener)
}
}
SnackbarHost(
hostState = snackbarHostState,
modifier = modifier,
)
}
private suspend fun AppUpdateManager.checkUpdateInfo() =
suspendCoroutine<AppUpdateInfo> { continuation ->
appUpdateInfo.addOnSuccessListener {
continuation.resumeWith(Result.success(it))
}.addOnFailureListener {
continuation.resumeWith(Result.failure(it))
}
}

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:textColorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M2.5,19h19v2h-19V19zM19.34,15.85c0.8,0.21 1.62,-0.26 1.84,-1.06c0.21,-0.8 -0.26,-1.62 -1.06,-1.84l-5.31,-1.42l-2.76,-9.02L10.12,2v8.28L5.15,8.95L4.22,6.63L2.77,6.24v5.17L19.34,15.85z" />
</vector>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:textColorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M2.5,19h19v2h-19V19zM22.07,9.64c-0.21,-0.8 -1.04,-1.28 -1.84,-1.06L14.92,10l-6.9,-6.43L6.09,4.08l4.14,7.17l-4.97,1.33l-1.97,-1.54l-1.45,0.39l2.59,4.49c0,0 7.12,-1.9 16.57,-4.43C21.81,11.26 22.28,10.44 22.07,9.64z" />
</vector>

View File

@ -1,170 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -25,6 +25,6 @@
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@ -1,12 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#1f60f5</color>
<color name="colorPrimaryVariant">#0037c1</color>
<color name="colorPrimaryVariant3">#1A6F8DFF</color>
<color name="colorOnPrimary">@color/white</color>
<color name="colorSecondary">#f1c931</color>
<color name="colorSecondaryVariant">#ba9900</color>
<color name="colorOnSecondary">@color/black</color>
<color name="black">#ff000000</color>
<color name="white">#ffffffff</color>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="match_constraints">0dp</dimen>
<dimen name="size_tinyish">2dp</dimen>
<dimen name="size_tiny">4dp</dimen>
<dimen name="size_tiny_plus">6dp</dimen>
<dimen name="size_small">8dp</dimen>
<dimen name="size_small_plus">10dp</dimen>
<dimen name="size_medium">12dp</dimen>
<dimen name="size_general">16dp</dimen>
<dimen name="size_general_plus">18dp</dimen>
<dimen name="size_extra_large">20dp</dimen>
<dimen name="size_extra_large_plus">26dp</dimen>
<dimen name="size_general_double">32dp</dimen>
<dimen name="size_general_plus_double">36dp</dimen>
<dimen name="size_double_extra_large">42dp</dimen>
<dimen name="size_general_double_double">64dp</dimen>
</resources>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="adult">
<item quantity="one">%d Adult</item>
<item quantity="other">%d Adults</item>
</plurals>
<plurals name="teen">
<item quantity="one">%d Teen</item>
<item quantity="other">%d Teens</item>
</plurals>
<plurals name="child">
<item quantity="one">%d Child</item>
<item quantity="other">%d Children</item>
</plurals>
<plurals name="stop">
<item quantity="one">%d Stop</item>
<item quantity="other">%d Stops</item>
</plurals>
</resources>

View File

@ -1,32 +1,3 @@
<resources>
<string name="app_name">AndroidChallenge</string>
<string name="from">From</string>
<string name="to">To</string>
<string name="departure_date">Departure date</string>
<string name="passengers">Passengers</string>
<string name="search">Search</string>
<string name="select_departure_airport_first">You need to select departure airport first</string>
<string name="select_departure_date">Select departure date</string>
<string name="adult">Adult</string>
<string name="teen">Teen</string>
<string name="child">Child</string>
<string name="cancel">Cancel</string>
<string name="submit">Submit</string>
<string name="server_error">Unable to connect to server</string>
<string name="no_internet_connection">No internet connection</string>
<string name="fill_all_data_first">You need to fill all data first.</string>
<string name="flight_not_found">Unfortunately there is no flight on particular date.\nChoose different one.</string>
<string name="go_back">Go back</string>
<string name="total_price">Total price:</string>
<string name="price_adult">Adult: %d x %.2f %s</string>
<string name="price_teen">Teen: %d x %.2f %s</string>
<string name="price_child">Child: %d x %.2f %s</string>
<string name="search_airport">Search airport</string>
<string name="find_airport">Find airport</string>
<string name="flights">Flights</string>
<string name="search_origin_airport">Search origin airport</string>
<string name="search_destination_airport">Search destination airport</string>
<string name="flight_details">Flight details</string>
<string name="price_range">Price range:</string>
<string name="no_filter_results">There are no flights in selected price range</string>
<string name="app_name" translatable="false">Flights</string>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Flights" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -1,7 +1,7 @@
package com.ryanair.androidchallenge.core
package dev.adriankuta.flights
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class App : Application()
class MyApplication : Application() {}