Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
android:theme="@style/Theme.Copa2022"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".features.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Copa2022">
Expand Down
44 changes: 0 additions & 44 deletions app/src/main/kotlin/me/dio/copa/catar/MainActivity.kt

This file was deleted.

40 changes: 40 additions & 0 deletions app/src/main/kotlin/me/dio/copa/catar/features/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package me.dio.copa.catar.features

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import dagger.hilt.android.AndroidEntryPoint
import me.dio.copa.catar.extensions.observe
import me.dio.copa.catar.notification.scheduler.extensions.NotificationMatcherWorker
import me.dio.copa.catar.ui.theme.Copa2022Theme

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
observeActions()
setContent {
Copa2022Theme {
val state by viewModel.state.collectAsState()
MainScreen(matches = state.matches, viewModel::toggleNotification)
}
}
}

private fun observeActions() {
viewModel.action.observe(this) { action ->
when (action) {
is MainUiAction.MatchesNotFound -> TODO()
MainUiAction.Unexpected -> TODO()
is MainUiAction.DisableNotification ->
NotificationMatcherWorker.cancel(applicationContext, action.match)
is MainUiAction.EnableNotification ->
NotificationMatcherWorker.start(applicationContext, action.match)
}
}
}
}
150 changes: 150 additions & 0 deletions app/src/main/kotlin/me/dio/copa/catar/features/MainScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package me.dio.copa.catar.features

import androidx.compose.foundation.Image
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.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
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.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import me.dio.copa.catar.R
import me.dio.copa.catar.domain.extensions.getDate
import me.dio.copa.catar.domain.model.MatchDomain
import me.dio.copa.catar.domain.model.TeamDomain
import me.dio.copa.catar.ui.theme.Shapes

typealias NotificationOnClick = (match: MatchDomain) -> Unit

@Composable
fun MainScreen(matches: List<MatchDomain>, onNotificationClick: NotificationOnClick) {
Box(modifier = Modifier
.fillMaxSize()
.padding(8.dp)) {
LazyColumn (verticalArrangement = Arrangement.spacedBy(8.dp)) {
items(matches){ match ->
MatchInfo(match, onNotificationClick)
}
}
}
}

@Composable
fun MatchInfo(match: MatchDomain, onNotificationClick: NotificationOnClick) {
Card (
shape = Shapes.large,
modifier = Modifier.fillMaxWidth()
){
Box{
AsyncImage(
model = match.stadium.image,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.height(160.dp)
)
Column(modifier = Modifier.padding(16.dp)) {
Notification(match, onNotificationClick)
Title(match)
Teams(match)
}
}
}
}

@Composable
fun Notification(match: MatchDomain, onClick: NotificationOnClick) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
val drawable = if (match.notificationEnabled) R.drawable.ic_notifications_active
else R.drawable.ic_notifications

Image(
painter = painterResource(id = drawable),
modifier = Modifier.clickable {
onClick(match)
},
contentDescription = null
)
}
}

@Composable
fun Title(match: MatchDomain) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(
text = "${match.date.getDate()} - ${match.name}",
style = MaterialTheme.typography.h6.copy(color = Color.White)

)

}
}

@Composable
fun Teams(match: MatchDomain) {
Row (
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
){
TeamItem(
team = match.team1,
)

Text(
text = "X",
modifier = Modifier.padding(end = 16.dp, start = 16.dp),
style = MaterialTheme.typography.h6.copy(color = Color.White)
)

TeamItem(
team = match.team2,
)
}
}

@Composable
fun TeamItem(team: TeamDomain) {
Row (
verticalAlignment = Alignment.CenterVertically
){
Text(
text = team.flag,
modifier = Modifier.align(Alignment.CenterVertically),
style = MaterialTheme.typography.h3.copy(color = Color.White)
)

Spacer(
modifier = Modifier.size(16.dp)
)

Text(
text = team.displayName,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6.copy(color = Color.White)
)
}
}
74 changes: 74 additions & 0 deletions app/src/main/kotlin/me/dio/copa/catar/features/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package me.dio.copa.catar.features

import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.dio.copa.catar.core.BaseViewModel
import me.dio.copa.catar.domain.model.Match
import me.dio.copa.catar.domain.model.MatchDomain
import me.dio.copa.catar.domain.usecase.DisableNotificationUseCase
import me.dio.copa.catar.domain.usecase.EnableNotificationUseCase
import me.dio.copa.catar.domain.usecase.GetMatchesUseCase
import me.dio.copa.catar.remote.NotFoundException
import me.dio.copa.catar.remote.UnexpectedException
import javax.inject.Inject

@HiltViewModel
class MainViewModel @Inject constructor(
private val getMatchesUseCase: GetMatchesUseCase,
private val disableNotificationUseCase: DisableNotificationUseCase,
private val enableNotificationUseCase: EnableNotificationUseCase
) : BaseViewModel<MainUiState, MainUiAction> (MainUiState()){

init {
fetchMatches()
}

private fun fetchMatches() = viewModelScope.launch {
getMatchesUseCase()
.flowOn(Dispatchers.Main)
.catch {
when(it){
is NotFoundException -> sendAction(MainUiAction.MatchesNotFound(it.message ?: "Erro sem mensagem"))
is UnexpectedException -> sendAction(MainUiAction.Unexpected)
}
}.collect { matches ->
setState {
copy(matches = matches)
}
}
}

fun toggleNotification(match: Match){
viewModelScope.launch {
runCatching {
withContext(Dispatchers.Main) {
val action = if (match.notificationEnabled) {
disableNotificationUseCase(match.id)
MainUiAction.DisableNotification(match)
} else {
enableNotificationUseCase(match.id)
MainUiAction.EnableNotification(match)
}

sendAction(action)
}
}
}
}
}

data class MainUiState(
val matches: List<MatchDomain> = emptyList()
)

sealed interface MainUiAction {
object Unexpected: MainUiAction
data class MatchesNotFound(val message: String) : MainUiAction
data class EnableNotification(val match: Match) : MainUiAction
data class DisableNotification(val match: Match) : MainUiAction
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package me.dio.copa.catar.domain.usecase

import me.dio.copa.catar.domain.repositories.MatchesRepository
import javax.inject.Inject

class DisableNotificationUseCase @Inject constructor(
private val repository: MatchesRepository
) {
suspend operator fun invoke(id: String) {
repository.disableNotificationFor(id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package me.dio.copa.catar.domain.usecase

import me.dio.copa.catar.domain.repositories.MatchesRepository
import javax.inject.Inject

class EnableNotificationUseCase @Inject constructor(
private val repository: MatchesRepository
){
suspend operator fun invoke(id: String) {
repository.enableNotificationFor(id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.dio.copa.catar.domain.usecase

import kotlinx.coroutines.flow.Flow
import me.dio.copa.catar.domain.model.Match
import me.dio.copa.catar.domain.repositories.MatchesRepository
import javax.inject.Inject

class GetMatchesUseCase @Inject constructor(
private val repository: MatchesRepository
){
suspend operator fun invoke(): Flow<List<Match>> {
return repository.getMatches()
}
}
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
5 changes: 2 additions & 3 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#Sat Oct 15 16:48:08 BRT 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading