@ -1,17 +1,25 @@
|
||||
Thanks for submitting a pull request. Please include the following information.
|
||||
**DO NOT CREATE A PULL REQUEST WITHOUT READING THESE INSTRUCTIONS**
|
||||
|
||||
**What I have done and why**
|
||||
Include a summary of what your pull request contains, and why you have made these changes.
|
||||
## Instructions
|
||||
Thanks for submitting a pull request. To accept your pull request we need you do a few things:
|
||||
|
||||
**If this is your first pull request**
|
||||
|
||||
- [Sign the contributors license agreement](https://cla.developers.google.com/)
|
||||
|
||||
**Ensure tests pass and code is formatted correctly**
|
||||
|
||||
Fixes #<issue_number_goes_here>
|
||||
- Run local tests on the `DemoDebug` variant by running `./gradlew testDemoDebug`
|
||||
- Fix code formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply`
|
||||
|
||||
**Do tests pass?**
|
||||
- [ ] Run local tests on `DemoDebug` variant: `./gradlew testDemoDebug`
|
||||
- [ ] Check formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply`
|
||||
**Add a description**
|
||||
|
||||
**Is this your first pull request?**
|
||||
- [ ] [Sign the CLA](https://cla.developers.google.com/)
|
||||
- [ ] Run `./tools/setup.sh`
|
||||
- [ ] Import the code formatting style as explained in [the setup script](/tools/setup.sh#L40).
|
||||
We need to know what you've done and why you've done it. Include a summary of what your pull request contains, and why you have made these changes. Include links to any relevant issues which it fixes.
|
||||
|
||||
[Here's an example](https://github.com/android/nowinandroid/pull/1257).
|
||||
|
||||
**NOW DELETE THIS LINE AND EVERYTHING ABOVE IT**
|
||||
|
||||
**What I have done and why**
|
||||
|
||||
\<add your PR description here\>
|
||||
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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.ui
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.material3.SnackbarDuration.Indefinite
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
||||
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.test.DeviceConfigurationOverride
|
||||
import androidx.compose.ui.test.ForcedSize
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.takahirom.roborazzi.captureRoboImage
|
||||
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
|
||||
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
|
||||
import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository
|
||||
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
|
||||
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
|
||||
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
|
||||
import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions
|
||||
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
|
||||
import dagger.hilt.android.testing.BindValue
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.GraphicsMode
|
||||
import org.robolectric.annotation.LooperMode
|
||||
import java.util.TimeZone
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Tests that the Snackbar is correctly displayed on different screen sizes.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@GraphicsMode(GraphicsMode.Mode.NATIVE)
|
||||
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
|
||||
// This allows enough room to render the content under test without clipping or scaling.
|
||||
@Config(application = HiltTestApplication::class, qualifiers = "w1000dp-h1000dp-480dpi")
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
@HiltAndroidTest
|
||||
class SnackbarScreenshotTests {
|
||||
|
||||
/**
|
||||
* Manages the components' state and is used to perform injection on your test
|
||||
*/
|
||||
@get:Rule(order = 0)
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
/**
|
||||
* Create a temporary folder used to create a Data Store file. This guarantees that
|
||||
* the file is removed in between each test, preventing a crash.
|
||||
*/
|
||||
@BindValue
|
||||
@get:Rule(order = 1)
|
||||
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
|
||||
|
||||
/**
|
||||
* Use a test activity to set the content on.
|
||||
*/
|
||||
@get:Rule(order = 2)
|
||||
val composeTestRule = createAndroidComposeRule<HiltComponentActivity>()
|
||||
|
||||
@Inject
|
||||
lateinit var networkMonitor: NetworkMonitor
|
||||
|
||||
@Inject
|
||||
lateinit var timeZoneMonitor: TimeZoneMonitor
|
||||
|
||||
@Inject
|
||||
lateinit var userDataRepository: FakeUserDataRepository
|
||||
|
||||
@Inject
|
||||
lateinit var topicsRepository: TopicsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var userNewsResourceRepository: UserNewsResourceRepository
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
|
||||
// Configure user data
|
||||
runBlocking {
|
||||
userDataRepository.setShouldHideOnboarding(true)
|
||||
|
||||
userDataRepository.setFollowedTopicIds(
|
||||
setOf(topicsRepository.getTopics().first().first().id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setTimeZone() {
|
||||
// Make time zone deterministic in tests
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun phone_noSnackbar() {
|
||||
val snackbarHostState = SnackbarHostState()
|
||||
testSnackbarScreenshotWithSize(
|
||||
snackbarHostState,
|
||||
400.dp,
|
||||
500.dp,
|
||||
"snackbar_compact_medium_noSnackbar",
|
||||
action = { },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun snackbarShown_phone() {
|
||||
val snackbarHostState = SnackbarHostState()
|
||||
testSnackbarScreenshotWithSize(
|
||||
snackbarHostState,
|
||||
400.dp,
|
||||
500.dp,
|
||||
"snackbar_compact_medium",
|
||||
) {
|
||||
snackbarHostState.showSnackbar(
|
||||
"This is a test snackbar message",
|
||||
actionLabel = "Action Label",
|
||||
duration = Indefinite,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun snackbarShown_foldable() {
|
||||
val snackbarHostState = SnackbarHostState()
|
||||
testSnackbarScreenshotWithSize(
|
||||
snackbarHostState,
|
||||
600.dp,
|
||||
600.dp,
|
||||
"snackbar_medium_medium",
|
||||
) {
|
||||
snackbarHostState.showSnackbar(
|
||||
"This is a test snackbar message",
|
||||
actionLabel = "Action Label",
|
||||
duration = Indefinite,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun snackbarShown_tablet() {
|
||||
val snackbarHostState = SnackbarHostState()
|
||||
testSnackbarScreenshotWithSize(
|
||||
snackbarHostState,
|
||||
900.dp,
|
||||
900.dp,
|
||||
"snackbar_expanded_expanded",
|
||||
) {
|
||||
snackbarHostState.showSnackbar(
|
||||
"This is a test snackbar message",
|
||||
actionLabel = "Action Label",
|
||||
duration = Indefinite,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun testSnackbarScreenshotWithSize(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
width: Dp,
|
||||
height: Dp,
|
||||
screenshotName: String,
|
||||
action: suspend () -> Unit,
|
||||
) {
|
||||
lateinit var scope: CoroutineScope
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(
|
||||
// Replaces images with placeholders
|
||||
LocalInspectionMode provides true,
|
||||
) {
|
||||
scope = rememberCoroutineScope()
|
||||
|
||||
DeviceConfigurationOverride(
|
||||
DeviceConfigurationOverride.ForcedSize(DpSize(width, height)),
|
||||
) {
|
||||
BoxWithConstraints {
|
||||
val appState = rememberNiaAppState(
|
||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
||||
DpSize(maxWidth, maxHeight),
|
||||
),
|
||||
networkMonitor = networkMonitor,
|
||||
userNewsResourceRepository = userNewsResourceRepository,
|
||||
timeZoneMonitor = timeZoneMonitor,
|
||||
)
|
||||
NiaTheme {
|
||||
NiaApp(appState, snackbarHostState, false, {}, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
action()
|
||||
}
|
||||
|
||||
composeTestRule.onRoot()
|
||||
.captureRoboImage(
|
||||
"src/testDemo/screenshots/$screenshotName.png",
|
||||
roborazziOptions = DefaultRoborazziOptions,
|
||||
)
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 260 KiB |
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 811 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.core.domain
|
||||
|
||||
import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A use case which returns total count of *Fts tables
|
||||
*/
|
||||
class GetSearchContentsCountUseCase @Inject constructor(
|
||||
private val searchContentsRepository: SearchContentsRepository,
|
||||
) {
|
||||
operator fun invoke(): Flow<Int> =
|
||||
searchContentsRepository.getSearchContentsCount()
|
||||
}
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 863 B After Width: | Height: | Size: 784 B |
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 778 B |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 893 B After Width: | Height: | Size: 796 B |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 851 B After Width: | Height: | Size: 776 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.4 KiB |
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2024 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.feature.topic
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
|
||||
|
||||
@Composable
|
||||
fun TopicDetailPlaceholder(modifier: Modifier = Modifier) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
|
||||
shape = RoundedCornerShape(24.dp, 24.dp, 0.dp, 0.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
20.dp,
|
||||
alignment = Alignment.CenterVertically,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.feature_topic_ic_topic_placeholder),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.feature_topic_select_an_interest),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 200, heightDp = 300)
|
||||
@Composable
|
||||
fun TopicDetailPlaceholderPreview() {
|
||||
NiaTheme {
|
||||
TopicDetailPlaceholder()
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 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
|
||||
|
||||
http://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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M1,14.125C1,10.742 3.742,8 7.125,8H49.875C53.258,8 56,10.742 56,14.125V56.875C56,60.258 53.258,63 49.875,63H7.125C3.742,63 1,60.258 1,56.875V14.125Z"
|
||||
android:strokeColor="#8C4190"
|
||||
android:strokeWidth="2" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M31,18.79C31,17.437 31.307,16.536 31.768,15.988C32.203,15.469 32.895,15.125 34.008,15.125H47.367C48.119,15.125 48.846,15.306 49.377,15.775C49.883,16.223 50.375,17.08 50.375,18.79V27.272C50.375,28.871 50.054,29.737 49.63,30.208C49.229,30.654 48.56,30.938 47.367,30.938H34.008C32.719,30.938 32.046,30.497 31.65,29.94C31.211,29.322 31,28.399 31,27.272V18.79Z"
|
||||
android:strokeColor="#8C4190"
|
||||
android:strokeWidth="2" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M6,23a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||
android:strokeColor="#8C4190"
|
||||
android:strokeWidth="2" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M10,23l4,4l7.5,-7.5"
|
||||
android:strokeColor="#8C4190"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="1.6"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M30,42H50 M30,52H50 M30,47H44 M30,37H42 M46,37H50 M30,57H35"
|
||||
android:strokeColor="#8C4190"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="2" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M12,5C12,5 14,2 19,2C22.699,2 40.627,2 49.707,2C53.103,2 56.349,3.349 58.75,5.75V5.75C61.151,8.151 62.5,11.408 62.5,14.803V51"
|
||||
android:strokeColor="#8C4190"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="2" />
|
||||
</vector>
|