Merge remote-tracking branch 'origin/main'

pull/850/head
qamarelsafadi 1 year ago
commit 42b16bf7be

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

@ -183,7 +183,7 @@ class NiaAppStateTest {
@Composable @Composable
private fun rememberTestNavController(): TestNavHostController { private fun rememberTestNavController(): TestNavHostController {
val context = LocalContext.current val context = LocalContext.current
val navController = remember { return remember<TestNavHostController> {
TestNavHostController(context).apply { TestNavHostController(context).apply {
navigatorProvider.addNavigator(ComposeNavigator()) navigatorProvider.addNavigator(ComposeNavigator())
graph = createGraph(startDestination = "a") { graph = createGraph(startDestination = "a") {
@ -193,5 +193,4 @@ private fun rememberTestNavController(): TestNavHostController {
} }
} }
} }
return navController
} }

@ -88,3 +88,17 @@ fun MacrobenchmarkScope.forYouScrollFeedDownUp() {
val feedList = device.findObject(By.res("forYou:feed")) val feedList = device.findObject(By.res("forYou:feed"))
device.flingElementDownUp(feedList) device.flingElementDownUp(feedList)
} }
fun MacrobenchmarkScope.setAppTheme(isDark: Boolean) {
when (isDark) {
true -> device.findObject(By.text("Dark")).click()
false -> device.findObject(By.text("Light")).click()
}
device.waitForIdle()
device.findObject(By.text("OK")).click()
// Wait until the top app bar is visible on screen
device.wait(Until.hasObject(By.res("niaTopAppBar")), 2_000)
val topAppBar = device.findObject(By.res("niaTopAppBar"))
topAppBar.wait(Until.hasObject(By.text("Now in Android")), 2_000)
}

@ -0,0 +1,83 @@
/*
* Copyright 2023 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 android.os.Build.VERSION_CODES
import androidx.annotation.RequiresApi
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.PowerCategory
import androidx.benchmark.macro.PowerCategoryDisplayLevel
import androidx.benchmark.macro.PowerMetric
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 com.google.samples.apps.nowinandroid.allowNotifications
import com.google.samples.apps.nowinandroid.foryou.forYouScrollFeedDownUp
import com.google.samples.apps.nowinandroid.foryou.forYouSelectTopics
import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent
import com.google.samples.apps.nowinandroid.foryou.setAppTheme
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@OptIn(ExperimentalMetricApi::class)
@RequiresApi(VERSION_CODES.Q)
@RunWith(AndroidJUnit4::class)
class ScrollTopicListPowerMetricsBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
private val categories = PowerCategory.values()
.associateWith { PowerCategoryDisplayLevel.TOTAL }
@Test
fun benchmarkStateChangeCompilationLight() =
benchmarkStateChangeWithTheme(CompilationMode.Partial(), false)
@Test
fun benchmarkStateChangeCompilationDark() =
benchmarkStateChangeWithTheme(CompilationMode.Partial(), true)
private fun benchmarkStateChangeWithTheme(compilationMode: CompilationMode, isDark: Boolean) =
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric(), PowerMetric(PowerMetric.Energy(categories))),
compilationMode = compilationMode,
iterations = 2,
startupMode = StartupMode.WARM,
setupBlock = {
// Start the app
pressHome()
startActivityAndWait()
allowNotifications()
// Navigate to Settings
device.findObject(By.desc("Settings")).click()
device.waitForIdle()
setAppTheme(isDark)
},
) {
forYouWaitForContent()
forYouSelectTopics()
repeat(3) {
forYouScrollFeedDownUp()
}
}
}

@ -27,6 +27,7 @@ import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.allowNotifications
import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -86,6 +87,7 @@ abstract class AbstractStartupBenchmark(private val startupMode: StartupMode) {
}, },
) { ) {
startActivityAndWait() startActivityAndWait()
allowNotifications()
// Waits until the content is ready to capture Time To Full Display // Waits until the content is ready to capture Time To Full Display
forYouWaitForContent() forYouWaitForContent()
} }

@ -21,7 +21,7 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_OUT=$DIR/app/build/outputs APP_OUT=$DIR/app/build/outputs
export JAVA_HOME="$(cd $DIR/../../../prebuilts/studio/jdk/jdk11/linux && pwd )" export JAVA_HOME="$(cd $DIR/../nowinandroid-prebuilts/jdk17/linux && pwd )"
echo "JAVA_HOME=$JAVA_HOME" echo "JAVA_HOME=$JAVA_HOME"
export ANDROID_HOME="$(cd $DIR/../../../prebuilts/fullsdk/linux && pwd )" export ANDROID_HOME="$(cd $DIR/../../../prebuilts/fullsdk/linux && pwd )"

@ -52,6 +52,13 @@ protobuf {
} }
} }
androidComponents.beforeVariants {
android.sourceSets.register(it.name) {
java.srcDir(buildDir.resolve("generated/source/proto/${it.name}/java"))
kotlin.srcDir(buildDir.resolve("generated/source/proto/${it.name}/kotlin"))
}
}
dependencies { dependencies {
implementation(project(":core:common")) implementation(project(":core:common"))
implementation(project(":core:model")) implementation(project(":core:model"))

@ -75,6 +75,7 @@ import kotlinx.datetime.Instant
import kotlinx.datetime.toJavaInstant import kotlinx.datetime.toJavaInstant
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale import java.util.Locale
import com.google.samples.apps.nowinandroid.core.designsystem.R as DesignsystemR import com.google.samples.apps.nowinandroid.core.designsystem.R as DesignsystemR
@ -252,8 +253,11 @@ fun dateFormatted(publishDate: Instant): String {
} }
} }
return DateTimeFormatter.ofPattern("MMM d, yyyy") return DateTimeFormatter
.withZone(zoneId).format(publishDate.toJavaInstant()) .ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.getDefault())
.withZone(zoneId)
.format(publishDate.toJavaInstant())
} }
@Composable @Composable

@ -18,22 +18,20 @@ package com.google.samples.apps.nowinandroid.feature.interests
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.component.DynamicAsyncImage import com.google.samples.apps.nowinandroid.core.designsystem.component.DynamicAsyncImage
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
@ -51,23 +49,18 @@ fun InterestsItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
iconModifier: Modifier = Modifier, iconModifier: Modifier = Modifier,
description: String = "", description: String = "",
itemSeparation: Dp = 16.dp,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.weight(1f)
.clickable { onClick() }
.padding(vertical = itemSeparation),
) { ) {
ListItem(
leadingContent = {
InterestsIcon(topicImageUrl, iconModifier.size(64.dp)) InterestsIcon(topicImageUrl, iconModifier.size(64.dp))
Spacer(modifier = Modifier.width(24.dp)) },
InterestContent(name, description) headlineContent = {
} Text(text = name)
},
supportingContent = {
Text(text = description)
},
trailingContent = {
NiaIconToggleButton( NiaIconToggleButton(
checked = following, checked = following,
onCheckedChange = onFollowButtonClick, onCheckedChange = onFollowButtonClick,
@ -88,26 +81,14 @@ fun InterestsItem(
) )
}, },
) )
} },
} colors = ListItemDefaults.colors(
containerColor = Color.Transparent,
@Composable
private fun InterestContent(name: String, description: String, modifier: Modifier = Modifier) {
Column(modifier) {
Text(
text = name,
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(
vertical = if (description.isEmpty()) 0.dp else 4.dp,
), ),
modifier = modifier
.semantics(mergeDescendants = true) { /* no-op */ }
.clickable(enabled = true, onClick = onClick),
) )
if (description.isNotEmpty()) {
Text(
text = description,
style = MaterialTheme.typography.bodyMedium,
)
}
}
} }
@Composable @Composable

@ -20,11 +20,14 @@ import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.hasScrollToNodeAction
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToIndex
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.DARK
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.ANDROID
@ -139,15 +142,18 @@ class SearchScreenTest {
composeTestRule composeTestRule
.onNodeWithText(topicsString) .onNodeWithText(topicsString)
.assertIsDisplayed() .assertIsDisplayed()
val scrollableNode = composeTestRule
.onAllNodes(hasScrollToNodeAction())
.onFirst()
followableTopicTestData.forEachIndexed { index, followableTopic ->
scrollableNode.performScrollToIndex(index)
composeTestRule composeTestRule
.onNodeWithText(followableTopicTestData[0].topic.name) .onNodeWithText(followableTopic.topic.name)
.assertIsDisplayed()
composeTestRule
.onNodeWithText(followableTopicTestData[1].topic.name)
.assertIsDisplayed()
composeTestRule
.onNodeWithText(followableTopicTestData[2].topic.name)
.assertIsDisplayed() .assertIsDisplayed()
}
composeTestRule composeTestRule
.onAllNodesWithContentDescription(followButtonContentDesc) .onAllNodesWithContentDescription(followButtonContentDesc)

@ -38,3 +38,7 @@ android.nonTransitiveRClass=true
# https://developer.android.com/build/releases/gradle-plugin#default-changes # https://developer.android.com/build/releases/gradle-plugin#default-changes
android.defaults.buildfeatures.resvalues=false android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false android.defaults.buildfeatures.shaders=false
# Use newer lint version to support Kotlin 1.9 and corresponding kotlinx-metadata-jvm
# https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html
android.experimental.lint.version=8.1.0-rc01

@ -6,7 +6,7 @@ androidxActivity = "1.7.0"
androidxAppCompat = "1.5.1" androidxAppCompat = "1.5.1"
androidxBrowser = "1.4.0" androidxBrowser = "1.4.0"
androidxComposeBom = "2023.06.01" androidxComposeBom = "2023.06.01"
androidxComposeCompiler = "1.4.8" androidxComposeCompiler = "1.5.0"
androidxComposeRuntimeTracing = "1.0.0-alpha03" androidxComposeRuntimeTracing = "1.0.0-alpha03"
androidxCore = "1.9.0" androidxCore = "1.9.0"
androidxCoreSplashscreen = "1.0.0" androidxCoreSplashscreen = "1.0.0"
@ -34,18 +34,18 @@ firebasePerfPlugin = "1.4.2"
gmsPlugin = "4.3.14" gmsPlugin = "4.3.14"
googleOss = "17.0.1" googleOss = "17.0.1"
googleOssPlugin = "0.10.6" googleOssPlugin = "0.10.6"
hilt = "2.46.1" hilt = "2.47"
hiltExt = "1.0.0" hiltExt = "1.0.0"
jacoco = "0.8.7" jacoco = "0.8.7"
junit4 = "4.13.2" junit4 = "4.13.2"
kotlin = "1.8.22" kotlin = "1.9.0"
kotlinxCoroutines = "1.6.4" kotlinxCoroutines = "1.6.4"
kotlinxDatetime = "0.4.0" kotlinxDatetime = "0.4.0"
kotlinxSerializationJson = "1.5.1" kotlinxSerializationJson = "1.5.1"
ksp = "1.8.22-1.0.11" ksp = "1.9.0-1.0.11"
lint = "30.3.1" lint = "31.0.2"
okhttp = "4.10.0" okhttp = "4.10.0"
protobuf = "3.23.0" protobuf = "3.23.4"
protobufPlugin = "0.9.3" protobufPlugin = "0.9.3"
retrofit = "2.9.0" retrofit = "2.9.0"
retrofitKotlinxSerializationJson = "1.0.0" retrofitKotlinxSerializationJson = "1.0.0"

@ -36,7 +36,10 @@ echo y | ${ANDROID_HOME}/tools/bin/sdkmanager --licenses
cd $KOKORO_ARTIFACTS_DIR/git/nowinandroid cd $KOKORO_ARTIFACTS_DIR/git/nowinandroid
# The build needs Java 17, set it as the default Java version. # The build needs Java 17, set it as the default Java version.
sudo update-java-alternatives --set java-1.17.0-openjdk-amd64 sudo apt-get update
sudo apt-get install -y openjdk-17-jdk
sudo update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java
java -version
# Also clear JAVA_HOME variable so java -version is used instead # Also clear JAVA_HOME variable so java -version is used instead
export JAVA_HOME= export JAVA_HOME=

@ -21,12 +21,11 @@ set -e
set -x set -x
# Run the normal build, but replace the default virtual devices with physical ones. # Run the normal build, but replace the default virtual devices with physical ones.
# hammerhead | Nexus 5 | API 23 | Phone
# walleye | Pixel 2 | API 27 | Phone # walleye | Pixel 2 | API 27 | Phone
# gts4lltevzw | Galaxy Tab S4 | API 28 | Tablet # gts4lltevzw | Galaxy Tab S4 | API 28 | Tablet
# a10 | Samsung A10 | API 29 | Phone # a10 | Samsung A10 | API 29 | Phone
# redfin | Pixel 5e | API 30 | Phone # redfin | Pixel 5e | API 30 | Phone
# oriole | Pixel 6 | API 31 | Phone # oriole | Pixel 6 | API 31 | Phone
bash $KOKORO_ARTIFACTS_DIR/git/nowinandroid/kokoro/build.sh "hammerhead,walleye,gts4lltevzw,a10,redfin,oriole" "23,27,28,29,30,31" bash $KOKORO_ARTIFACTS_DIR/git/nowinandroid/kokoro/build.sh "walleye,gts4lltevzw,a10,redfin,oriole" "27,28,29,30,31"
exit $? exit $?

Loading…
Cancel
Save