Merge branch 'android:main' into loading-progress-for-image

pull/850/head^2
Qamar A. Safadi 1 year ago committed by GitHub
commit 2db3e0c804
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -121,12 +121,3 @@ dependencies {
implementation(libs.kotlinx.coroutines.guava) implementation(libs.kotlinx.coroutines.guava)
implementation(libs.coil.kt) implementation(libs.coil.kt)
} }
// androidx.test is forcing JUnit, 4.12. This forces it to use 4.13
configurations.configureEach {
resolutionStrategy {
force(libs.junit4)
// Temporary workaround for https://issuetracker.google.com/174733673
force("org.objenesis:objenesis:2.6")
}
}

@ -43,6 +43,6 @@ class MainActivityViewModel @Inject constructor(
} }
sealed interface MainActivityUiState { sealed interface MainActivityUiState {
object Loading : MainActivityUiState data object Loading : MainActivityUiState
data class Success(val userData: UserData) : MainActivityUiState data class Success(val userData: UserData) : MainActivityUiState
} }

@ -21,7 +21,6 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices
import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.configurePrintApksTask
import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests
import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
@ -46,13 +45,6 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
configurePrintApksTask(this) configurePrintApksTask(this)
disableUnnecessaryAndroidTests(target) disableUnnecessaryAndroidTests(target)
} }
configurations.configureEach {
resolutionStrategy {
force(libs.findLibrary("junit4").get())
// Temporary workaround for https://issuetracker.google.com/174733673
force("org.objenesis:objenesis:2.6")
}
}
dependencies { dependencies {
add("androidTestImplementation", kotlin("test")) add("androidTestImplementation", kotlin("test"))
add("testImplementation", kotlin("test")) add("testImplementation", kotlin("test"))

@ -1,21 +0,0 @@
/*
* 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.core.decoder
interface StringDecoder {
fun decodeString(encodedString: String): String
}

@ -1,24 +0,0 @@
/*
* 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.core.decoder
import android.net.Uri
import javax.inject.Inject
class UriDecoder @Inject constructor() : StringDecoder {
override fun decodeString(encodedString: String): String = Uri.decode(encodedString)
}

@ -1,31 +0,0 @@
/*
* 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.core.decoder.di
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.core.decoder.UriDecoder
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
abstract class StringDecoderModule {
@Binds
abstract fun bindStringDecoder(uriDecoder: UriDecoder): StringDecoder
}

@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.onStart
sealed interface Result<out T> { sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T> data class Success<T>(val data: T) : Result<T>
data class Error(val exception: Throwable? = null) : Result<Nothing> data class Error(val exception: Throwable? = null) : Result<Nothing>
object Loading : Result<Nothing> data object Loading : Result<Nothing>
} }
fun <T> Flow<T>.asResult(): Flow<Result<T>> { fun <T> Flow<T>.asResult(): Flow<Result<T>> {

@ -1,24 +0,0 @@
/*
* 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.core.testing.decoder
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import javax.inject.Inject
class FakeStringDecoder @Inject constructor() : StringDecoder {
override fun decodeString(encodedString: String): String = encodedString
}

@ -1,35 +0,0 @@
/*
* 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.core.testing.di
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.core.decoder.di.StringDecoderModule
import com.google.samples.apps.nowinandroid.core.testing.decoder.FakeStringDecoder
import dagger.Binds
import dagger.Module
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
@Module
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [StringDecoderModule::class],
)
abstract class TestStringDecoderModule {
@Binds
abstract fun bindsStringDecoder(fakeStringDecoder: FakeStringDecoder): StringDecoder
}

@ -112,7 +112,7 @@ sealed interface NewsFeedUiState {
/** /**
* The feed is still loading. * The feed is still loading.
*/ */
object Loading : NewsFeedUiState data object Loading : NewsFeedUiState
/** /**
* The feed is loaded with the given list of news resources. * The feed is loaded with the given list of news resources.

@ -25,17 +25,17 @@ sealed interface OnboardingUiState {
/** /**
* The onboarding state is loading. * The onboarding state is loading.
*/ */
object Loading : OnboardingUiState data object Loading : OnboardingUiState
/** /**
* The onboarding state was unable to load. * The onboarding state was unable to load.
*/ */
object LoadFailed : OnboardingUiState data object LoadFailed : OnboardingUiState
/** /**
* There is no onboarding state. * There is no onboarding state.
*/ */
object NotShown : OnboardingUiState data object NotShown : OnboardingUiState
/** /**
* There is a onboarding state, with the given lists of topics. * There is a onboarding state, with the given lists of topics.

@ -53,11 +53,11 @@ class InterestsViewModel @Inject constructor(
} }
sealed interface InterestsUiState { sealed interface InterestsUiState {
object Loading : InterestsUiState data object Loading : InterestsUiState
data class Interests( data class Interests(
val topics: List<FollowableTopic>, val topics: List<FollowableTopic>,
) : InterestsUiState ) : InterestsUiState
object Empty : InterestsUiState data object Empty : InterestsUiState
} }

@ -23,7 +23,6 @@ import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQue
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
@ -43,13 +42,12 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class TopicViewModel @Inject constructor( class TopicViewModel @Inject constructor(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository, private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository, topicsRepository: TopicsRepository,
userNewsResourceRepository: UserNewsResourceRepository, userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() { ) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder) private val topicArgs: TopicArgs = TopicArgs(savedStateHandle)
val topicId = topicArgs.topicId val topicId = topicArgs.topicId

@ -16,7 +16,6 @@
package com.google.samples.apps.nowinandroid.feature.topic.navigation package com.google.samples.apps.nowinandroid.feature.topic.navigation
import android.net.Uri
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.navigation.NavController import androidx.navigation.NavController
@ -24,19 +23,23 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.feature.topic.TopicRoute import com.google.samples.apps.nowinandroid.feature.topic.TopicRoute
import java.net.URLDecoder
import java.net.URLEncoder
import kotlin.text.Charsets.UTF_8
private val URL_CHARACTER_ENCODING = UTF_8.name()
@VisibleForTesting @VisibleForTesting
internal const val topicIdArg = "topicId" internal const val topicIdArg = "topicId"
internal class TopicArgs(val topicId: String) { internal class TopicArgs(val topicId: String) {
constructor(savedStateHandle: SavedStateHandle, stringDecoder: StringDecoder) : constructor(savedStateHandle: SavedStateHandle) :
this(stringDecoder.decodeString(checkNotNull(savedStateHandle[topicIdArg]))) this(URLDecoder.decode(checkNotNull(savedStateHandle[topicIdArg]), URL_CHARACTER_ENCODING))
} }
fun NavController.navigateToTopic(topicId: String) { fun NavController.navigateToTopic(topicId: String) {
val encodedId = Uri.encode(topicId) val encodedId = URLEncoder.encode(topicId, URL_CHARACTER_ENCODING)
this.navigate("topic_route/$encodedId") { this.navigate("topic_route/$encodedId") {
launchSingleTop = true launchSingleTop = true
} }

@ -22,7 +22,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.testing.decoder.FakeStringDecoder
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
@ -63,7 +62,6 @@ class TopicViewModelTest {
fun setup() { fun setup() {
viewModel = TopicViewModel( viewModel = TopicViewModel(
savedStateHandle = SavedStateHandle(mapOf(topicIdArg to testInputTopics[0].topic.id)), savedStateHandle = SavedStateHandle(mapOf(topicIdArg to testInputTopics[0].topic.id)),
stringDecoder = FakeStringDecoder(),
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
topicsRepository = topicsRepository, topicsRepository = topicsRepository,
userNewsResourceRepository = userNewsResourceRepository, userNewsResourceRepository = userNewsResourceRepository,

Loading…
Cancel
Save