-
Notifications
You must be signed in to change notification settings - Fork 170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New details screen using Jetpack compose #50
base: master
Are you sure you want to change the base?
Changes from 11 commits
6850b2b
fba2d09
15f83d5
c60b094
9c79fab
acbe215
6d7de5d
05e7a08
cbd141e
713cf81
8f7bbad
fa0bf31
b63f574
7d46a28
8686255
2df855c
350e1d2
5fbc54e
f15d8b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.akshay.newsapp.core.ui.compose | ||
|
||
import androidx.compose.ui.graphics.Color | ||
|
||
val colorPrimary = Color(0xFF121258) | ||
val colorPrimaryDark = Color(0xFF0F0F4A) | ||
val colorAccent = Color(0xFFFFC039) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.akshay.newsapp.core.ui.compose | ||
|
||
import androidx.compose.foundation.isSystemInDarkTheme | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.MaterialTheme.shapes | ||
import androidx.compose.material.darkColors | ||
import androidx.compose.material.lightColors | ||
import androidx.compose.runtime.Composable | ||
|
||
private val DarkColorPalette = darkColors( | ||
primary = colorPrimary, | ||
primaryVariant = colorPrimaryDark, | ||
secondary = colorAccent | ||
) | ||
|
||
private val LightColorPalette = lightColors( | ||
primary = colorPrimary, | ||
primaryVariant = colorPrimaryDark, | ||
secondary = colorAccent | ||
) | ||
|
||
@Composable | ||
fun NewsTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) { | ||
val colors = if (darkTheme) { | ||
DarkColorPalette | ||
} else { | ||
LightColorPalette | ||
} | ||
|
||
MaterialTheme( | ||
colors = colors, | ||
shapes = shapes, | ||
content = content | ||
) | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -27,6 +27,13 @@ import javax.inject.Singleton | |||||
*/ | ||||||
interface NewsRepository { | ||||||
|
||||||
/** | ||||||
* Gets the particular article from database for | ||||||
* the give articleId | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor typo
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can skip the |
||||||
* @param articleId id from NewsArticleDb | ||||||
*/ | ||||||
fun getNewsArticle(articleId: Int): Flow<ViewState<NewsArticleDb>> | ||||||
|
||||||
/** | ||||||
* Gets tne cached news article from database and tries to get | ||||||
* fresh news articles from web and save into database | ||||||
|
@@ -46,6 +53,15 @@ class DefaultNewsRepository @Inject constructor( | |||||
private val newsService: NewsService | ||||||
) : NewsRepository, NewsMapper { | ||||||
|
||||||
override fun getNewsArticle(articleId: Int): Flow<ViewState<NewsArticleDb>> = flow { | ||||||
// 1. Start with loading | ||||||
emit(ViewState.loading()) | ||||||
|
||||||
// 2. Fetch and update state particular article from database | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
val article = newsDao.getNewsArticle(articleId = articleId) | ||||||
emitAll(article.map { ViewState.success(it) }) | ||||||
}.flowOn(Dispatchers.IO) | ||||||
|
||||||
override fun getNewsArticles(): Flow<ViewState<List<NewsArticleDb>>> = flow { | ||||||
// 1. Start with loading | ||||||
emit(ViewState.loading()) | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -28,6 +28,12 @@ interface NewsArticlesDao { | |||||
insertArticles(articles) | ||||||
} | ||||||
|
||||||
/** | ||||||
* Get News article for given articleId | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
*/ | ||||||
@Query("SELECT * FROM news_article WHERE id = :articleId") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor suggestion to use constant
Suggested change
|
||||||
fun getNewsArticle(articleId: Int): Flow<NewsArticleDb> | ||||||
|
||||||
/** | ||||||
* Get all the articles from table | ||||||
*/ | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,116 @@ | ||||||||||||||||||
package com.akshay.newsapp.news.ui.activity | ||||||||||||||||||
|
||||||||||||||||||
import android.os.Bundle | ||||||||||||||||||
import androidx.activity.viewModels | ||||||||||||||||||
import androidx.compose.foundation.Icon | ||||||||||||||||||
import androidx.compose.foundation.ScrollableColumn | ||||||||||||||||||
import androidx.compose.foundation.Text | ||||||||||||||||||
import androidx.compose.foundation.layout.* | ||||||||||||||||||
import androidx.compose.material.* | ||||||||||||||||||
import androidx.compose.material.icons.Icons | ||||||||||||||||||
import androidx.compose.material.icons.filled.ArrowBack | ||||||||||||||||||
import androidx.compose.runtime.Composable | ||||||||||||||||||
import androidx.compose.runtime.getValue | ||||||||||||||||||
import androidx.compose.runtime.livedata.observeAsState | ||||||||||||||||||
import androidx.compose.ui.Alignment | ||||||||||||||||||
import androidx.compose.ui.Modifier | ||||||||||||||||||
import androidx.compose.ui.draw.clip | ||||||||||||||||||
import androidx.compose.ui.platform.setContent | ||||||||||||||||||
import androidx.compose.ui.unit.dp | ||||||||||||||||||
import androidx.ui.tooling.preview.Preview | ||||||||||||||||||
import com.akshay.newsapp.R | ||||||||||||||||||
import com.akshay.newsapp.core.ui.ViewState | ||||||||||||||||||
import com.akshay.newsapp.core.ui.base.BaseActivity | ||||||||||||||||||
import com.akshay.newsapp.core.ui.compose.NewsTheme | ||||||||||||||||||
import com.akshay.newsapp.news.storage.entity.NewsArticleDb | ||||||||||||||||||
import com.akshay.newsapp.news.ui.viewmodel.NewsArticleViewModel | ||||||||||||||||||
import dev.chrisbanes.accompanist.coil.CoilImage | ||||||||||||||||||
|
||||||||||||||||||
const val NEWS_ARG_ARTICLE_ID = "articleId" | ||||||||||||||||||
|
||||||||||||||||||
class NewsDetailsActivity : BaseActivity() { | ||||||||||||||||||
|
||||||||||||||||||
private val articleId: Int by lazy { | ||||||||||||||||||
intent.getIntExtra(NEWS_ARG_ARTICLE_ID, -1) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private val newsArticleViewModel: NewsArticleViewModel by viewModels() | ||||||||||||||||||
|
||||||||||||||||||
override fun onCreate(savedInstanceState: Bundle?) { | ||||||||||||||||||
super.onCreate(savedInstanceState) | ||||||||||||||||||
setContent { | ||||||||||||||||||
NewsTheme { | ||||||||||||||||||
Scaffold(topBar = { | ||||||||||||||||||
TopAppBar( | ||||||||||||||||||
title = {}, | ||||||||||||||||||
backgroundColor = MaterialTheme.colors.primary, | ||||||||||||||||||
navigationIcon = { IconButton(onClick = { finish() }) { | ||||||||||||||||||
Icon(Icons.Filled.ArrowBack) } | ||||||||||||||||||
} | ||||||||||||||||||
) | ||||||||||||||||||
}, bodyContent = { | ||||||||||||||||||
newsDetailsScreen(newsArticleViewModel = newsArticleViewModel, articleId) | ||||||||||||||||||
}) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
@Composable | ||||||||||||||||||
fun newsDetailsScreen(newsArticleViewModel: NewsArticleViewModel, newsId: Int) { | ||||||||||||||||||
val viewState by newsArticleViewModel.getNewsArticle(articleId = newsId).observeAsState(ViewState.loading()) | ||||||||||||||||||
when (viewState) { | ||||||||||||||||||
is ViewState.Loading -> { | ||||||||||||||||||
loadingIndicator() | ||||||||||||||||||
} | ||||||||||||||||||
is ViewState.Error -> { | ||||||||||||||||||
errorView((viewState as ViewState.Error<NewsArticleDb>).message) | ||||||||||||||||||
} | ||||||||||||||||||
is ViewState.Success -> { | ||||||||||||||||||
ScrollableColumn(modifier = Modifier.padding(horizontal = 16.dp)) { | ||||||||||||||||||
with((viewState as ViewState.Success<NewsArticleDb>).data) { | ||||||||||||||||||
Spacer(modifier = Modifier.preferredHeight(8.dp)) | ||||||||||||||||||
CoilImage(data = urlToImage ?: R.drawable.tools_placeholder, | ||||||||||||||||||
modifier = Modifier | ||||||||||||||||||
.heightIn(min = 180.dp) | ||||||||||||||||||
.fillMaxWidth() | ||||||||||||||||||
.clip(shape = MaterialTheme.shapes.medium) | ||||||||||||||||||
) | ||||||||||||||||||
Spacer(Modifier.preferredHeight(16.dp)) | ||||||||||||||||||
Text(text = title ?: "", style = MaterialTheme.typography.h6) | ||||||||||||||||||
Spacer(Modifier.preferredHeight(8.dp)) | ||||||||||||||||||
Text(text = content ?: "", style = MaterialTheme.typography.body1) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
@Preview(showBackground = true) | ||||||||||||||||||
@Composable | ||||||||||||||||||
fun loadingIndicator() { | ||||||||||||||||||
Column(modifier = Modifier.fillMaxWidth().fillMaxHeight(), | ||||||||||||||||||
verticalArrangement = Arrangement.Center, | ||||||||||||||||||
horizontalAlignment = Alignment.CenterHorizontally) { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor suggestion to put all parameters on new line for cleaner look
Suggested change
|
||||||||||||||||||
CircularProgressIndicator( | ||||||||||||||||||
color = MaterialTheme.colors.secondary, | ||||||||||||||||||
strokeWidth = 6.dp, | ||||||||||||||||||
modifier = Modifier.preferredSize(64.dp) | ||||||||||||||||||
) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
@Composable | ||||||||||||||||||
fun errorView(message: String) { | ||||||||||||||||||
Column(modifier = Modifier.fillMaxWidth().fillMaxHeight(), | ||||||||||||||||||
verticalArrangement = Arrangement.Center, | ||||||||||||||||||
horizontalAlignment = Alignment.CenterHorizontally) { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same suggestion as above to put all parameters on new line |
||||||||||||||||||
Text(text = message, style = MaterialTheme.typography.body1) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
@Preview(showBackground = true) | ||||||||||||||||||
@Composable | ||||||||||||||||||
fun errorViewPreview() { | ||||||||||||||||||
errorView(message = "Something went wrong!") | ||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's best to keep the version as a constant in
project/build.gradle