Merge branch 'main' into tm/generate-bp-configuration

pull/385/head
mlykotom 2 years ago
commit 07e80adf4a

@ -2,5 +2,15 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base", "group:all", ":dependencyDashboard", "schedule:daily"
],
"packageRules": [
{
"matchPackageNames": ["org.objenesis:objenesis"],
"allowedVersions": "<=2.6"
},
{
"matchPackageNames": ["com.google.protobuf"],
"allowedVersions": "<=0.8.19"
}
]
}

@ -1,12 +1,12 @@
![Now in Android](docs/images/nia-splash.jpg "Now in Android")
Now in Android App [Work in progress 🚧]
Now in Android App
==================
**Learn how this app was designed and built in the [design case study](https://goo.gle/nia-figma), [architecture learning journey](docs/ArchitectureLearningJourney.md) and [modularization learning journey](docs/ModularizationLearningJourney.md).**
This is the repository for the [Now in Android](https://developer.android.com/series/now-in-android)
app.
app. It is a **work in progress** 🚧.
**Now in Android** is a fully functional Android app built entirely with Kotlin and Jetpack Compose. It
follows Android design and development best practices and is intended to be a useful reference

@ -26,8 +26,8 @@ plugins {
android {
defaultConfig {
applicationId = "com.google.samples.apps.nowinandroid"
versionCode = 1
versionName = "0.0.1" // X.Y.Z; X = Major, Y = minor, Z = Patch level
versionCode = 2
versionName = "0.0.2" // X.Y.Z; X = Major, Y = minor, Z = Patch level
// Custom test runner to set up Hilt dependency graph
testInstrumentationRunner = "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"

@ -255,7 +255,8 @@ class NavigationTest {
fun navigationBar_multipleBackStackInterests() {
composeTestRule.apply {
onNodeWithText(interests).performClick()
onNodeWithText("Android Studio").performClick() // TODO: Grab string from fake data
// TODO: Grab string from fake data
onNodeWithText("Android Studio & Tools").performClick()
// Switch tab
onNodeWithText(forYou).performClick()

@ -31,5 +31,4 @@ plugins {
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.hilt) apply false
alias(libs.plugins.secrets) apply false
id("org.jetbrains.kotlin.android") version "1.7.10" apply false
}

@ -61,10 +61,9 @@ class GetFollowableTopicsStreamUseCase @Inject constructor(
isFollowed = topic.id in followedIds
)
}
if (sortBy == NAME) {
followedTopics.sortedBy { it.topic.name }
} else {
followedTopics
when (sortBy) {
NAME -> followedTopics.sortedBy { it.topic.name }
else -> followedTopics
}
}
}

@ -49,7 +49,7 @@ class FakeNiaNetworkDataSourceTest {
fun testDeserializationOfNewsResources() = runTest(testDispatcher) {
assertEquals(
FakeDataSource.sampleResource,
subject.getNewsResources().first()
subject.getNewsResources().find { it.id == FakeDataSource.sampleResource.id }
)
}
}

@ -43,10 +43,12 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.text.font.FontWeight
@ -54,7 +56,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
@ -73,8 +74,8 @@ fun AuthorsCarousel(
LazyRow(
modifier = modifier.testTag(tag),
contentPadding = PaddingValues(24.dp),
horizontalArrangement = Arrangement.spacedBy(24.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
state = lazyListState
) {
items(items = authors, key = { item -> item.author.id }) { followableAuthor ->
@ -97,24 +98,32 @@ fun AuthorItem(
modifier: Modifier = Modifier,
) {
val followDescription = if (following) {
stringResource(id = R.string.following)
stringResource(R.string.following)
} else {
stringResource(id = R.string.not_following)
stringResource(R.string.not_following)
}
val followActionLabel = if (following) {
stringResource(R.string.unfollow)
} else {
stringResource(R.string.follow)
}
Column(
modifier = modifier
.toggleable(
value = following,
enabled = true,
role = Role.Button,
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false),
onValueChange = { newFollowing -> onAuthorClick(newFollowing) },
)
.padding(8.dp)
.sizeIn(maxWidth = 48.dp)
.semantics(mergeDescendants = true) {
// Add information for A11y services, explaining what each state means and
// what will happen when the user interacts with the author item.
stateDescription = "$followDescription ${author.name}"
onClick(label = followActionLabel, action = null)
}
) {
Box(
@ -136,28 +145,27 @@ fun AuthorItem(
AsyncImage(
modifier = authorImageModifier,
model = author.imageUrl,
contentScale = ContentScale.Fit,
contentScale = ContentScale.Crop,
contentDescription = null
)
}
NiaToggleButton(
checked = following,
onCheckedChange = onAuthorClick,
modifier = Modifier.align(Alignment.BottomEnd),
icon = {
Icon(
imageVector = NiaIcons.Add,
contentDescription = null
)
},
checkedIcon = {
Icon(
imageVector = NiaIcons.Check,
contentDescription = null
)
},
size = 24.dp,
backgroundColor = MaterialTheme.colorScheme.surface
val backgroundColor =
if (following)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surface
Icon(
imageVector = if (following) NiaIcons.Check else NiaIcons.Add,
contentDescription = null,
modifier = Modifier
.align(Alignment.BottomEnd)
.size(18.dp)
.drawBehind {
drawCircle(
color = backgroundColor,
radius = 12.dp.toPx()
)
}
)
}
Spacer(modifier = Modifier.height(4.dp))

@ -27,5 +27,7 @@
<!-- Authors-->
<string name="following">You are following</string>
<string name="not_following">You are not following</string>
<string name="follow">Follow</string>
<string name="unfollow">Unfollow</string>
</resources>

@ -82,7 +82,7 @@ class SettingsDialogTest {
}
@Test
fun whenStateIsSuccess_allLegalLinksAreDisplayed() {
fun whenStateIsSuccess_allLinksAreDisplayed() {
composeTestRule.setContent {
SettingsDialog(
settingsUiState = Success(
@ -100,5 +100,6 @@ class SettingsDialogTest {
composeTestRule.onNodeWithText(getString(R.string.privacy_policy)).assertExists()
composeTestRule.onNodeWithText(getString(R.string.licenses)).assertExists()
composeTestRule.onNodeWithText(getString(R.string.brand_guidelines)).assertExists()
composeTestRule.onNodeWithText(getString(R.string.feedback)).assertExists()
}
}

@ -110,7 +110,7 @@ fun SettingsDialog(
}
}
Divider(Modifier.padding(top = 8.dp))
LegalPanel()
LinksPanel()
}
},
confirmButton = {
@ -201,7 +201,7 @@ fun SettingsDialogThemeChooserRow(
}
@Composable
private fun LegalPanel() {
private fun LinksPanel() {
Row(
modifier = Modifier.padding(top = 16.dp)
) {
@ -226,6 +226,11 @@ private fun LegalPanel() {
text = stringResource(string.brand_guidelines),
url = BRAND_GUIDELINES_URL
)
Spacer(Modifier.width(16.dp))
TextLink(
text = stringResource(string.feedback),
url = FEEDBACK_URL
)
}
}
}
@ -283,3 +288,4 @@ fun PreviewSettingsDialogLoading() {
private const val PRIVACY_POLICY_URL = "https://policies.google.com/privacy"
private const val LICENSES_URL = "https://github.com/android/nowinandroid/blob/main/app/LICENSES.md#open-source-licenses-and-copyright-notices"
private const val BRAND_GUIDELINES_URL = "https://developer.android.com/distribute/marketing-tools/brand-guidelines"
private const val FEEDBACK_URL = "https://goo.gle/nia-app-feedback"

@ -21,6 +21,7 @@
<string name="privacy_policy">Privacy policy</string>
<string name="licenses">Licenses</string>
<string name="brand_guidelines">Brand Guidelines</string>
<string name="feedback">Feedback</string>
<string name="theme">Theme</string>
<string name="brand_default">Default</string>
<string name="brand_android">Android</string>

@ -1,6 +1,6 @@
[versions]
accompanist = "0.27.0"
androidDesugarJdkLibs = "1.1.5"
androidDesugarJdkLibs = "1.2.0"
androidGradlePlugin = "7.3.1"
androidxActivity = "1.6.1"
androidxAppCompat = "1.5.1"
@ -12,10 +12,9 @@ androidxCoreSplashscreen = "1.0.0"
androidxDataStore = "1.0.0"
androidxEspresso = "3.4.0"
androidxHiltNavigationCompose = "1.0.0"
# Skipping version 2.6.0-alpha02 due to https://issuetracker.google.com/249686765
androidxLifecycle = "2.6.0-alpha01"
androidxLifecycle = "2.6.0-alpha03"
androidxMacroBenchmark = "1.1.0"
androidxNavigation = "2.5.2"
androidxNavigation = "2.5.3"
androidxMetrics = "1.0.0-alpha03"
androidxProfileinstaller = "1.2.0"
androidxStartup = "1.1.1"
@ -28,24 +27,24 @@ androidxTracing = "1.1.0"
androidxUiAutomator = "2.2.0"
androidxWork = "2.7.1"
coil = "2.2.2"
hilt = "2.42"
hilt = "2.44"
hiltExt = "1.0.0"
jacoco = "0.8.7"
junit4 = "4.13.2"
kotlin = "1.7.20"
kotlinxCoroutines = "1.6.4"
kotlinxDatetime = "0.4.0"
kotlinxSerializationJson = "1.4.0"
ksp = "1.7.20-1.0.7"
lint = "30.2.2"
kotlinxSerializationJson = "1.4.1"
ksp = "1.7.20-1.0.8"
lint = "30.3.1"
okhttp = "4.10.0"
protobuf = "3.21.9"
protobufPlugin = "0.8.19"
retrofit = "2.9.0"
retrofitKotlinxSerializationJson = "0.8.0"
room = "2.5.0-alpha03"
room = "2.5.0-beta01"
secrets = "2.0.1"
turbine = "0.8.0"
turbine = "0.12.1"
[libraries]
accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" }

Loading…
Cancel
Save