Merge pull request #417 from SimonMarquis/patch/assets-as-json-files

Migrate hard-coded JSON data into JSON assets files
pull/445/head
Milosz Moczkowski 2 years ago committed by GitHub
commit 3ca68d49ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -22,6 +22,11 @@ plugins {
android { android {
namespace = "com.google.samples.apps.nowinandroid.core.data" namespace = "com.google.samples.apps.nowinandroid.core.data"
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
} }
dependencies { dependencies {

@ -21,16 +21,18 @@ import com.google.samples.apps.nowinandroid.core.data.repository.AuthorsReposito
import com.google.samples.apps.nowinandroid.core.model.data.Author import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import java.io.InputStream
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
/** /**
* Fake implementation of the [AuthorsRepository] that returns hardcoded authors. * Fake implementation of the [AuthorsRepository] that returns hardcoded authors.
@ -41,11 +43,14 @@ import kotlinx.serialization.json.Json
class FakeAuthorsRepository @Inject constructor( class FakeAuthorsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json, private val networkJson: Json,
private val assets: FakeAssetManager,
) : AuthorsRepository { ) : AuthorsRepository {
override fun getAuthorsStream(): Flow<List<Author>> = flow { override fun getAuthorsStream(): Flow<List<Author>> = flow {
emit( emit(
networkJson.decodeFromString<List<NetworkAuthor>>(FakeDataSource.authors).map { assets.open(FakeDataSource.AUTHORS)
.use<InputStream, List<NetworkAuthor>>(networkJson::decodeFromStream)
.map {
Author( Author(
id = it.id, id = it.id,
name = it.name, name = it.name,

@ -24,15 +24,17 @@ import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
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.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import java.io.InputStream
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
/** /**
* Fake implementation of the [NewsRepository] that retrieves the news resources from a JSON String. * Fake implementation of the [NewsRepository] that retrieves the news resources from a JSON String.
@ -42,13 +44,15 @@ import kotlinx.serialization.json.Json
*/ */
class FakeNewsRepository @Inject constructor( class FakeNewsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json private val networkJson: Json,
private val assets: FakeAssetManager,
) : NewsRepository { ) : NewsRepository {
override fun getNewsResourcesStream(): Flow<List<NewsResource>> = override fun getNewsResourcesStream(): Flow<List<NewsResource>> =
flow { flow {
emit( emit(
networkJson.decodeFromString<List<NetworkNewsResource>>(FakeDataSource.data) assets.open(FakeDataSource.DATA)
.use<InputStream, List<NetworkNewsResource>>(networkJson::decodeFromStream)
.map(NetworkNewsResource::asEntity) .map(NetworkNewsResource::asEntity)
.map(NewsResourceEntity::asExternalModel) .map(NewsResourceEntity::asExternalModel)
) )
@ -61,13 +65,15 @@ class FakeNewsRepository @Inject constructor(
): Flow<List<NewsResource>> = ): Flow<List<NewsResource>> =
flow { flow {
emit( emit(
networkJson.decodeFromString<List<NetworkNewsResource>>(FakeDataSource.data) assets.open(FakeDataSource.DATA).use { stream ->
networkJson.decodeFromStream<List<NetworkNewsResource>>(stream)
.filter { .filter {
it.authors.intersect(filterAuthorIds).isNotEmpty() || it.authors.intersect(filterAuthorIds).isNotEmpty() ||
it.topics.intersect(filterTopicIds).isNotEmpty() it.topics.intersect(filterTopicIds).isNotEmpty()
} }
.map(NetworkNewsResource::asEntity) .map(NetworkNewsResource::asEntity)
.map(NewsResourceEntity::asExternalModel) .map(NewsResourceEntity::asExternalModel)
}
) )
} }
.flowOn(ioDispatcher) .flowOn(ioDispatcher)

@ -21,16 +21,18 @@ import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepositor
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.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import java.io.InputStream
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
/** /**
* Fake implementation of the [TopicsRepository] that retrieves the topics from a JSON String, and * Fake implementation of the [TopicsRepository] that retrieves the topics from a JSON String, and
@ -42,10 +44,13 @@ import kotlinx.serialization.json.Json
class FakeTopicsRepository @Inject constructor( class FakeTopicsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json, private val networkJson: Json,
private val assets: FakeAssetManager,
) : TopicsRepository { ) : TopicsRepository {
override fun getTopicsStream(): Flow<List<Topic>> = flow<List<Topic>> { override fun getTopicsStream(): Flow<List<Topic>> = flow {
emit( emit(
networkJson.decodeFromString<List<NetworkTopic>>(FakeDataSource.topicsData).map { assets.open(FakeDataSource.TOPICS)
.use<InputStream, List<NetworkTopic>>(networkJson::decodeFromStream)
.map {
Topic( Topic(
id = it.id, id = it.id,
name = it.name, name = it.name,

@ -17,12 +17,13 @@
package com.google.samples.apps.nowinandroid.core.data.testdoubles package com.google.samples.apps.nowinandroid.core.data.testdoubles
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import kotlinx.serialization.decodeFromString import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
enum class CollectionType { enum class CollectionType {
@ -36,16 +37,13 @@ enum class CollectionType {
*/ */
class TestNiaNetworkDataSource : NiaNetworkDataSource { class TestNiaNetworkDataSource : NiaNetworkDataSource {
private val networkJson = Json private val source = FakeNiaNetworkDataSource(UnconfinedTestDispatcher(), Json)
private val allTopics = private val allTopics = runBlocking { source.getTopics() }
networkJson.decodeFromString<List<NetworkTopic>>(FakeDataSource.topicsData)
private val allAuthors = private val allAuthors = runBlocking { source.getAuthors() }
networkJson.decodeFromString<List<NetworkAuthor>>(FakeDataSource.authors)
private val allNewsResources = private val allNewsResources = runBlocking { source.getNewsResources() }
networkJson.decodeFromString<List<NetworkNewsResource>>(FakeDataSource.data)
private val changeLists: MutableMap<CollectionType, List<NetworkChangeList>> = mutableMapOf( private val changeLists: MutableMap<CollectionType, List<NetworkChangeList>> = mutableMapOf(
CollectionType.Topics to allTopics CollectionType.Topics to allTopics

@ -27,6 +27,11 @@ android {
buildConfig = true buildConfig = true
} }
namespace = "com.google.samples.apps.nowinandroid.core.network" namespace = "com.google.samples.apps.nowinandroid.core.network"
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
} }
secrets { secrets {

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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
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.
-->
<lint>
<!--
Lint crashes when it tries to analyse a file without a package name:
java.lang.IllegalStateException: () -> kotlin.String at org.jetbrains.kotlin.asJava.classes.KtLightClassForFacadeImpl$Companion.createForFacadeNoCache
-->
<issue id="LintError">
<ignore path="**/JvmUnitTestFakeAssetManager.kt" />
</issue>
</lint>

@ -20,26 +20,13 @@ import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import kotlinx.serialization.json.Json
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
interface NetworkModule { interface FlavoredNetworkModule {
@Binds @Binds
fun bindsNiaNetwork( fun FakeNiaNetworkDataSource.binds(): NiaNetworkDataSource
niANetwork: FakeNiaNetworkDataSource
): NiaNetworkDataSource
companion object {
@Provides
@Singleton
fun providesNetworkJson(): Json = Json {
ignoreUnknownKeys = true
}
}
} }

@ -0,0 +1,794 @@
[
{
"id": "1",
"name": "Márton Braun",
"mediumPage": "https://medium.com/@zsmb13",
"twitter": "https://twitter.com/zsmb13",
"imageUrl": "https://pbs.twimg.com/profile_images/1047591879431397377/nNjQUt0F_400x400.jpg",
"bio": "Márton is an Android Developer Relations Engineer at Google, working on anything and everything Kotlin."
},
{
"id": "2",
"name": "Greg Hartrell",
"mediumPage": "https://medium.com/@greghart/about",
"twitter": "https://twitter.com/ghartrell",
"imageUrl": "https://pbs.twimg.com/profile_images/971602488984940547/plY3bBRz_400x400.jpg",
"bio": "Greg Hartrell is a product leader with a 15+ year history helping large teams build high performing software products and businesses. At Google, hes a Product Management Director at Google Play / Android, covering product lines from games, to digital content and platform expansion. Hes previously been VP of Product Development at Capcom/Beeline, and a product leader for 8 years at Microsoft for Xbox Live/360 and the Windows platform. When hes not speaking, he enjoys looting random objects out of boxes, jumping from platform to platform, and grinding while afk."
},
{
"id": "3",
"name": "Simona Stojanovic",
"mediumPage": "https://medium.com/@anomisSi",
"twitter": "https://twitter.com/anomisSi",
"imageUrl": "https://pbs.twimg.com/profile_images/1437506849016778756/pG0NZALw_400x400.jpg",
"bio": "Android Developer Relations Engineer @Google, working on the Compose team and taking care of Layouts & Navigation."
},
{
"id": "4",
"name": "Andrew Flynn",
"mediumPage": "",
"twitter": "",
"imageUrl": "https://lh3.googleusercontent.com/xfI5PujnEqdQ4GlsRZAvVOGiC_v3VTz6wYM8kxaPyOtXIZY4-BDYOr-d-cjN8kxAkr4yAthuWu2gTZ7t-do=s1016-rw-no",
"bio": "Andrew joined Google in 2007 after graduating from Dartmouth College. He has worked on a wide range of team from Ads to Android devices to Google Fi, and currently works on the Play Store."
},
{
"id": "5",
"name": "Jon Boekenoogen",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "6",
"name": "Florina Muntenescu",
"mediumPage": "https://medium.com/@florina.muntenescu",
"twitter": "https://twitter.com/FMuntenescu",
"imageUrl": "https://pbs.twimg.com/profile_images/726323972686503937/nZkTQVQJ_400x400.jpg",
"bio": "Florina is working as an Android Developer Relations Engineer at Google, helping developers build beautiful apps with Jetpack Compose. She has been working with Android for more than 10 years, previous work covering news at upday, payment solutions at payleven and navigation services at Garmin."
},
{
"id": "7",
"name": "Lidia Gaymond",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "8",
"name": "Vicki Amin",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "9",
"name": "Marcel Pintó",
"mediumPage": "https://medium.com/@marxallski",
"twitter": "https://twitter.com/marxallski",
"imageUrl": "https://pbs.twimg.com/profile_images/1196804242310234112/6pq8qguX_400x400.jpg",
"bio": ""
},
{
"id": "10",
"name": "Krish Vitaldevara",
"mediumPage": "",
"twitter": "https://twitter.com/vitaldevara",
"imageUrl": "https://pbs.twimg.com/profile_images/1326982232100122626/GDN-QoX-_400x400.png",
"bio": ""
},
{
"id": "11",
"name": "Gerry Fan",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "12",
"name": "Pietro Maggi",
"mediumPage": "https://medium.com/@pmaggi",
"twitter": "https://twitter.com/pfmaggi",
"imageUrl": "https://pbs.twimg.com/profile_images/1149328128868794370/T59BFarK_400x400.png",
"bio": "Pietro joined Android DevRel in 2018, and has worked supporting Jetpack WorkManager, large screen devices and lately, Android Enterprise."
},
{
"id": "13",
"name": "Rohan Shah",
"mediumPage": "",
"twitter": "twitter.com/rohanscloud",
"imageUrl": "https://pbs.twimg.com/profile_images/828723313396502530/dfVBFeZP_400x400.jpg",
"bio": "Product Manager on the System UI team, experienced Android developer in a past life. Focused on customization, gesture navigation, and smooth motion."
},
{
"id": "14",
"name": "Dave Burke",
"mediumPage": "",
"twitter": "https://twitter.com/davey_burke",
"imageUrl": "https://pbs.twimg.com/profile_images/1035017742825357312/f0IHVAG1_400x400.jpg",
"bio": ""
},
{
"id": "15",
"name": "Meghan Mehta",
"mediumPage": "https://medium.com/@magicalmeghan",
"twitter": "https://twitter.com/adressyengineer",
"imageUrl": "https://pbs.twimg.com/profile_images/1121847353214824449/wIB-dD0__400x400.jpg",
"bio": "Meghan has been in Google DevRel since 2018 and loves every minute of it. She mostly works on training and is passionate about making learning Android accessible to all. Outside of work you can find her singing, dancing, or petting dogs."
},
{
"id": "16",
"name": "Anna Bernbaum",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Anna Bernbaum is a Product Manager for Wear OS, she specifically focusses on Watch Faces, Complications and Tiles."
},
{
"id": "17",
"name": "Adarsh Fernando",
"mediumPage": "",
"twitter": "https://twitter.com/AdarshFernando",
"imageUrl": "https://pbs.twimg.com/profile_images/1291755351298121728/SFGD_KCm_400x400.jpg",
"bio": ""
},
{
"id": "18",
"name": "Madan Ankapura",
"mediumPage": "",
"twitter": "https://twitter.com/madan_ankapura",
"imageUrl": "https://pbs.twimg.com/profile_images/195140822/IMG_0612_400x400.JPG",
"bio": ""
},
{
"id": "19",
"name": "Kateryna Semenova",
"mediumPage": "https://medium.com/@katerynasemenova",
"twitter": "https://twitter.com/SKateryna",
"imageUrl": "https://miro.medium.com/fit/c/176/176/2*MWidJNpRKpwnPhMYw1hBTA.png",
"bio": "Kate joined Google Android DevRel team in 2020 with the focus on Android performance. She worked on Android12 features, such as Splash screens and App links as well as App startup improvements. Right now she is working on new Deep links tools for develoeprs.\n\nBefore Google, Kate worked as an Android engineer at Lyft. She built LyftPink membership, ride passes, subscriptions and users onboarding flow."
},
{
"id": "20",
"name": "Rahul Ravikumar",
"mediumPage": "https://medium.com/@rahulrav",
"twitter": "https://twitter.com/tikurahul",
"imageUrl": "https://pbs.twimg.com/profile_images/839866273160822785/sLb2Ld53_400x400.jpg",
"bio": "Software Engineer on the Toolkit team. Recent projects include Jetpack Macrobenchmarks, Baseline Profiles and WorkManager."
},
{
"id": "21",
"name": "Chris Craik",
"mediumPage": "https://medium.com/@chriscraik",
"twitter": "https://twitter.com/chris_craik",
"imageUrl": "https://pbs.twimg.com/profile_images/865356883971919872/EkFpz3r1_400x400.jpg",
"bio": ""
},
{
"id": "22",
"name": "Alex Vanyo",
"mediumPage": "https://medium.com/@alexvanyo",
"twitter": "https://twitter.com/alex_vanyo",
"imageUrl": "https://pbs.twimg.com/profile_images/1431339735931305989/nOE2mmi2_400x400.jpg",
"bio": "Alex joined Android DevRel in 2021, and has worked supporting form factors from small watches to large foldables and tablets. His special interests include insets, Compose, testing and state."
},
{
"id": "23",
"name": "Manuel Vivo",
"mediumPage": "https://medium.com/@manuelvicnt",
"twitter": "https://twitter.com/manuelvicnt",
"imageUrl": "https://pbs.twimg.com/profile_images/1126564755202760705/x3qLaiBB_400x400.jpg",
"bio": "Manuel is an Android Developer Relations Engineer at Google. With previous experience at Capital One, he currently focuses on App Architecture, Kotlin & Coroutines, Dependency Injection and Jetpack Compose."
},
{
"id": "24",
"name": "Arjun Dayal",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "25",
"name": "Murat Yener",
"mediumPage": "https://medium.com/@yenerm",
"twitter": "https://twitter.com/yenerm",
"imageUrl": "https://pbs.twimg.com/profile_images/1201558524385316866/7lBmCYmD_400x400.jpg",
"bio": ""
},
{
"id": "26",
"name": "Alex Saveau",
"mediumPage": "https://medium.com/@SUPERCILEX",
"twitter": "https://twitter.com/SUPERCILEX",
"imageUrl": "https://pbs.twimg.com/profile_images/1093296033759604736/kO9WSDy4_400x400.jpg",
"bio": ""
},
{
"id": "27",
"name": "Paul Lammertsma",
"mediumPage": "https://medium.com/@officesunshine",
"twitter": "https://twitter.com/officesunshine",
"imageUrl": "https://pbs.twimg.com/profile_images/1281278859506331651/2XBTbfIK_400x400.jpg",
"bio": "Paul is an Android Developer Relations Engineer at Google, supporting developers for TVs, watches and large screens. He has passionate about building amazing Android experiences since Froyo."
},
{
"id": "28",
"name": "Caren Chang",
"mediumPage": "https://medium.com/@calren24",
"twitter": "https://twitter.com/calren24",
"imageUrl": "https://pbs.twimg.com/profile_images/1521260707521519617/cW-G-T2R_400x400.jpg",
"bio": "Developer Relations Engineer on the Android team, with a focus on Accessibiliity. "
},
{
"id": "29",
"name": "Mayuri Khinvasara Khabya",
"mediumPage": "https://medium.com/@mayuri.k18",
"twitter": "https://twitter.com/mayuri_k",
"imageUrl": "https://pbs.twimg.com/profile_images/769886964170436608/6tsEB7zi_400x400.jpg",
"bio": "Mayuri is an Android Developer Relations Engineer and has worked across Android TV, App performance and device backup"
},
{
"id": "30",
"name": "Romain Guy",
"mediumPage": "https://medium.com/@romainguy",
"twitter": "https://twitter.com/romainguy",
"imageUrl": "https://pbs.twimg.com/profile_images/459175652105527298/6qGNL0QI_400x400.jpeg",
"bio": "Romain joined the Android team in 2007, working on the UI Toolkit and the Graphics pipeline for many years. He now leads the Toolkit and Jetpack teams."
},
{
"id": "31",
"name": "Chet Haase",
"mediumPage": "https://chethaase.medium.com/",
"twitter": "https://twitter.com/chethaase",
"imageUrl": "https://miro.medium.com/max/3150/1*5pR0GFT8Cosn_zGIdRfc0Q.jpeg",
"bio": "Chet joined the Android team in 2010, where he has worked as an engineer and lead for the UI Toolkit team, as Chief Android Advocate on the Developer Relations team, and now as an engineer on the Graphics team."
},
{
"id": "32",
"name": "Tor Norbye",
"mediumPage": "",
"twitter": "https://twitter.com/tornorbye",
"imageUrl": "https://pbs.twimg.com/profile_images/1411058065839845376/SeUHA-sR_400x400.jpg",
"bio": "Tor joined the Android team in 2010, and leads engineering for Android Studio."
},
{
"id": "33",
"name": "Nicole Laure",
"mediumPage": "",
"twitter": "https://twitter.com/nicolelaure",
"imageUrl": "https://pbs.twimg.com/profile_images/442979550285557760/0nq7QOTn_400x400.jpeg",
"bio": ""
},
{
"id": "34",
"name": "Yigit Boyar",
"mediumPage": "https://medium.com/@yigit",
"twitter": "https://twitter.com/yigitboyar",
"imageUrl": "https://pbs.twimg.com/profile_images/530840695347482624/me4HgEMU_400x400.jpeg",
"bio": "Yigit joined the Android team in 2014 and worked on various projects from RecyclerView to Architecture Components with the dream of making Android development better. Still trying :)"
},
{
"id": "35",
"name": "Sean McQuillan",
"mediumPage": "https://medium.com/@objcode",
"twitter": "https://twitter.com/objcode",
"imageUrl": "https://pbs.twimg.com/profile_images/913524063175286784/nhyO1wkU_400x400.jpg",
"bio": "Software Engineer on the Toolkit team. Working on making text 📜 sparkle ✨. Also helping melting face 🫠 render on your phone."
},
{
"id": "36",
"name": "Ben Weiss",
"mediumPage": "https://medium.com/@keyboardsurfer",
"twitter": "https://twitter.com/keyboardsurfer",
"imageUrl": "https://pbs.twimg.com/profile_images/1455956781830709255/GqeqbgEY_400x400.jpg",
"bio": "Ben works as an Engineer on the Android Developer Relations team. Over the years he contributed to many areas of Android. To get the latest info on what's up with Ben, read one of his articles or follow him on Twitter."
},
{
"id": "37",
"name": "Carmen Jackson",
"mediumPage": "",
"twitter": "",
"imageUrl": "https://media-exp1.licdn.com/dms/image/C5603AQE8wPl4iNUPeA/profile-displayphoto-shrink_200_200/0/1553574292680?e=1655337600&v=beta&t=6zsc9QaC95DM19uXbG3FJQwUIpObp4XeO-WsLzlNGIo",
"bio": "Carmen has been an engineer on the Android Platform Performance team since 2016 and has worked with Android for over 10 years. She currently leads a team focused on improving application performance."
},
{
"id": "38",
"name": "TJ Dahunsi",
"mediumPage": "https://tunjid.medium.com/",
"twitter": "",
"imageUrl": "https://pbs.twimg.com/profile_images/1504815529848152074/iA9Q_QME_400x400.jpg",
"bio": "Tj is an engineer on the Android Developer Relations Team working on app architecture. He's also a connoiseur of fine memes."
},
{
"id": "39",
"name": "Shailen Tuli",
"mediumPage": "",
"twitter": "https://twitter.com/shailentuli",
"imageUrl": "https://pbs.twimg.com/profile_images/1521593961/shailen_400x400.jpg",
"bio": ""
},
{
"id": "40",
"name": "Kailiang Chen",
"mediumPage": "https://medium.com/@bbfee",
"twitter": "https://twitter.com/KailiangChen3",
"imageUrl": "https://avatars.githubusercontent.com/u/1756481?v=4",
"bio": "Kailiang is a software engineer on Android Camera Platform team. He works on Jetpack CameraX. In the past, he worked at Youtube and Uber."
},
{
"id": "41",
"name": "Jeremy Walker",
"mediumPage": "https://medium.com/@codingjeremy",
"twitter": "https://twitter.com/codingjeremy",
"imageUrl": "https://pbs.twimg.com/profile_images/1124334569887428610/etnNE5hz_400x400.png",
"bio": ""
},
{
"id": "42",
"name": "Don Turner",
"mediumPage": "https://medium.com/@donturner",
"twitter": "https://twitter.com/donturner",
"imageUrl": "https://pbs.twimg.com/profile_images/1282701855555018753/xcnlScis_400x400.jpg",
"bio": "Don is an engineer on the Android Developer Relations team. He has founded and led several businesses in the software development, digital marketing and events industries. He joined Google in 2014 and focuses on improving Android app architecture. "
},
{
"id": "43",
"name": "Lilian Young",
"mediumPage": "",
"twitter": "https://twitter.com/memnus",
"imageUrl": "https://developer.android.com/events/dev-summit/images/speakers/lilian_young_720.jpg",
"bio": "Lilian has been part of the Android Vulnerability Rewards Program nearly from the beginning, since joining Android Security Assurance in 2016. As a member of both the vulnerability assessment team and the design review team for upcoming Android versions, they review bugs and features to determine which is which. "
},
{
"id": "44",
"name": "Wenhung Teng",
"mediumPage": "",
"twitter": "",
"imageUrl": "https://lh3.googleusercontent.com/a-/AOh14Ghsnwfup3BUugAxNwQsAD8Ph_CWNrH8SL6Wb8OZ",
"bio": ""
},
{
"id": "45",
"name": "Charcoal Chen",
"mediumPage": "https://medium.com/@charcoalchen",
"twitter": "",
"imageUrl": "https://miro.medium.com/fit/c/262/262/0*XgfVFjuchPekBYfh",
"bio": "Charcoal Chen is a software engineer on Jetpack CameraX team."
},
{
"id": "46",
"name": "Mike Yerou",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "47",
"name": "Peter Visontay",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "48",
"name": "Marcelo Hernandez",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "49",
"name": "Daniel Santiago",
"mediumPage": "https://medium.com/@danysantiago",
"twitter": "",
"imageUrl": "https://miro.medium.com/fit/c/262/262/2*fe7m2z-tRofWYjaiXihi9g.jpeg",
"bio": ""
},
{
"id": "50",
"name": "Brad Corso",
"mediumPage": "https://medium.com/@bcorso1",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "51",
"name": "Jonathan Koren",
"mediumPage": "https://medium.com/@jdkoren",
"twitter": "",
"imageUrl": "",
"bio": "Jonathan is an Android Developer Relations Engineer at Google, with particular interest in UI and app architecture. In the past, he developed Android applications and libraries at Yahoo and Personal Capital. In the future, he will have fixed the bugs he is writing in the present (hopefully)."
},
{
"id": "52",
"name": "Anna-Chiara Bellini",
"mediumPage": "",
"twitter": "https://twitter.com/dr0nequeen",
"imageUrl": "https://pbs.twimg.com/profile_images/852490449961066496/AxcrR7xX_400x400.jpg",
"bio": "Anna-Chiara joined Google in 2019 after a long career as a Computer Engineer and Startup founder. Now she focuses on making Android developers' lives easier. "
},
{
"id": "53",
"name": "Amanda Alexander",
"mediumPage": "",
"twitter": "",
"imageUrl": "https://developer.android.com/events/dev-summit/images/speakers/amanda_alexander_720.jpg",
"bio": ""
},
{
"id": "54",
"name": "Android Developers Backstage",
"mediumPage": "",
"twitter": "",
"imageUrl": "https://ssl-static.libsyn.com/p/assets/d/b/3/6/db362cf09b34bd4ce5bbc093207a2619/height_250_width_250_Android_Devs_Backstage_Thumb_v2.png",
"bio": "Android Developers Backstage is a podcast by and for Android developers. Hosted by developers from the Android platform team, this show covers topics of interest to Android programmers, with in-depth discussions and interviews with engineers on the Android team at Google."
},
{
"id": "55",
"name": "Nicole Borrelli",
"mediumPage": "https://medium.com/@borrelli",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "56",
"name": "Dan Saadati",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "57",
"name": "Nick Butcher",
"mediumPage": "https://medium.com/@crafty",
"twitter": "https://twitter.com/crafty",
"imageUrl": "https://pbs.twimg.com/profile_images/964457443517444097/AiMNiPtx_400x400.jpg",
"bio": ""
},
{
"id": "58",
"name": "Ian Lake",
"mediumPage": "https://medium.com/@ianhlake",
"twitter": "https://twitter.com/ianhlake",
"imageUrl": "https://pbs.twimg.com/profile_images/438932314504978434/uj3Sgwy9_400x400.jpeg",
"bio": "Ian is a software engineer on the Android Toolkit team at Google. He leads the teams working on the Navigation Component, Fragments, and the integration of fundamental building blocks such as Lifecycle, Saved State, and Activity APIs across Jetpack."
},
{
"id": "59",
"name": "Diana Wong",
"mediumPage": "",
"twitter": "https://twitter.com/droidiana1000",
"imageUrl": "https://pbs.twimg.com/profile_images/1268358805537951744/zCYvvpvV_400x400.jpg",
"bio": "Diana is a Product Manager on the Android Developer team, owning the Large Screen app ecosystem and loves Android foldables and tablets. Previously she's worked on Android Jetpack, the NDK, and the Android runtime (ART), among other things."
},
{
"id": "60",
"name": "Patricia Correa",
"mediumPage": "",
"twitter": "",
"imageUrl": "https://pbs.twimg.com/profile_images/940666196050944000/w12h2qOz_400x400.jpg",
"bio": ""
},
{
"id": "61",
"name": "The Modern Android Development Team",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Development tools, APIs, language, and distribution technologies recommended by the Android team to help developers be productive and create better apps that run across billions of devices."
},
{
"id": "62",
"name": "Maru Ahues Bouza",
"mediumPage": "https://medium.com/@mahues",
"twitter": "https://twitter.com/mabouza",
"imageUrl": "https://pbs.twimg.com/profile_images/1496362581253967872/4S4SBVYC_400x400.jpg",
"bio": "Director of Android Developer Relations @ Google. Venezolana 🇻🇪."
},
{
"id": "63",
"name": "Purnima Kochikar",
"mediumPage": "",
"twitter": "https://twitter.com/purnimakochikar",
"imageUrl": "https://media-exp1.licdn.com/dms/image/C4D03AQHUHmUGiioagQ/profile-displayphoto-shrink_800_800/0/1639519434507?e=2147483647&v=beta&t=OIt4yJkbJ7Suewlgyc7OrsLweMLBULRBvVHb9h4ZX5o",
"bio": "VP, Google Play, Apps & Games at Google"
},
{
"id": "64",
"name": "Yasmine Evjen",
"mediumPage": "https://yasmineevjen.medium.com/",
"twitter": "https://twitter.com/yasmineevjen",
"imageUrl": "https://miro.medium.com/fit/c/96/96/1*xK0hkXcG3TYOXDkdxorXOA.jpeg",
"bio": "Community Lead, Android Developer Relations"
},
{
"id": "65",
"name": "Jolanda Verhoef",
"mediumPage": "https://medium.com/@lojanda",
"twitter": "https://twitter.com/Lojanda",
"imageUrl": "https://pbs.twimg.com/profile_images/1396863889996980225/qBgkY5rY_400x400.jpg",
"bio": "Android Developer Relations Engineer @Google, focusing on Jetpack Compose 🚀"
},
{
"id": "66",
"name": "Rakesh Iyer",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Staff Software Engineer"
},
{
"id": "67",
"name": "Leland Rechis",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Group Product Manager"
},
{
"id": "68",
"name": "Chris Arriola",
"mediumPage": "https://medium.com/@arriolachris",
"twitter": "https://twitter.com/arriolachris",
"imageUrl": "https://pbs.twimg.com/profile_images/1392882006074093568/zITOTjRR_400x400.jpg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "69",
"name": "Ryan OLeary",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "70",
"name": "Wojtek Kaliciński",
"mediumPage": "https://medium.com/@wkalicinski",
"twitter": "https://twitter.com/wkalic",
"imageUrl": "https://pbs.twimg.com/profile_images/906094366388875264/RzDjkVh7_400x400.jpg",
"bio": "Android Developer Relations Engineer"
},
{
"id": "71",
"name": "Boris Farber",
"mediumPage": "",
"twitter": "https://twitter.com/BorisFarber",
"imageUrl": "",
"bio": ""
},
{
"id": "72",
"name": "Xavier Ducrohet",
"mediumPage": "",
"twitter": "https://twitter.com/droidxav",
"imageUrl": "",
"bio": ""
},
{
"id": "73",
"name": "Niharika Arora",
"mediumPage": "",
"twitter": "https://twitter.com/theDroidLady",
"imageUrl": "",
"bio": ""
},
{
"id": "74",
"name": "Marcus Leal",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": ""
},
{
"id": "75",
"name": "Kseniia Shumelchyk",
"mediumPage": "",
"twitter": "https://twitter.com/kseniiaS",
"imageUrl": "https://media-exp1.licdn.com/dms/image/C4D03AQGdVWXQeoKI3g/profile-displayphoto-shrink_800_800/0/1635165725069?e=2147483647&v=beta&t=lyf4j6SsqBidEZQZem5Ewi62QkqERPxZWVNByKeRMks",
"bio": "Developer Relations Engineer @Google focused on ⌚and 📱. Former Android @GoogleDevExpert, #GDG and @WomenTechmakers 🇺🇦."
},
{
"id": "76",
"name": "Tom Grinsted",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Group Product Manager, Google Play"
},
{
"id": "77",
"name": "Fred Chung",
"mediumPage": "",
"twitter": "https://twitter.com/fredchung",
"imageUrl": "https://pbs.twimg.com/profile_images/2670736355/1a3614dbe62d9ba31933626e273a3f77_400x400.jpeg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "78",
"name": "Kat Kuan",
"mediumPage": "",
"twitter": "https://twitter.com/katherine_kuan",
"imageUrl": "https://pbs.twimg.com/profile_images/1062503708964020224/gXpwDkOM_400x400.jpg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "79",
"name": "Summers Pitman",
"mediumPage": "https://medium.com/@mrsummers",
"twitter": "",
"imageUrl": "https://miro.medium.com/fit/c/176/176/0*8wWgPmCgxariqpFo",
"bio": ""
},
{
"id": "80",
"name": "Ben Trengrove",
"mediumPage": "https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535",
"twitter": "https://twitter.com/bentrengrove",
"imageUrl": "https://pbs.twimg.com/profile_images/1536488024661700609/zxdNBhWT_400x400.jpg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "81",
"name": "Takeshi Hagikura",
"mediumPage": "https://medium.com/@thagikura",
"twitter": "",
"imageUrl": "https://miro.medium.com/fit/c/176/176/1*esL5OPETU5LMXNO-8os7dw.jpeg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "82",
"name": "Miłosz Moczkowski",
"mediumPage": "https://medium.com/@m_moczkowski",
"twitter": "https://twitter.com/m_moczkowski?lang=en",
"imageUrl": "https://pbs.twimg.com/profile_images/1571116773612654592/sAJD-u-T_400x400.jpg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "83",
"name": "Sabs",
"mediumPage": "https://medium.com/@iamsabs",
"twitter": "",
"imageUrl": "",
"bio": "Developer Relations Engineer at Google"
},
{
"id": "84",
"name": "Alex Rocha",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Developer Relations Engineer at Google"
},
{
"id": "85",
"name": "Donovan McMurray",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "CameraX Developer Relations Engineer"
},
{
"id": "86",
"name": "Ataul Munim",
"mediumPage": "https://medium.com/@ataulm",
"twitter": "https://twitter.com/ataulm",
"imageUrl": "https://pbs.twimg.com/profile_images/1586614206803083266/BrGIIU23_400x400.jpg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "87",
"name": "Yafit Becher",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Google Play Product Manager at Google"
},
{
"id": "88",
"name": "Luis Dorelli",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Google Play Software Engineer at Google"
},
{
"id": "89",
"name": "Terence Zhang",
"mediumPage": "https://medium.com/@tzterencezhang",
"twitter": "",
"imageUrl": "",
"bio": "Developer Relations Engineer at Google"
},
{
"id": "90",
"name": "Roberto Orgiu",
"mediumPage": "https://tiwiz.medium.com/",
"twitter": "https://twitter.com/_tiwiz",
"imageUrl": "https://pbs.twimg.com/profile_images/1504459589877698562/CncWER1I_400x400.jpg",
"bio": "@AndroidDev Relations Engineer @Google | Former @nytimes | 🚴🚵📷 👨‍👩‍👧‍👦"
},
{
"id": "91",
"name": "Alejandra Stamato",
"mediumPage": "https://medium.com/@astamato",
"twitter": "https://twitter.com/astamatok",
"imageUrl": "https://developer.android.com/events/dev-summit/images/speakers/alejandra-stamato.jpg",
"bio": "Android Developer Relations Engineer 🥑 @Google #JetpackCompose | 🇦🇷 @ 🏴󠁧󠁢󠁥󠁮󠁧󠁿👑 | Ex 🇮🇪🍀"
},
{
"id": "92",
"name": "Jason Tang",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Product Manager at Google"
},
{
"id": "93",
"name": "Diego Zuluaga",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Developer Relations Engineer at Google"
},
{
"id": "94",
"name": "Michael Mauzy",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Developer Documentation at Google"
},
{
"id": "95",
"name": "Dan Galpin",
"mediumPage": "https://medium.com/@dagalpin",
"twitter": "https://twitter.com/dagalpin",
"imageUrl": "https://developer.android.com/events/dev-summit/images/speakers/daniel-galpin_720.jpg",
"bio": "Android Developer Relations Engineer at Google"
},
{
"id": "96",
"name": "Rebecca Franks",
"mediumPage": "https://medium.com/@riggaroo ",
"twitter": "https://twitter.com/riggaroo",
"imageUrl": "https://developer.android.com/events/dev-summit/images/speakers/rebecca-franks_720.jpg",
"bio": "Rebecca is a Developer Relations Engineer on the Android team. Having joined the team in 2022, she is focused on Jetpack Compose: all things animation and graphics. Prior to joining Google she worked on a couple of interesting projects such as a Image/Video editor and TV Streaming app. "
},
{
"id": "97",
"name": "Rebecca Gutteridge",
"mediumPage": "",
"twitter": "https://twitter.com/BexSG_",
"imageUrl": "https://twitter.com/BexSG_/photo",
"bio": "Senior Android Developer Relations Engineer at Google"
},
{
"id": "98",
"name": "Sachiyo Sugimoto",
"mediumPage": "",
"twitter": "",
"imageUrl": "",
"bio": "Android Partner Engineering"
},
{
"id": "99",
"name": "Todd Burner",
"mediumPage": "https://medium.com/@tburner",
"twitter": "",
"imageUrl": "",
"bio": "Developer Relations Engineer"
}
]

File diff suppressed because it is too large Load Diff

@ -0,0 +1,154 @@
[
{
"id": "1",
"name": "Headlines",
"shortDescription": "News you'll definitely be interested in",
"longDescription": "The latest events and announcements from the world of Android development.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f",
"url": ""
},
{
"id": "2",
"name": "UI",
"shortDescription": "not including Compose",
"longDescription": "Stay up to date on Material Design, Navigation, Text, Paging, Compose, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets and much more!\n\nTo get Compose specific news, make sure you also follow the Compose topic. ",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594",
"url": ""
},
{
"id": "3",
"name": "Compose",
"shortDescription": "",
"longDescription": "All the latest and greatest news on Jetpack Compose - Androids modern toolkit for building native user interfaces.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Compose.svg?alt=media&token=9f0228e8-fdf2-45ee-9fd0-7e51fda23b48",
"url": ""
},
{
"id": "4",
"name": "Architecture",
"shortDescription": "",
"longDescription": "Stay up-to-date with Android architecture best practices including scalability and modularization. ",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Architecture.svg?alt=media&token=e69ed228-fa91-49ae-9017-c8b7331f4269",
"url": ""
},
{
"id": "5",
"name": "Android Studio & Tools",
"shortDescription": "",
"longDescription": "The latest news on Android development tools, including Android Studio, Gradle, device emulators, debugging tools and more.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-Studio.svg?alt=media&token=b28b82dc-5aa1-4098-9eff-deb04636d3ac",
"url": ""
},
{
"id": "6",
"name": "Testing",
"shortDescription": "",
"longDescription": "The latest news on testing, including unit and UI testing, and continuous integration. ",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Testing.svg?alt=media&token=a11533c4-7cc8-4b11-91a3-806158ebf428",
"url": ""
},
{
"id": "7",
"name": "Performance",
"shortDescription": "",
"longDescription": "Up-to-date content on optimizing your app performance, including profiling, tracing and jank avoidance.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Performance.svg?alt=media&token=558fdf02-1918-4527-b13f-323db67e31cc",
"url": ""
},
{
"id": "8",
"name": "New APIs & Libraries",
"shortDescription": "",
"longDescription": "Stay up-to-date with new APIs & library releases, including Jetpack.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_New-APIs-_-Libraries.svg?alt=media&token=8efd12df-6dd9-4b1b-81fd-017a49a866ac",
"url": ""
},
{
"id": "9",
"name": "Data Storage",
"shortDescription": "",
"longDescription": "Everything to do with data storage, including Room and DataStore.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Data-Storage.svg?alt=media&token=c9f78039-f371-4ce1-ba82-2c0c1e20d180",
"url": ""
},
{
"id": "10",
"name": "Kotlin",
"shortDescription": "",
"longDescription": "New language features and guidance for getting the best out of Kotlin on Android. ",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Kotlin.svg?alt=media&token=bdc73380-e80d-47df-8954-d9b61cccacd2",
"url": ""
},
{
"id": "11",
"name": "Privacy & Security",
"shortDescription": "",
"longDescription": "The latest news on security best practices, APIs and libraries.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Privacy-_-Security.svg?alt=media&token=6232fd17-c1cc-43b3-bf70-a734323fa6df",
"url": ""
},
{
"id": "12",
"name": "Publishing & Distribution",
"shortDescription": "",
"longDescription": "Everything to do with publishing and distributing your app, including Google Play.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Publishing-_-Distribution.svg?alt=media&token=64a5aeaf-269a-479d-8a44-29f59d337dbf",
"url": ""
},
{
"id": "13",
"name": "Platform & Releases",
"shortDescription": "",
"longDescription": "Stay up-to-date with the latest Android releases and features.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Platform-_-Releases.svg?alt=media&token=ff6d7a38-5205-4a51-8b6a-721e665dc515",
"url": ""
},
{
"id": "14",
"name": "Accessibility",
"shortDescription": "",
"longDescription": "The latest news on accessibility features and services, helping you to improve your app's usability, particularly for users with disabilities.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Accessibility.svg?alt=media&token=5b783a03-dd3b-4d0c-9e0c-16ae8350295f",
"url": ""
},
{
"id": "15",
"name": "Android Auto",
"shortDescription": "",
"longDescription": "The latest news on Android Automotive OS and Android Auto.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-Auto.svg?alt=media&token=56453754-14a5-4953-b596-66d63c56c196",
"url": ""
},
{
"id": "16",
"name": "Android TV",
"shortDescription": "",
"longDescription": "Stay up-to-date on everything to do with building apps for Android TV.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-TV.svg?alt=media&token=a78ca0df-f1ba-44a6-a89d-3912c82ef661",
"url": ""
},
{
"id": "17",
"name": "Games",
"shortDescription": "",
"longDescription": "The latest news on Android game development.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Games.svg?alt=media&token=4effa537-cc42-4d7f-b6bd-f1f14568db07",
"url": ""
},
{
"id": "18",
"name": "Camera & Media",
"shortDescription": "",
"longDescription": "The latest news on capturing and playing media on Android, including the Camera and Media APIs. ",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Camera-_-Media.svg?alt=media&token=73adea20-20d4-4f4c-8f3b-eb47c1097496",
"url": ""
},
{
"id": "19",
"name": "Wear OS",
"shortDescription": "",
"longDescription": "The latest news on app development for Wear OS.",
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Wear.svg?alt=media&token=bd11fe4c-9c92-4536-8ebc-5210f44d09be",
"url": ""
}
]

@ -0,0 +1,42 @@
/*
* 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.
*/
import androidx.annotation.VisibleForTesting
import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager
import java.io.File
import java.io.InputStream
import java.util.Properties
/**
* This class helps with loading Android `/assets` files, especially when running JVM unit tests.
* It must remain on the root package for an easier [Class.getResource] with relative paths.
* @see <a href="https://developer.android.com/reference/tools/gradle-api/7.3/com/android/build/api/dsl/UnitTestOptions">UnitTestOptions</a>
*/
@VisibleForTesting
internal object JvmUnitTestFakeAssetManager : FakeAssetManager {
private val config =
requireNotNull(javaClass.getResource("com/android/tools/test_config.properties")) {
"""
Missing Android resources properties file.
Did you forget to enable the feature in the gradle build file?
android.testOptions.unitTests.isIncludeAndroidResources = true
""".trimIndent()
}
private val properties = Properties().apply { config.openStream().use(::load) }
private val assets = File(properties["android_merged_assets"].toString())
override fun open(fileName: String): InputStream = File(assets, fileName).inputStream()
}

@ -16,30 +16,29 @@
package com.google.samples.apps.nowinandroid.core.network.di package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.NiANetwork import android.content.Context
import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiANetwork import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager
import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton import javax.inject.Singleton
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
interface NetworkModule { object NetworkModule {
@Binds
fun bindsNiANetwork(
niANetwork: FakeNiANetwork
): NiANetwork
companion object {
@Provides @Provides
@Singleton @Singleton
fun providesNetworkJson(): Json = Json { fun providesNetworkJson(): Json = Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
} }
}
@Provides
@Singleton
fun providesFakeAssetManager(
@ApplicationContext context: Context,
): FakeAssetManager = FakeAssetManager(context.assets::open)
} }

@ -14,24 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.data.repository package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeNewsRepository import java.io.InputStream
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.serialization.json.Json
import org.junit.Before
class FakeNewsRepositoryTest { fun interface FakeAssetManager {
fun open(fileName: String): InputStream
private lateinit var subject: FakeNewsRepository
private val testDispatcher = StandardTestDispatcher()
@Before
fun setup() {
subject = FakeNewsRepository(
ioDispatcher = testDispatcher,
networkJson = Json { ignoreUnknownKeys = true }
)
}
} }

@ -16,6 +16,7 @@
package com.google.samples.apps.nowinandroid.core.network.fake package com.google.samples.apps.nowinandroid.core.network.fake
import JvmUnitTestFakeAssetManager
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
@ -26,29 +27,30 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
/** /**
* [NiaNetworkDataSource] implementation that provides static news resources to aid development * [NiaNetworkDataSource] implementation that provides static news resources to aid development
*/ */
class FakeNiaNetworkDataSource @Inject constructor( class FakeNiaNetworkDataSource @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val networkJson: Json private val networkJson: Json,
private val assets: FakeAssetManager = JvmUnitTestFakeAssetManager,
) : NiaNetworkDataSource { ) : NiaNetworkDataSource {
override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> = override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> =
withContext(ioDispatcher) { withContext(ioDispatcher) {
networkJson.decodeFromString(FakeDataSource.topicsData) assets.open(FakeDataSource.TOPICS).use(networkJson::decodeFromStream)
} }
override suspend fun getNewsResources(ids: List<String>?): List<NetworkNewsResource> = override suspend fun getNewsResources(ids: List<String>?): List<NetworkNewsResource> =
withContext(ioDispatcher) { withContext(ioDispatcher) {
networkJson.decodeFromString(FakeDataSource.data) assets.open(FakeDataSource.DATA).use(networkJson::decodeFromStream)
} }
override suspend fun getAuthors(ids: List<String>?): List<NetworkAuthor> = override suspend fun getAuthors(ids: List<String>?): List<NetworkAuthor> =
withContext(ioDispatcher) { withContext(ioDispatcher) {
networkJson.decodeFromString(FakeDataSource.authors) assets.open(FakeDataSource.AUTHORS).use(networkJson::decodeFromStream)
} }
override suspend fun getTopicChangeList(after: Int?): List<NetworkChangeList> = override suspend fun getTopicChangeList(after: Int?): List<NetworkChangeList> =

@ -20,26 +20,13 @@ import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.retrofit.RetrofitNiaNetwork import com.google.samples.apps.nowinandroid.core.network.retrofit.RetrofitNiaNetwork
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import kotlinx.serialization.json.Json
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
interface NetworkModule { interface FlavoredNetworkModule {
@Binds @Binds
fun bindsNiaNetwork( fun RetrofitNiaNetwork.binds(): NiaNetworkDataSource
niANetwork: RetrofitNiaNetwork
): NiaNetworkDataSource
companion object {
@Provides
@Singleton
fun providesNetworkJson(): Json = Json {
ignoreUnknownKeys = true
}
}
} }

@ -16,9 +16,16 @@
package com.google.samples.apps.nowinandroid.core.network.fake package com.google.samples.apps.nowinandroid.core.network.fake
import JvmUnitTestFakeAssetManager
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Codelab
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -33,14 +40,24 @@ class FakeNiaNetworkDataSourceTest {
fun setUp() { fun setUp() {
subject = FakeNiaNetworkDataSource( subject = FakeNiaNetworkDataSource(
ioDispatcher = testDispatcher, ioDispatcher = testDispatcher,
networkJson = Json { ignoreUnknownKeys = true } networkJson = Json { ignoreUnknownKeys = true },
assets = JvmUnitTestFakeAssetManager
) )
} }
@Test @Test
fun testDeserializationOfTopics() = runTest(testDispatcher) { fun testDeserializationOfTopics() = runTest(testDispatcher) {
assertEquals( assertEquals(
FakeDataSource.sampleTopic, /* ktlint-disable max-line-length */
NetworkTopic(
id = "1",
name = "Headlines",
shortDescription = "News you'll definitely be interested in",
longDescription = "The latest events and announcements from the world of Android development.",
url = "",
imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f"
),
/* ktlint-enable max-line-length */
subject.getTopics().first() subject.getTopics().first()
) )
} }
@ -48,8 +65,28 @@ class FakeNiaNetworkDataSourceTest {
@Test @Test
fun testDeserializationOfNewsResources() = runTest(testDispatcher) { fun testDeserializationOfNewsResources() = runTest(testDispatcher) {
assertEquals( assertEquals(
FakeDataSource.sampleResource, /* ktlint-disable max-line-length */
subject.getNewsResources().find { it.id == FakeDataSource.sampleResource.id } NetworkNewsResource(
id = "125",
title = "Android Basics with Compose",
content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. ",
url = "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html",
headerImageUrl = "https://developer.android.com/images/hero-assets/android-basics-compose.svg",
authors = listOf("25"),
publishDate = LocalDateTime(
year = 2022,
monthNumber = 5,
dayOfMonth = 4,
hour = 23,
minute = 0,
second = 0,
nanosecond = 0
).toInstant(TimeZone.UTC),
type = Codelab,
topics = listOf("2", "3", "10"),
),
/* ktlint-enable max-line-length */
subject.getNewsResources().find { it.id == "125" }
) )
} }
} }

Loading…
Cancel
Save