Skip to content

Commit

Permalink
Merge pull request #17 from CaZaIt/feature/16-home-ui
Browse files Browse the repository at this point in the history
[#16] home UI
  • Loading branch information
YiBeomSeok authored Jan 6, 2024
2 parents c48f012 + 7670211 commit d87ccd5
Show file tree
Hide file tree
Showing 49 changed files with 1,118 additions and 25 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies {
implementation(projects.feature.splash)

implementation(projects.core.repo.signin.impl)
implementation(projects.core.repo.home.impl)

implementation(projects.core.http)

Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<application
android:name=".CazaitApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme
@Composable
fun CazaitCard(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.surface,
color: Color = MaterialTheme.colorScheme.primaryContainer,
content: @Composable () -> Unit,
) {
Surface(
modifier = modifier,
color = color,
shape = RoundedCornerShape(32.dp),
shadowElevation = 11.dp,
shape = RoundedCornerShape(12.dp),
shadowElevation = 16.dp,
content = content,
)
}
Expand All @@ -32,16 +32,16 @@ fun CazaitCard(
modifier: Modifier = Modifier,
enabled: Boolean = false,
onClick: () -> Unit = {},
color: Color = MaterialTheme.colorScheme.surface,
color: Color = MaterialTheme.colorScheme.primaryContainer,
content: @Composable () -> Unit,
) {
Surface(
onClick = onClick,
enabled = enabled,
modifier = modifier,
color = color,
shape = RoundedCornerShape(32.dp),
shadowElevation = 11.dp,
shape = RoundedCornerShape(12.dp),
shadowElevation = 16.dp,
content = content,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.cazait.cazaitandroid.core.designsystem.component

import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.coil.CoilImage
import com.skydoves.landscapist.components.rememberImageComponent
import com.skydoves.landscapist.placeholder.placeholder.PlaceholderPlugin

@Composable
fun NetworkImage(
imageUrl: String?,
modifier: Modifier = Modifier,
placeholder: Painter? = null,
contentScale: ContentScale = ContentScale.Crop,
contentDescription: String? = null,
) {
CoilImage(
imageModel = { imageUrl },
modifier = modifier,
component = rememberImageComponent {
+PlaceholderPlugin.Loading(placeholder)
+PlaceholderPlugin.Failure(placeholder)
},
imageOptions = ImageOptions(
contentScale = contentScale,
alignment = Alignment.Center,
contentDescription = contentDescription,
)
)
}

@Preview
@Composable
private fun PreviewNetworkImage() {
NetworkImage(
imageUrl = "",
placeholder = painterResource(id = Color(0xFFFFFFFF).toArgb())
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

private val DarkColorScheme = darkColorScheme(
primary = White,
primary = SunsetOrange,
onPrimary = Black,
primaryContainer = Graphite,
onPrimaryContainer = White,
Expand Down Expand Up @@ -43,7 +43,7 @@ private val DarkColorScheme = darkColorScheme(
)

private val LightColorScheme = lightColorScheme(
primary = Black,
primary = SunsetOrange,
onPrimary = White,
primaryContainer = White,
onPrimaryContainer = Black,
Expand All @@ -61,7 +61,7 @@ private val LightColorScheme = lightColorScheme(
errorContainer = Red01,
onErrorContainer = Red06,
surface = Black,
onSurface = DuskGray,
onSurface = White,
inverseSurface = Yellow05,
inverseOnSurface = White,
outline = LightGray,
Expand Down
20 changes: 20 additions & 0 deletions core/location/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id("cazait.android.library")
id("cazait.android.hilt")
id("kotlinx-serialization")
}

android {
namespace = "org.cazait.cazaitandroid.core.location"
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
}

dependencies {
implementation(libs.play.services.location)

androidTestImplementation(libs.androidx.test.ext)
androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.coroutines.test)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.cazait.cazaitandroid.core.location

data class LocationDetails(val latitude: Double, val longitude: Double)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.cazait.cazaitandroid.core.location

import android.annotation.SuppressLint
import android.content.Context
import android.os.Looper
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.Priority
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import org.cazait.cazaitandroid.core.location.extension.hasLocationPermission
import javax.inject.Inject

interface LocationService {
fun requestLocationUpdates(): Flow<LocationDetails?>
}

internal class LocationServiceImpl @Inject constructor(
private val context: Context,
private val locationClient: FusedLocationProviderClient,
) : LocationService {
@SuppressLint("MissingPermission")
override fun requestLocationUpdates(): Flow<LocationDetails?> = callbackFlow {
if (!context.hasLocationPermission()) {
trySend(null)
return@callbackFlow
}

val request = LocationRequest.Builder(10000L)
.setIntervalMillis(10000L)
.setPriority(Priority.PRIORITY_HIGH_ACCURACY)
.build()

val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult.locations.lastOrNull()?.let {
trySend(LocationDetails(latitude = it.latitude, longitude = it.longitude))
}
}
}

locationClient.requestLocationUpdates(
request,
locationCallback,
Looper.getMainLooper()
)

awaitClose {
locationClient.removeLocationUpdates(locationCallback)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.cazait.cazaitandroid.core.location.di

import android.content.Context
import com.google.android.gms.location.LocationServices
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ViewModelScoped
import dagger.hilt.components.SingletonComponent
import org.cazait.cazaitandroid.core.location.LocationService
import org.cazait.cazaitandroid.core.location.LocationServiceImpl
import org.cazait.cazaitandroid.core.location.usecase.GetLocationUseCase
import org.cazait.cazaitandroid.core.location.usecase.GetLocationUseCaseImpl
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal object LocationModule {
@Singleton
@Provides
fun provideLocationClient(
@ApplicationContext context: Context,
): LocationService = LocationServiceImpl(
context = context,
locationClient = LocationServices.getFusedLocationProviderClient(context)
)
}

@Module
@InstallIn(ViewModelComponent::class)
internal abstract class UseCaseBindModule {
@Binds
@ViewModelScoped
abstract fun bindGetLocationUseCase(
dataSource: GetLocationUseCaseImpl,
): GetLocationUseCase
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.cazait.cazaitandroid.core.location.extension

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat

fun Context.hasLocationPermission(): Boolean {
val fineLocationGranted = ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
val coarseLocationGranted = ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED

return fineLocationGranted && coarseLocationGranted
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.cazait.cazaitandroid.core.location.usecase

import kotlinx.coroutines.flow.Flow
import org.cazait.cazaitandroid.core.location.LocationDetails
import org.cazait.cazaitandroid.core.location.LocationService
import javax.inject.Inject

interface GetLocationUseCase {
operator fun invoke(): Flow<LocationDetails?>
}

internal class GetLocationUseCaseImpl @Inject constructor(
private val locationService: LocationService,
) : GetLocationUseCase {
override fun invoke(): Flow<LocationDetails?> = locationService.requestLocationUpdates()
}
9 changes: 9 additions & 0 deletions core/repo/home/api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id("cazait.coroutine.library")
id("kotlinx-serialization")
}

dependencies {
api(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization.json)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.cazait.cazaitandroid.core.repo.home.api

import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes
import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit
import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude
import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude
import org.cazait.cazaitandroid.core.repo.home.api.model.SortBy

interface HomeRepository {
suspend fun getAllCongestionCafes(
latitude: Latitude,
longitude: Longitude,
sort: SortBy,
limit: DistanceLimit,
): CongestionCafes
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

data class Cafe(
val id: CafeId,
val name: CafeName,
val address: CafeAddress,
val cafeImages: CafeImages,
val latitude: Latitude,
val longitude: Longitude,
)

@JvmInline
value class Cafes(private val values: List<Cafe>) {
fun asList(): List<Cafe> = values
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

@JvmInline
value class CafeAddress(private val address: String) {
fun asString(): String = address
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

import java.util.UUID

@JvmInline
value class CafeId(private val id: UUID) {
fun asUUID(): UUID = id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

@JvmInline
value class CafeImage(private val url: String) {
fun asString(): String = url
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

@JvmInline
value class CafeImages(private val images: List<CafeImage>) {
fun asList(): List<CafeImage> = images
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

@JvmInline
value class CafeName(private val name: String) {
fun asString(): String = name
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.cazait.cazaitandroid.core.repo.home.api.model

data class CongestionCafe(
val cafe: Cafe,
val congestion: Congestion,
)

@JvmInline
value class CongestionCafes(private val values: List<CongestionCafe>) {
fun asList(): List<CongestionCafe> = values
}

enum class Congestion { FREE, NORMAL, CLOSE, CROWDED, VERY_CROWDED }
Loading

0 comments on commit d87ccd5

Please sign in to comment.