Merge branch 'main' into dt/tidy-build-config

pull/355/head
Don Turner 2 years ago committed by GitHub
commit c9ed09c2fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,16 @@
{
"$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"
}
]
}

@ -88,7 +88,7 @@ jobs:
api-level: ${{ matrix.api-level }}
arch: x86_64
disable-animations: true
disk-size: 2000M
disk-size: 6000M
heap-size: 600M
script: ./gradlew connectedProdDebugAndroidTest -x :benchmark:connectedProdBenchmarkAndroidTest --stacktrace

@ -19,7 +19,12 @@
---
status: PUBLISHED
technologies: [Android]
categories: [Architecture]
categories:
- AndroidTesting
- AndroidArchitecture
- AndroidArchitectureUILayer
- AndroidArchitectureDomainLayer
- AndroidArchitectureDataLayer
languages: [Kotlin]
solutions: [Mobile]
github: android/nowinandroid

@ -24,10 +24,7 @@ in or follow specific authors.
## Screenshots
![Screenshot showing For You screen](docs/images/screenshot-1-foryou.png "Screenshot showing For You screen")
![Screenshot showing Interests screen](docs/images/screenshot-2-interests.png "Screenshot showing Interests screen")
![Screenshot showing Topic detail screen](docs/images/screenshot-3-topicdetail.png "Screenshot showing Topic detail screen")
![Screenshot showing For You screen, Interests screen and Topic detail screen](docs/images/screenshots.png "Screenshot showing For You screen, Interests screen and Topic detail screen")
# Development Environment

@ -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()

@ -22,6 +22,7 @@
<application
android:name=".NiaApplication"
android:allowBackup="true"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"

File diff suppressed because it is too large Load Diff

@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.samples.apps.nowinandroid.Flavor
import com.google.samples.apps.nowinandroid.FlavorDimension
import com.android.build.api.dsl.ManagedVirtualDevice
import com.google.samples.apps.nowinandroid.configureFlavors
plugins {
@ -49,9 +48,21 @@ android {
// which is more close to what will be shipped to users (no fake data), but has ability to run the
// benchmarks on demo, so we benchmark on stable data.
configureFlavors(this)
targetProjectPath = ":app"
experimentalProperties["android.experimental.self-instrumenting"] = true
testOptions {
managedDevices {
devices {
create<ManagedVirtualDevice>("pixel5Api30") {
device = "Pixel 5"
apiLevel = 30
systemImageSource = "aosp"
}
}
}
}
}
dependencies {

@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.interests
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.Until
fun MacrobenchmarkScope.interestsScrollTopicsDownUp() {
val topicsList = device.findObject(By.res("interests:topics"))
@ -33,3 +34,14 @@ fun MacrobenchmarkScope.interestsScrollPeopleDownUp() {
device.waitForIdle()
peopleList.fling(Direction.UP)
}
fun MacrobenchmarkScope.interestsWaitForTopics() {
device.wait(Until.hasObject(By.text("Accessibility")), 30_000)
}
fun MacrobenchmarkScope.interestsToggleBookmarked() {
val topicsList = device.findObject(By.res("interests:topics"))
val checkable = topicsList.findObject(By.checkable(true))
checkable.click()
device.waitForIdle()
}

@ -0,0 +1,61 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.interests
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TopicsScreenRecompositionBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun benchmarkStateChangeCompilationBaselineProfile() =
benchmarkStateChange(CompilationMode.Partial())
private fun benchmarkStateChange(compilationMode: CompilationMode) =
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode,
iterations = 10,
startupMode = StartupMode.WARM,
setupBlock = {
// Start the app
pressHome()
startActivityAndWait()
// Navigate to interests screen
device.findObject(By.text("Interests")).click()
device.waitForIdle()
}
) {
interestsWaitForTopics()
repeat(3) {
interestsToggleBookmarked()
}
}
}

@ -57,13 +57,6 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get())
add("implementation", libs.findLibrary("kotlinx.coroutines.android").get())
// TODO : Remove this dependency once we upgrade to Android Studio Dolphin b/228889042
// These dependencies are currently necessary to render Compose previews
add(
"debugImplementation",
libs.findLibrary("androidx.customview.poolingcontainer").get()
)
}
}
}

@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
import java.io.File
@ -42,6 +43,12 @@ internal fun Project.configureAndroidCompose(
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters()
}
dependencies {
val bom = libs.findLibrary("androidx-compose-bom").get()
add("implementation", platform(bom))
add("androidTestImplementation", platform(bom))
}
}
}

@ -41,10 +41,4 @@ dependencies {
api(libs.androidx.compose.runtime)
lintPublish(project(":lint"))
androidTestImplementation(project(":core:testing"))
// TODO : Remove these dependency once we upgrade to Android Studio Dolphin b/228889042
// These dependencies are currently necessary to render Compose previews
debugImplementation(libs.androidx.customview.poolingcontainer)
debugImplementation(libs.androidx.lifecycle.viewModelCompose)
debugImplementation(libs.androidx.savedstate.ktx)
}

@ -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 }
)
}
}

@ -15,6 +15,7 @@
*/
plugins {
id("nowinandroid.android.library")
id("nowinandroid.android.library.compose")
id("nowinandroid.android.hilt")
}

@ -33,13 +33,6 @@ dependencies {
implementation(libs.coil.kt.compose)
implementation(libs.kotlinx.datetime)
// TODO : Remove these dependency once we upgrade to Android Studio Dolphin b/228889042
// These dependencies are currently necessary to render Compose previews
debugImplementation(libs.androidx.customview.poolingcontainer)
debugImplementation(libs.androidx.lifecycle.runtimeCompose)
debugImplementation(libs.androidx.lifecycle.viewModelCompose)
debugImplementation(libs.androidx.savedstate.ktx)
api(libs.androidx.compose.foundation)
api(libs.androidx.compose.foundation.layout)
api(libs.androidx.compose.material.iconsExtended)

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

@ -46,14 +46,15 @@ fun TopicsTabContent(
contentPadding = PaddingValues(top = 8.dp)
) {
topics.forEach { followableTopic ->
item {
val topicId = followableTopic.topic.id
item(key = topicId) {
InterestsItem(
name = followableTopic.topic.name,
following = followableTopic.isFollowed,
description = followableTopic.topic.shortDescription,
topicImageUrl = followableTopic.topic.imageUrl,
onClick = { onTopicClick(followableTopic.topic.id) },
onFollowButtonClick = { onFollowButtonClick(followableTopic.topic.id, it) }
onClick = { onTopicClick(topicId) },
onFollowButtonClick = { onFollowButtonClick(topicId, it) }
)
}
}

@ -80,4 +80,25 @@ class SettingsDialogTest {
composeTestRule.onNodeWithText(getString(R.string.brand_android)).assertIsSelected()
composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_dark)).assertIsSelected()
}
@Test
fun whenStateIsSuccess_allLegalLinksAreDisplayed() {
composeTestRule.setContent {
SettingsDialog(
settingsUiState = Success(
UserEditableSettings(
brand = ANDROID,
darkThemeConfig = DARK
)
),
onDismiss = { },
onChangeThemeBrand = {},
onChangeDarkThemeConfig = {}
)
}
composeTestRule.onNodeWithText(getString(R.string.privacy_policy)).assertExists()
composeTestRule.onNodeWithText(getString(R.string.licenses)).assertExists()
composeTestRule.onNodeWithText(getString(R.string.brand_guidelines)).assertExists()
}
}

@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
@ -86,7 +87,7 @@ fun SettingsDialog(
onDismissRequest = { onDismiss() },
title = {
Text(
text = stringResource(R.string.settings_title),
text = stringResource(string.settings_title),
style = MaterialTheme.typography.titleLarge
)
},
@ -96,7 +97,7 @@ fun SettingsDialog(
when (settingsUiState) {
Loading -> {
Text(
text = stringResource(R.string.loading),
text = stringResource(string.loading),
modifier = Modifier.padding(vertical = 16.dp)
)
}
@ -131,15 +132,15 @@ private fun SettingsPanel(
onChangeThemeBrand: (themeBrand: ThemeBrand) -> Unit,
onChangeDarkThemeConfig: (darkThemeConfig: DarkThemeConfig) -> Unit
) {
SettingsDialogSectionTitle(text = stringResource(R.string.theme))
SettingsDialogSectionTitle(text = stringResource(string.theme))
Column(Modifier.selectableGroup()) {
SettingsDialogThemeChooserRow(
text = stringResource(R.string.brand_default),
text = stringResource(string.brand_default),
selected = settings.brand == DEFAULT,
onClick = { onChangeThemeBrand(DEFAULT) }
)
SettingsDialogThemeChooserRow(
text = stringResource(R.string.brand_android),
text = stringResource(string.brand_android),
selected = settings.brand == ANDROID,
onClick = { onChangeThemeBrand(ANDROID) }
)
@ -147,17 +148,17 @@ private fun SettingsPanel(
SettingsDialogSectionTitle(text = "Dark mode preference")
Column(Modifier.selectableGroup()) {
SettingsDialogThemeChooserRow(
text = stringResource(R.string.dark_mode_config_system_default),
text = stringResource(string.dark_mode_config_system_default),
selected = settings.darkThemeConfig == FOLLOW_SYSTEM,
onClick = { onChangeDarkThemeConfig(FOLLOW_SYSTEM) }
)
SettingsDialogThemeChooserRow(
text = stringResource(R.string.dark_mode_config_light),
text = stringResource(string.dark_mode_config_light),
selected = settings.darkThemeConfig == LIGHT,
onClick = { onChangeDarkThemeConfig(LIGHT) }
)
SettingsDialogThemeChooserRow(
text = stringResource(R.string.dark_mode_config_dark),
text = stringResource(string.dark_mode_config_dark),
selected = settings.darkThemeConfig == DARK,
onClick = { onChangeDarkThemeConfig(DARK) }
)
@ -210,15 +211,22 @@ private fun LegalPanel() {
) {
Row {
TextLink(
text = stringResource(R.string.privacy_policy),
text = stringResource(string.privacy_policy),
url = PRIVACY_POLICY_URL
)
Spacer(Modifier.width(16.dp))
TextLink(
text = stringResource(R.string.licenses),
text = stringResource(string.licenses),
url = LICENSES_URL
)
}
Spacer(Modifier.height(16.dp))
Row {
TextLink(
text = stringResource(string.brand_guidelines),
url = BRAND_GUIDELINES_URL
)
}
}
}
}
@ -274,3 +282,4 @@ fun PreviewSettingsDialogLoading() {
/* ktlint-disable max-line-length */
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"

@ -20,6 +20,7 @@
<string name="loading">Loading...</string>
<string name="privacy_policy">Privacy policy</string>
<string name="licenses">Licenses</string>
<string name="brand_guidelines">Brand Guidelines</string>
<string name="theme">Theme</string>
<string name="brand_default">Default</string>
<string name="brand_android">Android</string>

@ -17,7 +17,7 @@
val ktlintVersion = "0.43.0"
initscript {
val spotlessVersion = "6.7.2"
val spotlessVersion = "6.11.0"
repositories {
mavenCentral()

@ -1,54 +1,50 @@
[versions]
accompanist = "0.24.8-beta"
androidDesugarJdkLibs = "1.1.5"
androidGradlePlugin = "7.3.0"
androidxActivity = "1.6.0"
accompanist = "0.27.0"
androidDesugarJdkLibs = "1.2.0"
androidGradlePlugin = "7.3.1"
androidxActivity = "1.6.1"
androidxAppCompat = "1.5.1"
androidxCompose = "1.3.0-beta02"
androidxComposeRuntimeTracing = "1.0.0-alpha01"
androidxComposeBom = "2022.10.00"
androidxComposeCompiler = "1.3.2"
androidxComposeMaterial3 = "1.0.0-beta03"
androidxComposeRuntimeTracing = "1.0.0-alpha01"
androidxCore = "1.9.0"
androidxCoreSplashscreen = "1.0.0"
androidxCustomView = "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"
androidxSavedState = "1.2.0"
androidxStartup = "1.1.1"
androidxWindowManager = "1.0.0"
androidxTestCore = "1.5.0-alpha02"
androidxTestCore = "1.5.0-rc01"
androidxTestExt = "1.1.3"
androidxTestRunner = "1.4.0"
androidxTestRules = "1.4.0"
androidxTracing = "1.1.0"
androidxUiAutomator = "2.2.0"
androidxWork = "2.7.1"
coil = "2.2.1"
hilt = "2.42"
coil = "2.2.2"
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.6"
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.5"
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" }
@ -57,22 +53,22 @@ android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }
androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "androidxCompose" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "androidxCompose" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "androidxCompose" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" }
androidx-compose-material3-windowSizeClass = {group = "androidx.compose.material3", name = "material3-window-size-class", version.ref="androidxComposeMaterial3"}
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "androidxCompose" }
androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "androidxCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-material3-windowSizeClass = {group = "androidx.compose.material3", name = "material3-window-size-class" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" }
androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" }
androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "androidxCompose" }
androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "androidxCompose" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidxCompose" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidxCompose" }
androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref = "androidxCompose" }
androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" }
androidx-customview-poolingcontainer = { group = "androidx.customview", name = "customview-poolingcontainer", version.ref = "androidxCustomView"}
androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" }
androidx-dataStore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidxDataStore" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
@ -83,7 +79,6 @@ androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", v
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }
androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" }
androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" }
androidx-savedstate-ktx = { group = "androidx.savedstate", name = "savedstate-ktx", version.ref= "androidxSavedState"}
androidx-startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "androidxStartup" }
androidx-window-manager = {module = "androidx.window:window", version.ref = "androidxWindowManager"}
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" }

@ -1,5 +0,0 @@
{
"extends": [
"config:base"
]
}
Loading…
Cancel
Save