From cbdf6a2d3c588988429f3a691d75409f6cb341a6 Mon Sep 17 00:00:00 2001 From: Milosz Moczkowski Date: Fri, 23 Sep 2022 16:00:05 +0200 Subject: [PATCH 1/3] Remove episodes related code --- .../nowinandroid/core/data/model/Episode.kt | 37 --- .../core/data/model/NewsResource.kt | 15 - .../repository/OfflineFirstNewsRepository.kt | 11 +- .../core/data/model/NetworkEntityKtTest.kt | 41 --- .../OfflineFirstNewsRepositoryTest.kt | 23 -- .../core/data/testdoubles/TestEpisodeDao.kt | 72 ---- .../data/testdoubles/TestNewsResourceDao.kt | 11 +- .../testdoubles/TestNiaNetworkDataSource.kt | 2 - .../database/model/PopulatedEpisodeKtTest.kt | 98 ------ .../model/PopulatedNewsResourceKtTest.kt | 9 - .../11.json | 314 ++++++++++++++++++ .../core/database/dao/NewsResourceDaoTest.kt | 39 +-- .../nowinandroid/core/database/DaosModule.kt | 6 - .../core/database/DatabaseMigrations.kt | 18 +- .../core/database/DatabaseModule.kt | 1 + .../nowinandroid/core/database/NiaDatabase.kt | 9 +- .../core/database/dao/EpisodeDao.kt | 71 ---- .../core/database/model/AuthorEntity.kt | 2 +- .../database/model/EpisodeAuthorCrossRef.kt | 54 --- .../core/database/model/EpisodeEntity.kt | 41 --- .../core/database/model/NewsResourceEntity.kt | 17 +- .../core/database/model/PopulatedEpisode.kt | 55 --- .../database/model/PopulatedNewsResource.kt | 6 - .../core/datastore/ChangeListVersions.kt | 1 - .../datastore/NiaPreferencesDataSource.kt | 3 - .../nowinandroid/data/user_preferences.proto | 1 - .../nowinandroid/core/model/data/Episode.kt | 32 -- .../core/model/data/NewsResource.kt | 4 - .../core/network/fake/FakeDataSource.kt | 193 ----------- .../core/network/model/NetworkEpisode.kt | 52 --- .../core/network/model/NetworkNewsResource.kt | 2 - .../nowinandroid/core/ui/NewsResourceCard.kt | 2 +- .../feature/author/AuthorScreenTest.kt | 1 - .../feature/author/AuthorViewModelTest.kt | 1 - .../foryou/src/main/res/values/strings.xml | 1 - .../feature/foryou/ForYouViewModelTest.kt | 3 - .../feature/topic/TopicScreenTest.kt | 1 - .../feature/topic/TopicViewModelTest.kt | 1 - 38 files changed, 340 insertions(+), 910 deletions(-) delete mode 100644 core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/Episode.kt delete mode 100644 core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestEpisodeDao.kt delete mode 100644 core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisodeKtTest.kt create mode 100644 core/database/schemas/com.google.samples.apps.nowinandroid.core.database.NiaDatabase/11.json delete mode 100644 core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/EpisodeDao.kt delete mode 100644 core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeAuthorCrossRef.kt delete mode 100644 core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeEntity.kt delete mode 100644 core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisode.kt delete mode 100644 core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/Episode.kt delete mode 100644 core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkEpisode.kt diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/Episode.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/Episode.kt deleted file mode 100644 index 3e7f7bec2..000000000 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/Episode.kt +++ /dev/null @@ -1,37 +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.data.model - -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity -import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisode -import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisodeExpanded - -fun NetworkEpisode.asEntity() = EpisodeEntity( - id = id, - name = name, - publishDate = publishDate, - alternateVideo = alternateVideo, - alternateAudio = alternateAudio, -) - -fun NetworkEpisodeExpanded.asEntity() = EpisodeEntity( - id = id, - name = name, - publishDate = publishDate, - alternateVideo = alternateVideo, - alternateAudio = alternateAudio, -) diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt index bd9609b3b..6e73ae9a3 100644 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt @@ -17,7 +17,6 @@ package com.google.samples.apps.nowinandroid.core.data.model import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceAuthorCrossRef import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef @@ -27,7 +26,6 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResour fun NetworkNewsResource.asEntity() = NewsResourceEntity( id = id, - episodeId = episodeId, title = title, content = content, url = url, @@ -38,7 +36,6 @@ fun NetworkNewsResource.asEntity() = NewsResourceEntity( fun NetworkNewsResourceExpanded.asEntity() = NewsResourceEntity( id = id, - episodeId = episodeId, title = title, content = content, url = url, @@ -47,18 +44,6 @@ fun NetworkNewsResourceExpanded.asEntity() = NewsResourceEntity( type = type, ) -/** - * A shell [EpisodeEntity] to fulfill the foreign key constraint when inserting - * a [NewsResourceEntity] into the DB - */ -fun NetworkNewsResource.episodeEntityShell() = EpisodeEntity( - id = episodeId, - name = "", - publishDate = publishDate, - alternateVideo = null, - alternateAudio = null, -) - /** * A shell [AuthorEntity] to fulfill the foreign key constraint when inserting * a [NewsResourceEntity] into the DB diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt index 4588fd404..bf605b9e2 100644 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt @@ -21,15 +21,12 @@ import com.google.samples.apps.nowinandroid.core.data.changeListSync import com.google.samples.apps.nowinandroid.core.data.model.asEntity import com.google.samples.apps.nowinandroid.core.data.model.authorCrossReferences import com.google.samples.apps.nowinandroid.core.data.model.authorEntityShells -import com.google.samples.apps.nowinandroid.core.data.model.episodeEntityShell import com.google.samples.apps.nowinandroid.core.data.model.topicCrossReferences import com.google.samples.apps.nowinandroid.core.data.model.topicEntityShells import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao -import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel @@ -37,9 +34,9 @@ import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource -import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import javax.inject.Inject /** * Disk storage backed implementation of the [NewsRepository]. @@ -47,7 +44,6 @@ import kotlinx.coroutines.flow.map */ class OfflineFirstNewsRepository @Inject constructor( private val newsResourceDao: NewsResourceDao, - private val episodeDao: EpisodeDao, private val authorDao: AuthorDao, private val topicDao: TopicDao, private val network: NiaNetworkDataSource, @@ -93,11 +89,6 @@ class OfflineFirstNewsRepository @Inject constructor( .flatten() .distinctBy(AuthorEntity::id) ) - episodeDao.insertOrIgnoreEpisodes( - episodeEntities = networkNewsResources - .map(NetworkNewsResource::episodeEntityShell) - .distinctBy(EpisodeEntity::id) - ) newsResourceDao.upsertNewsResources( newsResourceEntities = networkNewsResources .map(NetworkNewsResource::asEntity) diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt index bb27d74ac..c910d2c79 100644 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt @@ -18,8 +18,6 @@ package com.google.samples.apps.nowinandroid.core.data.model import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Article import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor -import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisode -import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisodeExpanded import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic @@ -71,7 +69,6 @@ class NetworkEntityKtTest { val networkModel = NetworkNewsResource( id = "0", - episodeId = "2", title = "title", content = "content", url = "url", @@ -82,7 +79,6 @@ class NetworkEntityKtTest { val entity = networkModel.asEntity() assertEquals("0", entity.id) - assertEquals("2", entity.episodeId) assertEquals("title", entity.title) assertEquals("content", entity.content) assertEquals("url", entity.url) @@ -93,7 +89,6 @@ class NetworkEntityKtTest { val expandedNetworkModel = NetworkNewsResourceExpanded( id = "0", - episodeId = "2", title = "title", content = "content", url = "url", @@ -105,7 +100,6 @@ class NetworkEntityKtTest { val entityFromExpanded = expandedNetworkModel.asEntity() assertEquals("0", entityFromExpanded.id) - assertEquals("2", entityFromExpanded.episodeId) assertEquals("title", entityFromExpanded.title) assertEquals("content", entityFromExpanded.content) assertEquals("url", entityFromExpanded.url) @@ -113,39 +107,4 @@ class NetworkEntityKtTest { assertEquals(Instant.fromEpochMilliseconds(1), entityFromExpanded.publishDate) assertEquals(Article, entityFromExpanded.type) } - - @Test - fun network_episode_can_be_mapped_to_episode_entity() { - val networkModel = NetworkEpisode( - id = "0", - name = "name", - publishDate = Instant.fromEpochMilliseconds(1), - alternateVideo = "alternateVideo", - alternateAudio = "alternateAudio", - ) - val entity = networkModel.asEntity() - - assertEquals("0", entity.id) - assertEquals("name", entity.name) - assertEquals("alternateVideo", entity.alternateVideo) - assertEquals("alternateAudio", entity.alternateAudio) - assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate) - - val expandedNetworkModel = - NetworkEpisodeExpanded( - id = "0", - name = "name", - publishDate = Instant.fromEpochMilliseconds(1), - alternateVideo = "alternateVideo", - alternateAudio = "alternateAudio", - ) - - val entityFromExpanded = expandedNetworkModel.asEntity() - - assertEquals("0", entityFromExpanded.id) - assertEquals("name", entityFromExpanded.name) - assertEquals("alternateVideo", entityFromExpanded.alternateVideo) - assertEquals("alternateAudio", entityFromExpanded.alternateAudio) - assertEquals(Instant.fromEpochMilliseconds(1), entityFromExpanded.publishDate) - } } diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt index 7f4db0d90..4429f1021 100644 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt @@ -20,21 +20,17 @@ import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.model.asEntity import com.google.samples.apps.nowinandroid.core.data.model.authorCrossReferences import com.google.samples.apps.nowinandroid.core.data.model.authorEntityShells -import com.google.samples.apps.nowinandroid.core.data.model.episodeEntityShell import com.google.samples.apps.nowinandroid.core.data.model.topicCrossReferences import com.google.samples.apps.nowinandroid.core.data.model.topicEntityShells import com.google.samples.apps.nowinandroid.core.data.testdoubles.CollectionType import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestAuthorDao -import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestEpisodeDao import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNewsResourceDao import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestTopicDao import com.google.samples.apps.nowinandroid.core.data.testdoubles.filteredInterestsIds import com.google.samples.apps.nowinandroid.core.data.testdoubles.nonPresentInterestsIds import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity -import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel @@ -57,8 +53,6 @@ class OfflineFirstNewsRepositoryTest { private lateinit var newsResourceDao: TestNewsResourceDao - private lateinit var episodeDao: TestEpisodeDao - private lateinit var authorDao: TestAuthorDao private lateinit var topicDao: TestTopicDao @@ -73,7 +67,6 @@ class OfflineFirstNewsRepositoryTest { @Before fun setup() { newsResourceDao = TestNewsResourceDao() - episodeDao = TestEpisodeDao() authorDao = TestAuthorDao() topicDao = TestTopicDao() network = TestNiaNetworkDataSource() @@ -85,7 +78,6 @@ class OfflineFirstNewsRepositoryTest { subject = OfflineFirstNewsRepository( newsResourceDao = newsResourceDao, - episodeDao = episodeDao, authorDao = authorDao, topicDao = topicDao, network = network, @@ -287,21 +279,6 @@ class OfflineFirstNewsRepositoryTest { ) } - @Test - fun offlineFirstNewsRepository_sync_saves_shell_episode_entities() = - runTest { - subject.syncWith(synchronizer) - - assertEquals( - network.getNewsResources() - .map(NetworkNewsResource::episodeEntityShell) - .distinctBy(EpisodeEntity::id), - episodeDao.getEpisodesStream() - .first() - .map(PopulatedEpisode::entity) - ) - } - @Test fun offlineFirstNewsRepository_sync_saves_topic_cross_references() = runTest { diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestEpisodeDao.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestEpisodeDao.kt deleted file mode 100644 index 2b6e9bc7b..000000000 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestEpisodeDao.kt +++ /dev/null @@ -1,72 +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.data.testdoubles - -import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity -import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.update -import kotlinx.datetime.Instant - -/** - * Test double for [EpisodeDao] - */ -class TestEpisodeDao : EpisodeDao { - - private var entitiesStateFlow = MutableStateFlow( - listOf( - EpisodeEntity( - id = "1", - name = "Episode", - publishDate = Instant.fromEpochMilliseconds(0), - alternateVideo = null, - alternateAudio = null, - ) - ) - ) - - override fun getEpisodesStream(): Flow> = - entitiesStateFlow.map { - it.map(EpisodeEntity::asPopulatedEpisode) - } - - override suspend fun insertOrIgnoreEpisodes(episodeEntities: List): List { - entitiesStateFlow.value = episodeEntities - // Assume no conflicts on insert - return episodeEntities.map { it.id.toLong() } - } - - override suspend fun updateEpisodes(entities: List) { - throw NotImplementedError("Unused in tests") - } - - override suspend fun deleteEpisodes(ids: List) { - val idSet = ids.toSet() - entitiesStateFlow.update { entities -> - entities.filterNot { idSet.contains(it.id) } - } - } -} - -private fun EpisodeEntity.asPopulatedEpisode() = PopulatedEpisode( - entity = this, - newsResources = emptyList(), - authors = emptyList(), -) diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt index 1ea216e9f..c5e24419d 100644 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt @@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.core.data.testdoubles import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceAuthorCrossRef import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef @@ -43,7 +42,6 @@ class TestNewsResourceDao : NewsResourceDao { listOf( NewsResourceEntity( id = "1", - episodeId = "0", title = "news", content = "Hilt", url = "url", @@ -109,16 +107,9 @@ class TestNewsResourceDao : NewsResourceDao { private fun NewsResourceEntity.asPopulatedNewsResource() = PopulatedNewsResource( entity = this, - episode = EpisodeEntity( - id = this.episodeId, - name = "episode 4", - publishDate = Instant.fromEpochMilliseconds(2), - alternateAudio = "audio", - alternateVideo = "video", - ), authors = listOf( AuthorEntity( - id = this.episodeId, + id = "id", name = "name", imageUrl = "imageUrl", twitter = "twitter", diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt index 9a4ae159c..82a6755a4 100644 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt @@ -28,7 +28,6 @@ import kotlinx.serialization.json.Json enum class CollectionType { Topics, Authors, - Episodes, NewsResources } @@ -53,7 +52,6 @@ class TestNiaNetworkDataSource : NiaNetworkDataSource { .mapToChangeList(idGetter = NetworkTopic::id), CollectionType.Authors to allAuthors .mapToChangeList(idGetter = NetworkAuthor::id), - CollectionType.Episodes to listOf(), CollectionType.NewsResources to allNewsResources .mapToChangeList(idGetter = NetworkNewsResource::id), ) diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisodeKtTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisodeKtTest.kt deleted file mode 100644 index 9bc92dbc6..000000000 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisodeKtTest.kt +++ /dev/null @@ -1,98 +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.database.model - -import com.google.samples.apps.nowinandroid.core.model.data.Author -import com.google.samples.apps.nowinandroid.core.model.data.Episode -import com.google.samples.apps.nowinandroid.core.model.data.NewsResource -import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video -import kotlinx.datetime.Instant -import org.junit.Assert.assertEquals -import org.junit.Test - -class PopulatedEpisodeKtTest { - @Test - fun populated_episode_can_be_mapped_to_episode() { - val populatedEpisode = PopulatedEpisode( - entity = EpisodeEntity( - id = "0", - name = "Test", - publishDate = Instant.fromEpochMilliseconds(1), - alternateAudio = "audio", - alternateVideo = "video" - ), - newsResources = listOf( - NewsResourceEntity( - id = "1", - episodeId = "0", - title = "news", - content = "Hilt", - url = "url", - headerImageUrl = "headerImageUrl", - type = Video, - publishDate = Instant.fromEpochMilliseconds(1), - ) - ), - authors = listOf( - AuthorEntity( - id = "2", - name = "name", - imageUrl = "imageUrl", - twitter = "twitter", - mediumPage = "mediumPage", - bio = "bio", - ) - ), - ) - val episode = populatedEpisode.asExternalModel() - - assertEquals( - Episode( - id = "0", - name = "Test", - publishDate = Instant.fromEpochMilliseconds(1), - alternateAudio = "audio", - alternateVideo = "video", - newsResources = listOf( - NewsResource( - id = "1", - episodeId = "0", - title = "news", - content = "Hilt", - url = "url", - headerImageUrl = "headerImageUrl", - type = Video, - publishDate = Instant.fromEpochMilliseconds(1), - authors = listOf(), - topics = listOf() - ) - ), - authors = listOf( - Author( - id = "2", - name = "name", - imageUrl = "imageUrl", - twitter = "twitter", - mediumPage = "mediumPage", - bio = "bio", - ) - ), - ), - episode - ) - } -} diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt index 491900eab..1bc89d1f4 100644 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt @@ -30,7 +30,6 @@ class PopulatedNewsResourceKtTest { val populatedNewsResource = PopulatedNewsResource( entity = NewsResourceEntity( id = "1", - episodeId = "0", title = "news", content = "Hilt", url = "url", @@ -38,13 +37,6 @@ class PopulatedNewsResourceKtTest { type = Video, publishDate = Instant.fromEpochMilliseconds(1), ), - episode = EpisodeEntity( - id = "4", - name = "episode 4", - publishDate = Instant.fromEpochMilliseconds(2), - alternateAudio = "audio", - alternateVideo = "video", - ), authors = listOf( AuthorEntity( id = "2", @@ -71,7 +63,6 @@ class PopulatedNewsResourceKtTest { assertEquals( NewsResource( id = "1", - episodeId = "0", title = "news", content = "Hilt", url = "url", diff --git a/core/database/schemas/com.google.samples.apps.nowinandroid.core.database.NiaDatabase/11.json b/core/database/schemas/com.google.samples.apps.nowinandroid.core.database.NiaDatabase/11.json new file mode 100644 index 000000000..6aabf24f9 --- /dev/null +++ b/core/database/schemas/com.google.samples.apps.nowinandroid.core.database.NiaDatabase/11.json @@ -0,0 +1,314 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "2f83f889f6d8a96243f4ce387adbc604", + "entities": [ + { + "tableName": "authors", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `image_url` TEXT NOT NULL, `twitter` TEXT NOT NULL DEFAULT '', `medium_page` TEXT NOT NULL DEFAULT '', `bio` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "image_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "twitter", + "columnName": "twitter", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "mediumPage", + "columnName": "medium_page", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "bio", + "columnName": "bio", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "news_resources_authors", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`news_resource_id` TEXT NOT NULL, `author_id` TEXT NOT NULL, PRIMARY KEY(`news_resource_id`, `author_id`), FOREIGN KEY(`news_resource_id`) REFERENCES `news_resources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`author_id`) REFERENCES `authors`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "newsResourceId", + "columnName": "news_resource_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "authorId", + "columnName": "author_id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "news_resource_id", + "author_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_news_resources_authors_news_resource_id", + "unique": false, + "columnNames": [ + "news_resource_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_authors_news_resource_id` ON `${TABLE_NAME}` (`news_resource_id`)" + }, + { + "name": "index_news_resources_authors_author_id", + "unique": false, + "columnNames": [ + "author_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_authors_author_id` ON `${TABLE_NAME}` (`author_id`)" + } + ], + "foreignKeys": [ + { + "table": "news_resources", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "news_resource_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "authors", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "author_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "news_resources", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `url` TEXT NOT NULL, `header_image_url` TEXT, `publish_date` INTEGER NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headerImageUrl", + "columnName": "header_image_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publishDate", + "columnName": "publish_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "news_resources_topics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`news_resource_id` TEXT NOT NULL, `topic_id` TEXT NOT NULL, PRIMARY KEY(`news_resource_id`, `topic_id`), FOREIGN KEY(`news_resource_id`) REFERENCES `news_resources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`topic_id`) REFERENCES `topics`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "newsResourceId", + "columnName": "news_resource_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topicId", + "columnName": "topic_id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "news_resource_id", + "topic_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_news_resources_topics_news_resource_id", + "unique": false, + "columnNames": [ + "news_resource_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_topics_news_resource_id` ON `${TABLE_NAME}` (`news_resource_id`)" + }, + { + "name": "index_news_resources_topics_topic_id", + "unique": false, + "columnNames": [ + "topic_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_topics_topic_id` ON `${TABLE_NAME}` (`topic_id`)" + } + ], + "foreignKeys": [ + { + "table": "news_resources", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "news_resource_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "topics", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "topic_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "topics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `longDescription` TEXT NOT NULL DEFAULT '', `url` TEXT NOT NULL DEFAULT '', `imageUrl` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortDescription", + "columnName": "shortDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "longDescription", + "columnName": "longDescription", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2f83f889f6d8a96243f4ce387adbc604')" + ] + } +} \ No newline at end of file diff --git a/core/database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt b/core/database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt index cb45bd00a..3c6557a2b 100644 --- a/core/database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt +++ b/core/database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt @@ -21,7 +21,6 @@ import androidx.room.Room import androidx.test.core.app.ApplicationProvider import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceAuthorCrossRef import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef @@ -38,7 +37,6 @@ import org.junit.Test class NewsResourceDaoTest { private lateinit var newsResourceDao: NewsResourceDao - private lateinit var episodeDao: EpisodeDao private lateinit var topicDao: TopicDao private lateinit var authorDao: AuthorDao private lateinit var db: NiaDatabase @@ -51,7 +49,6 @@ class NewsResourceDaoTest { NiaDatabase::class.java ).build() newsResourceDao = db.newsResourceDao() - episodeDao = db.episodeDao() topicDao = db.topicDao() authorDao = db.authorDao() } @@ -76,13 +73,6 @@ class NewsResourceDaoTest { millisSinceEpoch = 2, ), ) - val episodeEntityShells = newsResourceEntities - .map(NewsResourceEntity::episodeEntityShell) - .distinct() - - episodeDao.insertOrIgnoreEpisodes( - episodeEntityShells - ) newsResourceDao.upsertNewsResources( newsResourceEntities ) @@ -128,9 +118,6 @@ class NewsResourceDaoTest { millisSinceEpoch = 2, ), ) - val episodeEntityShells = newsResourceEntities - .map(NewsResourceEntity::episodeEntityShell) - .distinct() val newsResourceTopicCrossRefEntities = topicEntities.mapIndexed { index, topicEntity -> NewsResourceTopicCrossRef( newsResourceId = index.toString(), @@ -141,9 +128,6 @@ class NewsResourceDaoTest { topicDao.insertOrIgnoreTopics( topicEntities = topicEntities ) - episodeDao.insertOrIgnoreEpisodes( - episodeEntities = episodeEntityShells - ) newsResourceDao.upsertNewsResources( newsResourceEntities ) @@ -193,9 +177,6 @@ class NewsResourceDaoTest { millisSinceEpoch = 2, ), ) - val episodeEntityShells = newsResourceEntities - .map(NewsResourceEntity::episodeEntityShell) - .distinct() val newsResourceAuthorCrossRefEntities = authorEntities.mapIndexed { index, authorEntity -> NewsResourceAuthorCrossRef( newsResourceId = index.toString(), @@ -204,7 +185,6 @@ class NewsResourceDaoTest { } authorDao.upsertAuthors(authorEntities) - episodeDao.upsertEpisodes(episodeEntityShells) newsResourceDao.upsertNewsResources(newsResourceEntities) newsResourceDao.insertOrIgnoreAuthorCrossRefEntities(newsResourceAuthorCrossRefEntities) @@ -266,9 +246,7 @@ class NewsResourceDaoTest { millisSinceEpoch = 10, ), ) - val episodeEntityShells = newsResourceEntities - .map(NewsResourceEntity::episodeEntityShell) - .distinct() + val newsResourceTopicCrossRefEntities = topicEntities.mapIndexed { index, topicEntity -> NewsResourceTopicCrossRef( newsResourceId = index.toString(), @@ -286,7 +264,6 @@ class NewsResourceDaoTest { topicDao.upsertTopics(topicEntities) authorDao.upsertAuthors(authorEntities) - episodeDao.upsertEpisodes(episodeEntityShells) newsResourceDao.upsertNewsResources(newsResourceEntities) newsResourceDao.insertOrIgnoreTopicCrossRefEntities(newsResourceTopicCrossRefEntities) newsResourceDao.insertOrIgnoreAuthorCrossRefEntities(newsResourceAuthorCrossRefEntities) @@ -327,11 +304,6 @@ class NewsResourceDaoTest { millisSinceEpoch = 2, ), ) - val episodeEntityShells = newsResourceEntities - .map(NewsResourceEntity::episodeEntityShell) - .distinct() - - episodeDao.upsertEpisodes(episodeEntityShells) newsResourceDao.upsertNewsResources(newsResourceEntities) val (toDelete, toKeep) = newsResourceEntities.partition { it.id.toInt() % 2 == 0 } @@ -379,7 +351,6 @@ private fun testNewsResource( millisSinceEpoch: Long = 0 ) = NewsResourceEntity( id = id, - episodeId = "0", title = "", content = "", url = "", @@ -387,11 +358,3 @@ private fun testNewsResource( publishDate = Instant.fromEpochMilliseconds(millisSinceEpoch), type = NewsResourceType.DAC, ) - -private fun NewsResourceEntity.episodeEntityShell() = EpisodeEntity( - id = episodeId, - name = "", - publishDate = Instant.fromEpochMilliseconds(0), - alternateVideo = null, - alternateAudio = null, -) diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt index ae5a2bd3d..f3aa8bb4f 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt @@ -17,7 +17,6 @@ package com.google.samples.apps.nowinandroid.core.database import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao -import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import dagger.Module @@ -38,11 +37,6 @@ object DaosModule { database: NiaDatabase, ): TopicDao = database.topicDao() - @Provides - fun providesEpisodeDao( - database: NiaDatabase, - ): EpisodeDao = database.episodeDao() - @Provides fun providesNewsResourceDao( database: NiaDatabase, diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt index 948f0f158..7e80f023c 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt @@ -16,6 +16,8 @@ package com.google.samples.apps.nowinandroid.core.database +import androidx.room.DeleteColumn +import androidx.room.DeleteTable import androidx.room.RenameColumn import androidx.room.migration.AutoMigrationSpec @@ -26,7 +28,7 @@ import androidx.room.migration.AutoMigrationSpec * from and Y is the schema version you're migrating to. The class should implement * `AutoMigrationSpec`. */ -class DatabaseMigrations { +object DatabaseMigrations { @RenameColumn( tableName = "topics", @@ -34,4 +36,18 @@ class DatabaseMigrations { toColumnName = "shortDescription" ) class Schema2to3 : AutoMigrationSpec + + @DeleteColumn( + tableName = "news_resources", + columnName = "episode_id" + ) + @DeleteTable.Entries( + DeleteTable( + tableName = "episodes_authors" + ), + DeleteTable( + tableName = "episodes" + ) + ) + class Schema10to11 : AutoMigrationSpec } diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt index 7d4dd67c8..ca28bef1d 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt @@ -18,6 +18,7 @@ package com.google.samples.apps.nowinandroid.core.database import android.content.Context import androidx.room.Room +import androidx.room.migration.Migration import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt index e367c4bca..96e098fe4 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt @@ -21,12 +21,9 @@ import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao -import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeAuthorCrossRef -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceAuthorCrossRef import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef @@ -37,14 +34,12 @@ import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeC @Database( entities = [ AuthorEntity::class, - EpisodeAuthorCrossRef::class, - EpisodeEntity::class, NewsResourceAuthorCrossRef::class, NewsResourceEntity::class, NewsResourceTopicCrossRef::class, TopicEntity::class, ], - version = 10, + version = 11, autoMigrations = [ AutoMigration(from = 1, to = 2), AutoMigration(from = 2, to = 3, spec = DatabaseMigrations.Schema2to3::class), @@ -55,6 +50,7 @@ import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeC AutoMigration(from = 7, to = 8), AutoMigration(from = 8, to = 9), AutoMigration(from = 9, to = 10), + AutoMigration(from = 10, to = 11, spec = DatabaseMigrations.Schema10to11::class) ], exportSchema = true, ) @@ -65,6 +61,5 @@ import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeC abstract class NiaDatabase : RoomDatabase() { abstract fun topicDao(): TopicDao abstract fun authorDao(): AuthorDao - abstract fun episodeDao(): EpisodeDao abstract fun newsResourceDao(): NewsResourceDao } diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/EpisodeDao.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/EpisodeDao.kt deleted file mode 100644 index 7e823e222..000000000 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/EpisodeDao.kt +++ /dev/null @@ -1,71 +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.database.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction -import androidx.room.Update -import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity -import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode -import com.google.samples.apps.nowinandroid.core.model.data.Episode -import kotlinx.coroutines.flow.Flow - -/** - * DAO for [EpisodeEntity] and [Episode] access - */ -@Dao -interface EpisodeDao { - @Transaction - @Query(value = "SELECT * FROM episodes") - fun getEpisodesStream(): Flow> - - /** - * Inserts [episodeEntities] into the db if they don't exist, and ignores those that do - */ - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreEpisodes(episodeEntities: List): List - - /** - * Updates [entities] in the db that match the primary key, and no-ops if they don't - */ - @Update - suspend fun updateEpisodes(entities: List) - - /** - * Inserts or updates [entities] in the db under the specified primary keys - */ - @Transaction - suspend fun upsertEpisodes(entities: List) = upsert( - items = entities, - insertMany = ::insertOrIgnoreEpisodes, - updateMany = ::updateEpisodes - ) - - /** - * Deletes rows in the db matching the specified [ids] - */ - @Query( - value = """ - DELETE FROM episodes - WHERE id in (:ids) - """ - ) - suspend fun deleteEpisodes(ids: List) -} diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/AuthorEntity.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/AuthorEntity.kt index 5423f4a58..04d56e962 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/AuthorEntity.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/AuthorEntity.kt @@ -22,7 +22,7 @@ import androidx.room.PrimaryKey import com.google.samples.apps.nowinandroid.core.model.data.Author /** - * Defines an author for either an [EpisodeEntity] or [NewsResourceEntity]. + * Defines an author for [NewsResourceEntity]. * It has a many to many relationship with both entities */ @Entity( diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeAuthorCrossRef.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeAuthorCrossRef.kt deleted file mode 100644 index 368945680..000000000 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeAuthorCrossRef.kt +++ /dev/null @@ -1,54 +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.database.model - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.Index - -/** - * Cross reference for many to many relationship between [EpisodeEntity] and [AuthorEntity] - */ -@Entity( - tableName = "episodes_authors", - primaryKeys = ["episode_id", "author_id"], - foreignKeys = [ - ForeignKey( - entity = EpisodeEntity::class, - parentColumns = ["id"], - childColumns = ["episode_id"], - onDelete = ForeignKey.CASCADE - ), - ForeignKey( - entity = AuthorEntity::class, - parentColumns = ["id"], - childColumns = ["author_id"], - onDelete = ForeignKey.CASCADE - ), - ], - indices = [ - Index(value = ["episode_id"]), - Index(value = ["author_id"]), - ], -) -data class EpisodeAuthorCrossRef( - @ColumnInfo(name = "episode_id") - val episodeId: String, - @ColumnInfo(name = "author_id") - val authorId: String, -) diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeEntity.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeEntity.kt deleted file mode 100644 index 6d3d6adeb..000000000 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/EpisodeEntity.kt +++ /dev/null @@ -1,41 +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.database.model - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import kotlinx.datetime.Instant - -/** - * Defines an NiA episode. - * It is a parent in a 1 to many relationship with [NewsResourceEntity] - */ -@Entity( - tableName = "episodes", -) -data class EpisodeEntity( - @PrimaryKey - val id: String, - val name: String, - @ColumnInfo(name = "publish_date") - val publishDate: Instant, - @ColumnInfo(name = "alternate_video") - val alternateVideo: String?, - @ColumnInfo(name = "alternate_audio") - val alternateAudio: String?, -) diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt index 8d15a1f0b..690f1fe44 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt @@ -27,27 +27,13 @@ import kotlinx.datetime.Instant /** * Defines an NiA news resource. - * It is the child in a 1 to many relationship with [EpisodeEntity] */ @Entity( - tableName = "news_resources", - foreignKeys = [ - ForeignKey( - entity = EpisodeEntity::class, - parentColumns = ["id"], - childColumns = ["episode_id"], - onDelete = ForeignKey.CASCADE - ), - ], - indices = [ - Index(value = ["episode_id"]) - ] + tableName = "news_resources" ) data class NewsResourceEntity( @PrimaryKey val id: String, - @ColumnInfo(name = "episode_id") - val episodeId: String, val title: String, val content: String, val url: String, @@ -60,7 +46,6 @@ data class NewsResourceEntity( fun NewsResourceEntity.asExternalModel() = NewsResource( id = id, - episodeId = episodeId, title = title, content = content, url = url, diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisode.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisode.kt deleted file mode 100644 index 1a700f883..000000000 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedEpisode.kt +++ /dev/null @@ -1,55 +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.database.model - -import androidx.room.Embedded -import androidx.room.Junction -import androidx.room.Relation -import com.google.samples.apps.nowinandroid.core.model.data.Episode - -/** - * External data layer representation of an NiA episode - */ -data class PopulatedEpisode( - @Embedded - val entity: EpisodeEntity, - @Relation( - parentColumn = "id", - entityColumn = "episode_id" - ) - val newsResources: List, - @Relation( - parentColumn = "id", - entityColumn = "id", - associateBy = Junction( - value = EpisodeAuthorCrossRef::class, - parentColumn = "episode_id", - entityColumn = "author_id", - ) - ) - val authors: List -) - -fun PopulatedEpisode.asExternalModel() = Episode( - id = entity.id, - name = entity.name, - publishDate = entity.publishDate, - alternateVideo = entity.alternateVideo, - alternateAudio = entity.alternateAudio, - newsResources = newsResources.map(NewsResourceEntity::asExternalModel), - authors = authors.map(AuthorEntity::asExternalModel) -) diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt index d9a55a68c..9c75745e8 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt @@ -27,11 +27,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource data class PopulatedNewsResource( @Embedded val entity: NewsResourceEntity, - @Relation( - parentColumn = "episode_id", - entityColumn = "id" - ) - val episode: EpisodeEntity, @Relation( parentColumn = "id", entityColumn = "id", @@ -56,7 +51,6 @@ data class PopulatedNewsResource( fun PopulatedNewsResource.asExternalModel() = NewsResource( id = entity.id, - episodeId = entity.episodeId, title = entity.title, content = entity.content, url = entity.url, diff --git a/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt b/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt index ed41a3ac7..db10ca709 100644 --- a/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt +++ b/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt @@ -22,6 +22,5 @@ package com.google.samples.apps.nowinandroid.core.datastore data class ChangeListVersions( val topicVersion: Int = -1, val authorVersion: Int = -1, - val episodeVersion: Int = -1, val newsResourceVersion: Int = -1, ) diff --git a/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index 6d7028f0c..473fb4dfd 100644 --- a/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -87,7 +87,6 @@ class NiaPreferencesDataSource @Inject constructor( ChangeListVersions( topicVersion = it.topicChangeListVersion, authorVersion = it.authorChangeListVersion, - episodeVersion = it.episodeChangeListVersion, newsResourceVersion = it.newsResourceChangeListVersion, ) } @@ -103,7 +102,6 @@ class NiaPreferencesDataSource @Inject constructor( ChangeListVersions( topicVersion = currentPreferences.topicChangeListVersion, authorVersion = currentPreferences.authorChangeListVersion, - episodeVersion = currentPreferences.episodeChangeListVersion, newsResourceVersion = currentPreferences.newsResourceChangeListVersion ) ) @@ -111,7 +109,6 @@ class NiaPreferencesDataSource @Inject constructor( currentPreferences.copy { topicChangeListVersion = updatedChangeListVersions.topicVersion authorChangeListVersion = updatedChangeListVersions.authorVersion - episodeChangeListVersion = updatedChangeListVersions.episodeVersion newsResourceChangeListVersion = updatedChangeListVersions.newsResourceVersion } } diff --git a/core/datastore/src/main/proto/com/google/samples/apps/nowinandroid/data/user_preferences.proto b/core/datastore/src/main/proto/com/google/samples/apps/nowinandroid/data/user_preferences.proto index 407031215..c5750ef90 100644 --- a/core/datastore/src/main/proto/com/google/samples/apps/nowinandroid/data/user_preferences.proto +++ b/core/datastore/src/main/proto/com/google/samples/apps/nowinandroid/data/user_preferences.proto @@ -24,7 +24,6 @@ message UserPreferences { repeated int32 deprecated_int_followed_topic_ids = 1; int32 topicChangeListVersion = 3; int32 authorChangeListVersion = 4; - int32 episodeChangeListVersion = 5; int32 newsResourceChangeListVersion = 6; repeated int32 deprecated_int_followed_author_ids = 7; bool has_done_int_to_string_id_migration = 8; diff --git a/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/Episode.kt b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/Episode.kt deleted file mode 100644 index 7d1c1b420..000000000 --- a/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/Episode.kt +++ /dev/null @@ -1,32 +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.model.data - -import kotlinx.datetime.Instant - -/** - * External data layer representation of an NiA episode - */ -data class Episode( - val id: String, - val name: String, - val publishDate: Instant, - val alternateVideo: String?, - val alternateAudio: String?, - val newsResources: List, - val authors: List -) diff --git a/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt index fb3ee92d2..53de02d52 100644 --- a/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt +++ b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt @@ -30,7 +30,6 @@ import kotlinx.datetime.toInstant */ data class NewsResource( val id: String, - val episodeId: String, val title: String, val content: String, val url: String, @@ -44,7 +43,6 @@ data class NewsResource( val previewNewsResources = listOf( NewsResource( id = "1", - episodeId = "60", 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. You’ll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Android’s modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey", url = "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html", @@ -64,7 +62,6 @@ val previewNewsResources = listOf( ), NewsResource( id = "2", - episodeId = "52", title = "Thanks for helping us reach 1M YouTube Subscribers", content = "Thank you everyone for following the Now in Android series and everything the " + "Android Developers YouTube channel has to offer. During the Android Developer " + @@ -79,7 +76,6 @@ val previewNewsResources = listOf( ), NewsResource( id = "3", - episodeId = "52", title = "Transformations and customisations in the Paging Library", content = "A demonstration of different operations that can be performed " + "with Paging. Transformations like inserting separators, when to " + diff --git a/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/fake/FakeDataSource.kt b/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/fake/FakeDataSource.kt index 4f6366f41..a97327edb 100644 --- a/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/fake/FakeDataSource.kt +++ b/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/fake/FakeDataSource.kt @@ -37,7 +37,6 @@ object FakeDataSource { ) val sampleResource = NetworkNewsResource( id = "1", - episodeId = "60", 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. You’ll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Android’s modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey", url = "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html", @@ -227,7 +226,6 @@ object FakeDataSource { [ { "id": "1", - "episodeId": "60", "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. You’ll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Android’s modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey", "url": "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html", @@ -245,7 +243,6 @@ object FakeDataSource { }, { "id": "2", - "episodeId": "60", "title": "Android 13 Beta 1", "content": "Beta 1 includes the latest updates to photo picker, themed app icons, improved localization and language support, and the new notification permission which requires apps targeting Android 13 to request the notification permission from the user before posting notifications. There are also a few new features, including more granular permissions for media file access, better error reporting in Keystore and KeyMint, and help for media apps to identify how their audio is going to be routed. Check out the beta by visiting the Android 13 developer site.", "url": "https://android-developers.googleblog.com/2022/04/android-13-beta-1-blog.html", @@ -261,7 +258,6 @@ object FakeDataSource { }, { "id": "3", - "episodeId": "60", "title": "Architecture: Entities - MAD Skills", "content": "In this episode, Garima from GoDaddy Studio talks about entities and more specifically how creating separate entities per layer per project leads to clean and scalable model architecture.", "url": "https://www.youtube.com/watch?v=cfak1jDKM_4", @@ -275,7 +271,6 @@ object FakeDataSource { }, { "id": "4", - "episodeId": "60", "title": "Architecture: Live Q&A - MAD Skills", "content": "Manuel , Yigit , TJ , and Milosz hosted a very special Architecture Q&A and answered questions from the community. Find out the answers to: “Is LiveData deprecated?”, “MVVM or MVI for Compose”, “Should we use flow in DataSources” and more.", "url": "https://www.youtube.com/watch?v=_2BtE1P6MPE", @@ -293,7 +288,6 @@ object FakeDataSource { }, { "id": "5", - "episodeId": "60", "title": "MAD Skills: Architecture", "content": "To wrap up the Architecture Android MAD skills series, \nManuel wrote a blog post summarizing each episode of the series! Check it out to get caught up on all things Android Architecture.", "url": "https://android-developers.googleblog.com/2022/04/architecture-mad-skills-series-wrap-up.html", @@ -309,7 +303,6 @@ object FakeDataSource { }, { "id": "6", - "episodeId": "60", "title": "The first developer preview of Privacy Sandbox on Android", "content": "Privacy Sandbox is a program to help you conduct initial testing of the proposed APIs and evaluate how you might adopt them for your solutions. The Developer Preview provides additional platform APIs and services on top of the Android 13 Developer Beta release, SDK, system images, Preview APIs, API reference, and support references. See the release notes for more details on what’s included in the release.", "url": "https://android-developers.googleblog.com/2022/04/first-preview-privacy-sandbox-android.html", @@ -324,7 +317,6 @@ object FakeDataSource { }, { "id": "7", - "episodeId": "59", "title": "Expanding Play’s Target Level API Requirements 🎯", "content": "Starting on November 1, 2022, apps that don’t target an API level within two years of the latest major Android release version will not be available on Google Play to new users with devices running Android OS versions newer than your app’s target API level. For example, as of this November, existing apps need to target at least API level 30, Android 11, to be available to new users on Android 12 and 13 devices.", "url": "https://android-developers.googleblog.com/2022/04/expanding-plays-target-level-api-requirements-to-strengthen-user-security.html", @@ -340,7 +332,6 @@ object FakeDataSource { }, { "id": "8", - "episodeId": "59", "title": "Google Play PolicyBytes - April 2022 policy updates", "content": "Users who have previously installed your app from Google Play will continue to be able to discover, re-install, and use your app, even if they move to a new Android device. App updates still also need to target an API level within a year of the latest major Android release version. Expanding our target level API requirements will protect users from installing older apps that may not have the privacy and security protections in place that newer Android releases offer.", "url": "https://www.youtube.com/watch?v=O0UwUF2DgQc", @@ -354,7 +345,6 @@ object FakeDataSource { }, { "id": "9", - "episodeId": "59", "title": "Upgrading Android Attestation: Remote Provisioning 🔐", "content": "Attestation for device integrity has been mandated since Android 8.0, and is used in a variety of services such as SafetyNet. Android 12 added the option of Remote Key Provisioning for device manufacturers, and it will be mandated in Android 13. If you’re leveraging attestation in your app, watch out for a longer certificate chain structure, a new root of trust, the deprecation of RSA attestation, and short-lived certificates/attestation keys.", "url": "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html", @@ -368,7 +358,6 @@ object FakeDataSource { }, { "id": "10", - "episodeId": "59", "title": "Architecture: Handling UI events - MAD Skills", "content": "With this episode of MAD skills we continue with our architecture series of videos. In this video you'll learn about UI events. Developer Relations Engineer Manuel Vivo covers the different types of UI events, the best practices for handling them, and more!", "url": "https://www.youtube.com/watch?v=lwGtp0Yr0PE", @@ -384,7 +373,6 @@ object FakeDataSource { }, { "id": "11", - "episodeId": "59", "title": "Architecture: The Domain Layer - MAD Skills", "content": "In this episode of MAD skills you'll learn about the Domain Layer - an optional layer which sits between the UI and Data layers. Developer Relations Engineer Don Turner will explain how the domain layer can simplify your app architecture, making it easier to understand and test.", "url": "https://www.youtube.com/watch?v=gIhjCh3U88I", @@ -400,7 +388,6 @@ object FakeDataSource { }, { "id": "12", - "episodeId": "59", "title": "Architecture: Organizing modules - MAD Skills", "content": "In this episode of Architecture for Modern Android Development Skills, Emily Kager shares a tip around organizing modules in Android apps.", "url": "https://www.youtube.com/watch?v=HB_r9wn49Gc", @@ -414,7 +401,6 @@ object FakeDataSource { }, { "id": "13", - "episodeId": "59", "title": "App Excellence: Android Architecture", "content": "How do you build a successful app? Our advice: think like a building architect. If you need help getting started, we have the perfect blueprint for success when building on Android. Check out our updated guide to Android App Architecture, and build something that your users will love.", "url": "https://www.youtube.com/watch?v=fodD6UHjLmw", @@ -428,7 +414,6 @@ object FakeDataSource { }, { "id": "14", - "episodeId": "59", "title": "Accessibility on TV - Integrate with Android TV & Google TV", "content": "Thinking about accessibility is critical when developing a quality app on Android TV OS. Ian will be covering the most common issues, the solution to these issues, and some more complex scenarios. ", "url": "https://www.youtube.com/watch?v=GyglHvJ6LMY", @@ -443,7 +428,6 @@ object FakeDataSource { }, { "id": "15", - "episodeId": "59", "title": "Google Play Billing - Integrate with Android TV & Google TV", "content": "In this episode of Integrate with Android TV & Google TV, Thomas will discuss how you can monetize your Android TV app using Google Play Billing. ", "url": "https://www.youtube.com/watch?v=gb55CjH7NHY", @@ -457,7 +441,6 @@ object FakeDataSource { }, { "id": "16", - "episodeId": "59", "title": "Android for Cars 🚗", "content": "Android for cars has introduced media recommendations powered by Google Assistant, a progress bar for long form content, and per-item content styles to allow browsable/playable items to be individually assigned to a list or grid. Head on over to the developer documentation to learn about all of these changes.", "url": "https://developer.android.com/cars", @@ -471,7 +454,6 @@ object FakeDataSource { }, { "id": "17", - "episodeId": "59", "title": "Google Play Academy🎓 - Go Global: Japan\n", "content": "With over 2 billion active Android devices globally, more and more developers are looking for new markets to expand. If you’re looking to succeed in Japan, one of the largest mobile app and gaming markets, these courses will cover strategies to make your content relevant across development, marketing and growth, and monetization.", "url": "https://www.youtube.com/watch?v=hY1HH-9efkg", @@ -485,7 +467,6 @@ object FakeDataSource { }, { "id": "18", - "episodeId": "59", "title": "Google Play Academy🎓 - Go Global: Southeast Asia", "content": "With over 2 billion active Android devices globally, more and more developers are looking for new markets to expand. If you’re looking to succeed in Southeast Asia, a fast-growing market that spends as much as 1.5x more time on the mobile internet than any other region, these courses will cover strategies to make your content relevant across development, marketing and growth, and monetization.", "url": "https://www.youtube.com/watch?v=j9VRzvDhTO0", @@ -499,7 +480,6 @@ object FakeDataSource { }, { "id": "19", - "episodeId": "59", "title": "Google Play Academy🎓 - Design for All Users", "content": "Learn how to optimize for onboarding, build accessible apps, and reduce app size to reach more users.", "url": "https://www.youtube.com/watch?v=07NUULjEJ5A", @@ -513,7 +493,6 @@ object FakeDataSource { }, { "id": "20", - "episodeId": "59", "title": "Game development 🎮", "content": "We covered how to help you monitor your game’s stability using Android vitals on Google Play Console, how to best optimize your game to improve your customer engagement during the month of Ramadan, and announced that the Indie Games Accelerator & Indie Games Festival 2022 from Google Play is coming soon, offering a way to get notified when submissions open.", "url": "https://www.youtube.com/watch?v=m2gTnT2kCRQ", @@ -527,7 +506,6 @@ object FakeDataSource { }, { "id": "21", - "episodeId": "59", "title": "Migrating Architecture Blueprints to Jetpack Compose", "content": "Manuel wrote about how and why we’ve Migrated our Architecture Blueprints to Jetpack Compose, and some issues we faced in doing so.", "url": "https://medium.com/androiddevelopers/migrating-architecture-blueprints-to-jetpack-compose-8ffa6615ede3", @@ -544,7 +522,6 @@ object FakeDataSource { }, { "id": "22", - "episodeId": "59", "title": "The curious case of FLAG_ACTIVITY_LAUNCH_ADJACENT", "content": "Pietro wrote about how to enable split screen use cases using the Android 7.0 FLAG_ACTIVITY_LAUNCH_ADJACENT flag to open your Activity in a new adjacent window on Android 12L. (and some supported Android 11+ devices)", "url": "https://medium.com/androiddevelopers/the-curious-case-of-flag-activity-launch-adjacent-f1212f37a8e0", @@ -560,7 +537,6 @@ object FakeDataSource { }, { "id": "23", - "episodeId": "59", "title": "AndroidX releases 🚀", "content": "AppCompat AppCompat-Resources Version 1.5.0-alpha01 contains a bunch of bugfixes, as well as updated nullability to match Android 13 DP2 and a few small compatibility features involving TextView, AppCompatDialog, SearchView, and SwitchCompat.\n\nNavigation Version 2.4.2 has been released with all the new bugfixes backported from the 2.5 alpha releases.", "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", @@ -574,7 +550,6 @@ object FakeDataSource { }, { "id": "24", - "episodeId": "59", "title": "ADB Podcast Episodes🎙184: Skia and AGSL - Shaders of Things to Come", "content": "In this episode Tor, Chet, and Romain chat with Derek and Brian from the Skia team about Skia (the graphics layer that backs the Android Canvas APIs), pixel shaders, and the new “AGSL” API that lets you provide pixel shaders for advanced graphics effects, which was recently added as part of the Android T preview release. If you’re interested in graphics technology, this is the episode for you.", "url": "https://adbackstage.libsyn.com/episode-184-skia-and-agsl-shaders-of-things-to-come", @@ -593,7 +568,6 @@ object FakeDataSource { }, { "id": "25", - "episodeId": "58", "title": "Android 13 DP 2 😍", "content": "Recently we shared Android 13 Developer Preview 2 with more new features and changes for you to try in your apps! Some notable features include Developer downgradable permissions which allows your app to protect user privacy by downgrading previously granted runtime permissions, and Bluetooth LE Audio which helps users receive high fidelity audio without sacrificing battery life; it can also seamlessly switch between different use cases that were not possible with Bluetooth Classic. Check out all the new features in the post!", "url": "https://android-developers.googleblog.com/2022/03/second-preview-android-13.html", @@ -609,7 +583,6 @@ object FakeDataSource { }, { "id": "26", - "episodeId": "58", "title": "Architecture: The data layer - MAD Skills", "content": "Jose goes over the data layer and its two components: repositories and data sources. You will dive deeper into what the roles of these two are and understand their differences. You will also learn about data immutability, error handling, threading testing and more!", "url": "https://www.youtube.com/watch?v=r5AseKQh2ZE", @@ -623,7 +596,6 @@ object FakeDataSource { }, { "id": "27", - "episodeId": "58", "title": "Architecture: The UI layer - MAD Skills", "content": "TJ covers the UI layer in this episode of MAD skills using the JetNews sample app as a case study You will learn UI Layer pipeline, UI state exposure, UI state consumption and more!", "url": "https://www.youtube.com/watch?v=p9VR8KbmzEE", @@ -639,7 +611,6 @@ object FakeDataSource { }, { "id": "28", - "episodeId": "58", "title": "Account Linking - Integrate with Android TV & Google TV", "content": "Miguel and Adekunle discuss account linking. ​​Google Account Linking lets you safely link a user’s Google Account with their account on your platform, granting Google applications and devices access to your services and is needed for many integrations on Android TV & Google TV. They discuss the basics of OAuth like implementing your authorization, token exchange, and revocation endpoints. You will also learn the difference between the Web OAuth, Streamlined, and App Flip linking flows.", "url": "https://www.youtube.com/watch?v=-Fa99hpUsdk", @@ -653,7 +624,6 @@ object FakeDataSource { }, { "id": "29", - "episodeId": "58", "title": "Modern media playback on Android - Integrate with Android TV & Google TV", "content": "Paul explores best practices for integrating and validating media sessions, the unified way for Android apps to interact with media content. MediaSessions allows different devices like smart speakers, watches, peripherals and accessories to surface and interact with playback and content metadata.", "url": "https://www.youtube.com/watch?v=OYy41ceW59s", @@ -669,7 +639,6 @@ object FakeDataSource { }, { "id": "30", - "episodeId": "58", "title": "FHIR SDK for Android Developers 🏥", "content": "Community health workers in low-and-middle-income countries use mobile devices as critical tools for doing community outreach and providing crucial health services. Unfortunately, the lack of data interoperability means that patient records are fragmented and caregivers may only receive incomplete information. Last year, Google introduced a collaboration with the World Health Organization to build an open source software developer kit designed to help developers build mobile solutions using the Fast Healthcare Interoperability Resources (FHIR) global standard for healthcare data. Read the article to learn more about how this SDK can help you create apps to aid community health workers in low-and-middle-income countries.", "url": "https://medium.com/androiddevelopers/our-fhir-sdk-for-android-developers-9f8455e0b42f", @@ -683,7 +652,6 @@ object FakeDataSource { }, { "id": "31", - "episodeId": "58", "title": "Helping Users Discover Quality Apps on Large Screens 🔎", "content": "Adoption of large screens is growing rapidly and now there are over 250M active Android tablets, foldables, and ChromeOS devices. To help people get the most from their devices, we’re making three big changes in Google Play to enable users to discover and engage with high quality apps and games: ranking and promotability changes, alerts for low quality apps, and device-specific ratings and reviews. Read all about it in the post!", "url": "https://android-developers.googleblog.com/2022/03/helping-users-discover-quality-apps-on.html", @@ -699,7 +667,6 @@ object FakeDataSource { }, { "id": "32", - "episodeId": "58", "title": "Access Android vitals data through the new Play Developer Reporting API", "content": "In this article Lauren talks about Android vitals which are a great way to track how your app is performing in Google Play Console. Now there are new use cases for Android vitals which include building internal dashboards, joining with other datasets for deeper analysis and automation troubleshooting and releases. Learn more about the API and how to use it in this post!", "url": "https://android-developers.googleblog.com/2022/03/play-developer-reporting-API.html", @@ -713,7 +680,6 @@ object FakeDataSource { }, { "id": "33", - "episodeId": "58", "title": "Using performance class to optimize your user experience", "content": "The Jetpack Core Performance library in alpha has launched! This library enables you to easily identify what a device is capable of, and tailor your user experience accordingly. As developer, this means you can reliably group devices with the same level of performance and tailor your app’s behavior to those different groups. This enables you to deliver an optimal experience to users with both more and less capable devices.", "url": "https://android-developers.googleblog.com/2022/03/using-performance-class-to-optimize.html", @@ -729,7 +695,6 @@ object FakeDataSource { }, { "id": "34", - "episodeId": "58", "title": "AndroidX releases 🚀", "content": "We have a few libraries in alpha-01 including Activity Version 1.6.0-alpha01, CustomView-Poolingcontainer Version 1.0.0-alpha01, and Junit-Gtest Version 1.0.0-alpha01.\n\nCar App Version 1.2.0-rc01 and Mediarouter Version 1.3.0-rc01 are also in rc-01.", "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", @@ -743,7 +708,6 @@ object FakeDataSource { }, { "id": "35", - "episodeId": "57", "title": "Discontinuing Kotlin synthetics for views", "content": "Synthetic properties to access views were created as a way to eliminate the common boilerplate of findViewById calls. These synthetics are provided by JetBrains in the Kotlin Android Extensions Gradle plugin (not to be confused with Android KTX).", "url": "https://android-developers.googleblog.com/2022/02/discontinuing-kotlin-synthetics-for-views.html", @@ -760,7 +724,6 @@ object FakeDataSource { }, { "id": "36", - "episodeId": "57", "title": "Things to know from the 2022 Google for Games Developer Summit", "content": "This week marked the 2022 Google for Games Developer Summit, Google’s biggest event of the year centered around game development. The Android team shared information around the next generation of services, tools and features to help you develop and deliver high quality games. ", "url": "https://android-developers.googleblog.com/2022/03/GGDS-recap-blog.html", @@ -776,7 +739,6 @@ object FakeDataSource { }, { "id": "37", - "episodeId": "57", "title": "MAD Skills: DataStore and Introduction to Architecture💡", "content": "Now that our MAD Skills series on Jetpack DataStore is complete, let’s do a quick wrap up of all the things we’ve covered in each episode.", "url": "https://android-developers.googleblog.com/2022/03/jetpack-datastore-wrap-up.html", @@ -792,7 +754,6 @@ object FakeDataSource { }, { "id": "38", - "episodeId": "57", "title": "Play Time with Jetpack Compose", "content": "Learn about Google Play Store’s strategy for adopting Jetpack Compose, how they overcame specific performance challenges, and improved developer productivity and happiness.", "url": "https://android-developers.googleblog.com/2022/03/play-time-with-jetpack-compose.html", @@ -810,7 +771,6 @@ object FakeDataSource { }, { "id": "39", - "episodeId": "57", "title": "App Excellence Summit 2022 ⭐", "content": "Did you know that 54% of users who left a 1-star review in the Play Store mentioned app stability and bugs? *\n\nTo help product managers and business decision makers understand how high quality app experiences drive business growth and what tools they can use to make sound business and technical decisions, we are hosting our first Android App Excellence Summit in just a few weeks!", "url": "https://android-developers.googleblog.com/2022/03/app-excellence-summit-2022.html", @@ -824,7 +784,6 @@ object FakeDataSource { }, { "id": "40", - "episodeId": "57", "title": "#TheAndroidShow: Tablets, Jetpack Compose, and Android 13 📹", "content": "Last week, Florina and Huyen hosted #TheAndroidShow, where we went Behind the scenes with animations & Jetpack Compose, asked whether now is the moment to think tablet first, and covered Android 13 along with other key themes for Android this year.", "url": "https://www.youtube.com/watch?v=WL9h46CymlU", @@ -843,7 +802,6 @@ object FakeDataSource { }, { "id": "41", - "episodeId": "57", "title": "Freeing up 60% of storage for apps 💾", "content": "App archiving will allow users to reclaim ~60% of app storage temporarily by removing parts of the app rather than uninstalling the app completely.", "url": "https://android-developers.googleblog.com/2022/03/freeing-up-60-of-storage-for-apps.html", @@ -860,7 +818,6 @@ object FakeDataSource { }, { "id": "42", - "episodeId": "57", "title": "Demystifying Jetpack Glance for app widgets", "content": "We recently announced the first Alpha version of Glance, initially with support for AppWidgets and now for Tiles for Wear OS. This new framework is built on top of the Jetpack Compose runtime and designed to make it faster and easier to build “glanceables” such as app widgets without having to handle a lot of boilerplate code or lifecycle events to connect different components.", "url": "https://medium.com/androiddevelopers/demystifying-jetpack-glance-for-app-widgets-8fbc7041955c", @@ -877,7 +834,6 @@ object FakeDataSource { }, { "id": "43", - "episodeId": "57", "title": "Keeping Google Play safe with our key 2022 initiatives 🔒", "content": "We shared information about what’s ahead in 2022 for Google Play’s privacy and safety initiatives to give you time to prepare.", "url": "https://android-developers.googleblog.com/2022/03/privacy-and-security-direction.html", @@ -893,7 +849,6 @@ object FakeDataSource { }, { "id": "44", - "episodeId": "57", "title": "Games-Activity Version 1.1.0", "content": "adds WindowInsets listening/querying for notch and IME response along with key and motion event filters.", "url": "https://developer.android.com/jetpack/androidx/releases/games#1.1.0", @@ -907,7 +862,6 @@ object FakeDataSource { }, { "id": "45", - "episodeId": "57", "title": "Room Version 2.5.0-alpha01", "content": "Converted room-common, room-migration, and paging related files in room-runtime from Java to Kotlin along with a new API for multi-process lock to protect multi-process 1st time database creation and migrations", "url": "https://developer.android.com/jetpack/androidx/releases/room#2.5.0-alpha01", @@ -921,7 +875,6 @@ object FakeDataSource { }, { "id": "46", - "episodeId": "57", "title": "Media Version 1.6.0-alpha 01", "content": "Adds the extras necessary to setup a signin/settings page using CarAppLibrary.", "url": "https://developer.android.com/jetpack/androidx/releases/media#media-1.6.0-alpha01", @@ -935,7 +888,6 @@ object FakeDataSource { }, { "id": "47", - "episodeId": "57", "title": "AppCompat-Resources Version 1.6.0-alpha01", "content": "Adds support for customizing locales, providing backwards compatibility for the Android 13 per-language preferences API", "url": "https://developer.android.com/jetpack/androidx/releases/appcompat#1.6.0-alpha01", @@ -949,7 +901,6 @@ object FakeDataSource { }, { "id": "48", - "episodeId": "57", "title": "Recording Video with CameraX VideoCapture API", "content": "A picture is worth a thousand words, and CameraX ImageCapture has already made it much easier to tell your story through still images on Android. Now with the new VideoCapture API, CameraX can help you create thousands of continuous pictures to tell an even better and more engaging story!", "url": "https://medium.com/androiddevelopers/recording-video-with-camerax-videocapture-api-a36cfd8a48c8", @@ -965,7 +916,6 @@ object FakeDataSource { }, { "id": "49", - "episodeId": "57", "title": "Unbundling the stable WindowManager", "content": "The 1.0.0 stable release of Jetpack WindowManager, the foundation for great experiences on all types of large screen devices.", "url": "https://medium.com/androiddevelopers/unbundling-the-stable-windowmanager-a5471ff2907", @@ -981,7 +931,6 @@ object FakeDataSource { }, { "id": "50", - "episodeId": "56", "title": "Jetpack Compose 1.1 is now stable!", "content": "Last week we released version 1.1 of Jetpack Compose and Florina Muntenescu wrote an article giving us all the information! This release contains new features like improved focus handling, touch target sizing, ImageVector caching and support for Android 12 stretch overscroll. This also means that previously experimental APIs are now stable. Check out our recently updated samples, codelabs, and the Accompanist library!", "url": "https://android-developers.googleblog.com/2022/02/jetpack-compose-11-now-stable.html", @@ -997,7 +946,6 @@ object FakeDataSource { }, { "id": "51", - "episodeId": "56", "title": "MAD Skills: DataStore", "content": "The DataStore MAD Skills series rolls on! In the sixth episode, Simona Stojanovic covered DataStore: Best Practices part 2 covering DataStore-to-DataStore migration. This is used when you make significant changes to your dataset like renaming your data model values or changing their type. ", "url": "https://medium.com/androiddevelopers/datastore-and-data-migration-fdca806eb1aa", @@ -1013,7 +961,6 @@ object FakeDataSource { }, { "id": "52", - "episodeId": "56", "title": "DataStore and Testing", "content": "For the final part of the DataStore series, Simona covered DataStore and testing and teaches you how to fully test your DataStore.", "url": "https://medium.com/androiddevelopers/datastore-and-testing-edf7ae8df3d8", @@ -1029,7 +976,6 @@ object FakeDataSource { }, { "id": "53", - "episodeId": "56", "title": "Material You: Coming to more Android Devices near you", "content": "Material You will soon be available on more Android 12 phones globally including devices by Samsung, Oppo, OnePlus and more! Material You has made the Android experience more fluid and personal than ever. Our OEM partners continue to work with us to ensure that key design APIs work consistently across the Android ecosystem so developers can benefit from a cohesive experience.", "url": "https://android-developers.googleblog.com/2022/02/material-you-coming-to-more-android.html", @@ -1046,7 +992,6 @@ object FakeDataSource { }, { "id": "54", - "episodeId": "56", "title": "The first developer preview of Android 13", "content": "We’re sharing a first look at the next release of Android, with the Android 13 Developer Preview 1. With Android 13 we’re continuing some important themes: privacy and security, as well as developer productivity. We’ll also build on some of the newer updates we made in 12L to help you take advantage of the 250+ million large screen Android devices currently running.", "url": "https://android-developers.googleblog.com/2022/02/first-preview-android-13.html", @@ -1062,7 +1007,6 @@ object FakeDataSource { }, { "id": "55", - "episodeId": "56", "title": "AndroidX releases 🚀", "content": "Since Compose just went stable, the Animation, Compiler, Foundation, Material, Runtime and UI Versions also went stable! Games-Text-Input and ProfileInstaller also went stable! \n\nThere are a bunch of new APIs in alpha including new Testing APIs (Test Runner, Test Monitor, Test Services and Test Orchestrator), Metrics Version and Startup Version.", "url": "https://developer.android.com/jetpack/androidx/versions/all-channel#february_9_2022", @@ -1079,7 +1023,6 @@ object FakeDataSource { }, { "id": "56", - "episodeId": "55", "title": "DataStore best practices part 1", "content": "learn about performing synchronous work and how to make it work with Kotlin data class serialization and Hilt.", "url": "https://www.youtube.com/watch?v=S10ci36lBJ4", @@ -1095,7 +1038,6 @@ object FakeDataSource { }, { "id": "57", - "episodeId": "55", "title": "All about Proto DataStore", "content": "In this post, we will learn about Proto DataStore, one of two DataStore implementations. We will discuss how to create it, read and write data and how to handle exceptions, to better understand the scenarios that make Proto a great choice.", "url": "https://medium.com/androiddevelopers/all-about-proto-datastore-1b1af6cd2879", @@ -1111,7 +1053,6 @@ object FakeDataSource { }, { "id": "58", - "episodeId": "55", "title": "Glance: Tiles for Wear OS made simple ⌚️", "content": "Last year we announced the Wear Tiles API. To complement that Java API, we are excited to announce that support for Wear OS Tiles has been added to Glance, a new framework built on top of Jetpack Compose designed to make it easier to build for surfaces outside your Android app. As this library is in alpha, we’d love to get your feedback.", "url": "https://android-developers.googleblog.com/2022/01/announcing-glance-tiles-for-wear-os.html", @@ -1127,7 +1068,6 @@ object FakeDataSource { }, { "id": "59", - "episodeId": "55", "title": "Android Studio Bumblebee 🐝 stable", "content": "Android Studio Bumblebee (2021.1.1) is now stable. We’ve since patched it to address some launch issues — so make sure to upgrade! It improves functionality across the typical developer workflow: Build and Deploy, Profiling and Inspection, and Design.", "url": "https://android-developers.googleblog.com/2022/01/android-studio-bumblebee-202111-stable.html", @@ -1143,7 +1083,6 @@ object FakeDataSource { }, { "id": "60", - "episodeId": "55", "title": "All about Preferences DataStore", "content": "In this post, we will take a look at Preferences DataStore, one of two DataStore implementations. We will go over how to create it, read and write data, and how to handle exceptions, all of which should, hopefully, provide you with enough information to decide if it’s the right choice for your app.", "url": "https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334", @@ -1159,7 +1098,6 @@ object FakeDataSource { }, { "id": "61", - "episodeId": "55", "title": "Building apps for Android Automotive OS 🚘", "content": "The Car App Library version 1.2 is already in beta, enabling app developers to start building their navigation, parking, and charging apps for Android Automotive OS. Now, developers can begin building and testing apps for these categories using the Automotive OS emulator across both Android Automotive OS and Android Auto.", "url": "https://android-developers.googleblog.com/2022/01/building-apps-for-android-automotive-os.html", @@ -1175,7 +1113,6 @@ object FakeDataSource { }, { "id": "62", - "episodeId": "55", "title": "Navigation 2.4 is stable ", "content": "It’s been rewritten in Kotlin, with two pane integration, Navigation routes + Kotlin DSL improvements, Navigation Compose’s first stable release, and multiple back stack support.", "url": "https://developer.android.com/jetpack/androidx/releases/navigation#2.4.0", @@ -1189,7 +1126,6 @@ object FakeDataSource { }, { "id": "63", - "episodeId": "55", "title": "Google Maps with Jetpack Compose", "content": "A project which contains Jetpack Compose components for the Google Maps SDK for Android.\n\n", "url": "https://github.com/googlemaps/android-maps-compose", @@ -1203,7 +1139,6 @@ object FakeDataSource { }, { "id": "64", - "episodeId": "55", "title": "Improving App Performance with Baseline Profiles", "content": "In this blog post we’ll discuss Baseline Profiles and how they improve app and library performance, including startup time by up to 40%. While this blogpost focuses on startup, baseline profiles also significantly improve jank as well.", "url": "https://android-developers.googleblog.com/2022/01/improving-app-performance-with-baseline.html", @@ -1221,7 +1156,6 @@ object FakeDataSource { }, { "id": "65", - "episodeId": "55", "title": "Smule Adopts Google’s Oboe to Improve Recording Quality & Completion Rates", "content": "As the most downloaded singing app of all time, Smule Inc. has been investing on Android to improve the overall audio quality and, more specifically, to reduce latency, i.e. allowing singers to hear their voices in the headset as they perform. The teams specialized in Audio and Video allocated a significant part of 2021 into making the necessary changes to convert the Smule application used by over ten million Android users from using the OpenSL audio API to the Oboe audio library, enabling roughly a 10%+ increase in recording completion rate.", "url": "https://android-developers.googleblog.com/2022/02/smule-adopts-googles-oboe-to-improve.html", @@ -1235,7 +1169,6 @@ object FakeDataSource { }, { "id": "66", - "episodeId": "55", "title": "Guide to background work", "content": "Do you use coroutines or WorkManager for background work? The team updated the guide to background work to help you choose which library is best for your use case. It depends on whether or not the work is persistent, and if it needs to run immediately, it’s long running, or deferrable.", "url": "https://developer.android.com/guide/background", @@ -1249,7 +1182,6 @@ object FakeDataSource { }, { "id": "67", - "episodeId": "55", "title": "Accessibility best practices", "content": "If you work on Android TV, you should be aware of the accessibility best practices that the team created. It provides recommendations for both native and non-native apps. Get to know why accessibility is important for your TV app, how to evaluate your apps when TalkBack is used, how to adopt system caption settings, and more!", "url": "https://developer.android.com/training/tv/accessibility", @@ -1264,7 +1196,6 @@ object FakeDataSource { }, { "id": "68", - "episodeId": "55", "title": "TalkBack - the Google screen reader", "content": "Next up in the Accessibility series is TalkBack, the Google screen reader! In this video, learn what TalkBack is, how to set it up, how to navigate through your app with it, and how you can use it to improve the Accessibility of your app.", "url": "https://www.youtube.com/watch?v=_1yRVwhEv5I", @@ -1278,7 +1209,6 @@ object FakeDataSource { }, { "id": "69", - "episodeId": "55", "title": "ADB Podcast 182: Large screens are a big deal", "content": "Clara, Florina and Daniel join your usual hosts to talk about large screens, what they are and what they mean for app developers. You will also learn about the resources at your disposal to build high quality experiences on large screen devices: from samples and guidance to canonical layouts and new APIs such as window size classes. Disclaimer: Florina is very excited about this, don’t miss the epic Large screens! Large screens! Large screens! intro!", "url": "https://adbackstage.libsyn.com/episode-182-large-screens-are-a-big-deal", @@ -1292,7 +1222,6 @@ object FakeDataSource { }, { "id": "70", - "episodeId": "54", "title": "Jetpack Alpha for Glance Widgets 🔍", "content": "We made the first release of Jetpack Glance available, a new framework designed to make it faster and easier to build app widgets for the home screen and other surfaces. Glance offers similar modern, declarative Kotlin APIs that you are used to with Jetpack Compose, helping you build beautiful, responsive app widgets with way less code. Glance provides a base-set of its own Composables to help build “glanceable” experiences — starting today with app widget components but with more coming. Using the Jetpack Compose runtime, Glance translates these Composables into RemoteViews that can be displayed in an app widget", "url": "https://android-developers.googleblog.com/2021/12/announcing-jetpack-glance-alpha-for-app.html", @@ -1308,7 +1237,6 @@ object FakeDataSource { }, { "id": "71", - "episodeId": "54", "title": "Jetpack Watch Face Library ⌚", "content": "We launched the Jetpack Watch Face library written from the ground up in Kotlin, including all functionality from the Wearable Support Library along with many new features such as: Watch face styling which persists across both the watch and phone (with no need for your own database or companion app); Support for a WYSIWYG watch face configuration UI on the phone; Smaller, separate libraries (that only include what you need); Battery improvements through promoting good battery usage patterns out of the box, such as automatically reducing the interactive frame rate when the battery is low; New screenshot APIs so users can see previews of their watch face changes in real time on both the watch and phone.\n\nIf you are still using the Wearable Support Library, we strongly encourage migrating to the new Jetpack libraries to take advantage of the new APIs and upcoming features and bug fixes.", "url": "https://android-developers.googleblog.com/2021/12/develop-watch-faces-with-stable-jetpack.html", @@ -1326,7 +1254,6 @@ object FakeDataSource { }, { "id": "72", - "episodeId": "54", "title": "Rebuilding our Guide to App Architecture 📐", "content": "We launched a revamped guide to app architecture which includes best practices. As Android apps grow in size, it’s important to design the code with an architecture in place that allows the app to scale, improves quality and robustness, and makes testing easier. The guide contains pages for UI, domain, and data layers including deep dives into more complex topics, such as how to handle UI events. We also have a learning pathway to walk you through it.", "url": "https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html", @@ -1342,7 +1269,6 @@ object FakeDataSource { }, { "id": "73", - "episodeId": "54", "title": "Google Play Games on PC Beta 🎮", "content": "We announced that we’re opening sign-ups for Google Play Games on PC as a beta in Korea, Taiwan, and Hong Kong, allowing users participating in the beta to play a catalog of Google Play games on their PC via a standalone application built by Google. The developer site has a form to express interest, along with information about bringing your Android game to PCs. It involves many of the same updates that you do to optimize your game for Chrome OS devices, such as support for Mouse and Keyboard controls.", "url": "https://developers.googleblog.com/2022/01/googleplaygames.html", @@ -1358,7 +1284,6 @@ object FakeDataSource { }, { "id": "74", - "episodeId": "54", "title": "MAD Skills: Gradle 🐘", "content": "Murat covered building custom plugins in more depth, including the Artifact API in addition to the Variant API covered previously. It demonstrates building a plugin which automatically updates the version code specified in the app manifest with the git version. With the AGP 7.0 release, you can use these APIs to control build inputs, read, modify, or even replace intermediate and final artifacts.", "url": "https://medium.com/androiddevelopers/gradle-and-agp-build-apis-taking-your-plugin-to-the-next-step-95e7bd1cd4c9", @@ -1374,7 +1299,6 @@ object FakeDataSource { }, { "id": "75", - "episodeId": "54", "title": "Gradle and AGP Build APIs: Community tip - MAD Skills", "content": "On this episode of Gradle and AGP Build APIs for MAD Skills, Alex Saveau walks you through manipulating Android build artifacts with the Android Gradle Plugin (AGP) and Gradle APIs.", "url": "https://www.youtube.com/watch?v=8SFfffaB0CU", @@ -1390,7 +1314,6 @@ object FakeDataSource { }, { "id": "76", - "episodeId": "54", "title": "Gradle and AGP Build APIs: Taking your plugin to the next step - MAD Skills", "content": "On this episode of Gradle and AGP Build APIs for MAD Skills, Murat will discuss Gradle tasks, providers, properties, and basics of task inputs and outputs. Next, you will be able to take your plugin a step further and learn how to get access to various build artifacts using the new Artifact API. ", "url": "https://www.youtube.com/watch?v=SB4QlngQQW0", @@ -1406,7 +1329,6 @@ object FakeDataSource { }, { "id": "77", - "episodeId": "54", "title": "MAD Skills Gradle and AGP build APIs Wrap Up!", "content": "This wrap-up post summarizes the whole MAD Skills Gradle series", "url": "https://android-developers.googleblog.com/2021/12/mad-skills-gradle-and-agp-build-apis.html", @@ -1422,7 +1344,6 @@ object FakeDataSource { }, { "id": "78", - "episodeId": "54", "title": "MAD Skills: DataStore 🗄️", "content": "Simona began MAD Skills: DataStore. DataStore is a thread-safe, non-blocking library in Android Jetpack that provides a safe and consistent way to store small amounts of data, such as preferences or application state, replacing SharedPreferences. It provides an implementation that stores typed objects backed by protocol buffers (Proto DataStore) and an implementation that stores key-value pairs (Preferences DataStore).", "url": "https://www.youtube.com/watch?v=9ws-cJzlJkU", @@ -1438,7 +1359,6 @@ object FakeDataSource { }, { "id": "79", - "episodeId": "54", "title": "AndroidX releases 🚀", "content": "Since the last Now in Android episode, a lot of libraries were promoted to stable! Compose ConstraintLayout brings support for ConstraintLayout syntax to Compose. We also released CoordinatorLayout 1.2, Car App 1.1.0, Room 2.4.0, Sqlite 2.2.0, Collection 1.2.0, and Wear Watchface 1.0.0.\n\nOur first alpha of Jetpack Compose 1.2 was released, along with alphas for Glance 1.0.0, Core-Ktx 1.8.0, WorkManager 2.8.0, Mediarouter 1.3.0, Emoji2 1.1.0, Annotation 1.4.0, Core-RemoteViews, Core-Peformance, and more.", "url": "https://developer.android.com/jetpack/androidx/versions/all-channel#december_1_2021", @@ -1452,7 +1372,6 @@ object FakeDataSource { }, { "id": "80", - "episodeId": "54", "title": "Jetnews for every screen", "content": "Alex wrote about the recent updates to Jetnews that improves its behavior across big and small mobile devices. It describes our design and development process so that you can learn our philosophy and associated implementation steps for building an application optimized for all screens with Jetpack Compose, including how to build a list/detail layout.", "url": "https://medium.com/androiddevelopers/jetnews-for-every-screen-4d8e7927752", @@ -1468,7 +1387,6 @@ object FakeDataSource { }, { "id": "81", - "episodeId": "54", "title": "Simplifying drag and drop", "content": "Paul wrote about drag & drop, and how the Android Jetpack DragAndDrop library alpha makes it easier to handle data dropped into your app.", "url": "https://medium.com/androiddevelopers/simplifying-drag-and-drop-3713d6ef526e", @@ -1484,7 +1402,6 @@ object FakeDataSource { }, { "id": "82", - "episodeId": "54", "title": "Accessibility series 🌐: Handling content that times out - Accessibility on Android", "content": "The accessibility series continues on, beginning with an episode on how to properly implement UI elements that disappear after a set amount of time.", "url": "https://www.youtube.com/watch?v=X97P6Y8WHl0", @@ -1500,7 +1417,6 @@ object FakeDataSource { }, { "id": "83", - "episodeId": "54", "title": "Accessibility series 🌐: Acessibility Scanner", "content": "We also cover how Accessibility Scanner can help you improve your app for all users by suggesting improvements in areas of accessibility.", "url": "https://www.youtube.com/watch?v=i1gMzQv0hWU", @@ -1516,7 +1432,6 @@ object FakeDataSource { }, { "id": "84", - "episodeId": "54", "title": "Accessibility series 🌐: Accessibility test framework and Espresso - Accessibility on Android", "content": "We investigate how Espresso and the Accessibility Test Framework can help you create automated accessibility tests.", "url": "https://www.youtube.com/watch?v=DLN2s16HwcE", @@ -1532,7 +1447,6 @@ object FakeDataSource { }, { "id": "85", - "episodeId": "54", "title": "Android TV & Google TV 📺", "content": "Mayuri covered best practices for the Watch Next API on Android TV & Google TV, which increases engagement with your app by allowing your content to show up in the Watch Next row.", "url": "https://www.youtube.com/watch?v=QFMIP5GOo70", @@ -1548,7 +1462,6 @@ object FakeDataSource { }, { "id": "86", - "episodeId": "54", "title": "ADB Podcast 179: Flibberty Widget", "content": "In this episode, Chet and Romain talked with Nicole McWilliams and Petr Čermák from the London engineering office about their work on App Widgets and Digital Wellbeing.", "url": "https://adbackstage.libsyn.com/flibberty-widget", @@ -1567,7 +1480,6 @@ object FakeDataSource { }, { "id": "87", - "episodeId": "54", "title": "ADB Podcast 180: Kotlin Magic Platform", "content": "In this episode, we chat with Yigit Boyar from the Android Toolkit Team about Kotlin multi platform, while Romain provides light background music on his piano.", "url": "https://adbackstage.libsyn.com/episode-180-kotlin-magic-platform", @@ -1586,7 +1498,6 @@ object FakeDataSource { }, { "id": "88", - "episodeId": "54", "title": "ADB Podcast 181: Architecture → Fewer bugs at the end", "content": "In this episode, we chat with Yigit Boyar (again!) from the Android Toolkit Team and Manuel Vivo from the Developer Relations team about application architecture. The team has released new architecture guidance, and we talk about that guidance here, as well as how our architecture recommendations apply in the new Jetpack Compose world.", "url": "https://adbackstage.libsyn.com/episode-181-architecture-fewer-bugs-at-the-end", @@ -1605,7 +1516,6 @@ object FakeDataSource { }, { "id": "89", - "episodeId": "53", "title": "Android 12", "content": "We released Android 12 and pushed it to the Android Open Source Project (AOSP). We introduced a new design language called Material You. We reduced the CPU time used by core system services, added performance class device capabilities, and added new features to improve performance. Users have more control of their privacy with the Privacy Dashboard and other new security and privacy features. We improved the user experience with a unified API for rich content insertion, compatible media transcoding, easier blurs and effects, AVIF image support, enhanced haptics, new camera effects/capabilities, improved native crash debugging, support for rounded screen corners, Play as you download, and Game Mode APIs.", "url": "https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html", @@ -1621,7 +1531,6 @@ object FakeDataSource { }, { "id": "90", - "episodeId": "53", "title": "Compose", "content": "Jetpack Compose, Android’s modern, native UI toolkit became stable and ready for you to adopt in production. It interoperates with your existing app, integrates with existing Jetpack libraries, implements Material Design with straightforward theming, supports lists with Lazy components using minimal boilerplate, and has a powerful, extensible animation system. You can learn more about working with Compose in the Compose learning path and see where we’re going in future Compose releases in the Compose roadmap.", "url": "https://developer.android.com/jetpack/compose", @@ -1635,7 +1544,6 @@ object FakeDataSource { }, { "id": "91", - "episodeId": "53", "title": "Training", "content": "This year, the Android Training Team released the final four new units of Android Basics in Kotlin.", "url": "https://developer.android.com/courses/android-basics-kotlin/course", @@ -1649,7 +1557,6 @@ object FakeDataSource { }, { "id": "92", - "episodeId": "53", "title": "Introduction to Kotlin and Jetpack ", "content": "Learn the basics of Jetpack KTX libraries, how to simplify callbacks with coroutines and Flow, and how to use and test Room/WorkManager APIs.", "url": "https://youtu.be/nw7nnlHDkHw?list=PLWz5rJ2EKKc98e0f5ZbsgB63MdjZTFgsy", @@ -1666,7 +1573,6 @@ object FakeDataSource { }, { "id": "93", - "episodeId": "53", "title": "Introduction to Motion Layout", "content": "Learn how to use MotionLayout and its design tool to create rich, animated experiences.", "url": "https://www.youtube.com/watch?v=M1jE3W3_NTQ&list=PLWz5rJ2EKKc_PEOEHNBEyy6tPX1EgtUw2", @@ -1684,7 +1590,6 @@ object FakeDataSource { }, { "id": "94", - "episodeId": "53", "title": "Introduction to WorkManager", "content": "Learn how to schedule critical background work with WorkManager: from basic usage, threading, custom configuration and more.", "url": "https://www.youtube.com/watch?v=NtpgWjiXEfg&list=PLWz5rJ2EKKc_J88-h0PhCO_aV0HIAs9Qk", @@ -1700,7 +1605,6 @@ object FakeDataSource { }, { "id": "95", - "episodeId": "53", "title": "Introduction to Navigation", "content": "Learn the basics of the Navigation component, specific features of the tool and the APIs to create and navigate to destinations.", "url": "https://www.youtube.com/watch?list=PLWz5rJ2EKKc9VpBMZUS9geQtc5RJ2RsUd&v=fiQiMy0HzsY&feature=emb_title", @@ -1716,7 +1620,6 @@ object FakeDataSource { }, { "id": "96", - "episodeId": "53", "title": "Introduction to Performance", "content": "Learn about using system tracing and sampling profiling to debug performance issues in apps.", "url": "https://www.youtube.com/watch?v=_5LgIrd4O5g&list=PLWz5rJ2EKKc-xjSI-rWn9SViXivBhQUnp", @@ -1732,7 +1635,6 @@ object FakeDataSource { }, { "id": "97", - "episodeId": "53", "title": "Introduction to Hilt", "content": "Learn how to add and use Hilt for dependency injection in your Android app, best practices for testing with Hilt, and more advanced content.", "url": "https://www.youtube.com/watch?v=mnMCgjuMJPA&list=PLWz5rJ2EKKc_9Qo-RBRYhVmME1iR4oeTK", @@ -1748,7 +1650,6 @@ object FakeDataSource { }, { "id": "98", - "episodeId": "53", "title": "Paging", "content": "Learn the basics of paging, from the core types to binding them to your UI elements.", "url": "https://www.youtube.com/watch?v=Pw-jhS-ucYA&list=PLWz5rJ2EKKc9L-fmWJLhyXrdPi1YKmvqS", @@ -1764,7 +1665,6 @@ object FakeDataSource { }, { "id": "99", - "episodeId": "53", "title": "Introduction to Gradle and AGP Build APIs\n", "content": "Learn how to configure your build, customize the build process to your needs and how to write your own plugins to extend your build even further.", "url": "https://www.youtube.com/watch?v=mk0XBWenod8&list=PLWz5rJ2EKKc8fyNmwKXYvA2CqxMhXqKXX", @@ -1781,7 +1681,6 @@ object FakeDataSource { }, { "id": "100", - "episodeId": "53", "title": "Google I/O", "content": "At I/O we released updates in Jetpack, Compose, Android Studio tooling, Large screens, Wear OS, Testing, and more! Get caught up on all the Android videos from I/O!", "url": "https://www.youtube.com/watch?v=D_mVOAXcrtc", @@ -1795,7 +1694,6 @@ object FakeDataSource { }, { "id": "101", - "episodeId": "53", "title": "Android Dev Summit", "content": "At Android Dev Summit we released updates on privacy and security, large screens, Android 12, Google Play & Games, Building across screens, Jetpack Compose, Modern Android Development and more. Check out all the videos from ADS!", "url": "https://www.youtube.com/watch?v=WZgR5Yf1iq8", @@ -1809,7 +1707,6 @@ object FakeDataSource { }, { "id": "102", - "episodeId": "52", "title": "Conveying state for Accessibility", "content": "In this episode of the Accessibility series, you can learn more about the StateDescription API, when to use stateDescription and contentDescription, and how to represent error states to the end user.", "url": "https://youtu.be/JvWM2PjLJls", @@ -1825,7 +1722,6 @@ object FakeDataSource { }, { "id": "103", - "episodeId": "52", "title": "Take your Gradle plugin to the next step", "content": "This third and last episode of the Gradle MAD Skills series teaches you how to get access to various build artifacts using the new Artifact API.", "url": "https://youtu.be/SB4QlngQQW0", @@ -1841,7 +1737,6 @@ object FakeDataSource { }, { "id": "104", - "episodeId": "52", "title": "How to write a Gradle plugin", "content": "In this second episode of the Gradle MAD Skills series, Murat explains how to write your own custom Gradle plugin.", "url": "https://youtu.be/LPzBVtwGxlo", @@ -1857,7 +1752,6 @@ object FakeDataSource { }, { "id": "105", - "episodeId": "52", "title": "Convert YUV to RGB for CameraX Image Analysis", "content": "Learn about a new feature in CameraX to convert YUV, the format that CameraX produces, to RGB used for image analysis capabilities available in TensorFlow Lite, for example. Read the blog post for more information about these formats and how to use the new conversion feature.", "url": "https://medium.com/androiddevelopers/convert-yuv-to-rgb-for-camerax-imageanalysis-6c627f3a0292", @@ -1874,7 +1768,6 @@ object FakeDataSource { }, { "id": "106", - "episodeId": "52", "title": "AppCompat, Activity, and Fragment to support multiple back stacks", "content": "The 1.4.0 release of these libraries brings stable support for multiple back stacks.", "url": "https://developer.android.com/jetpack/androidx/releases/appcompat#1.4.0", @@ -1889,7 +1782,6 @@ object FakeDataSource { }, { "id": "107", - "episodeId": "52", "title": "Emoji2 adds support for modern emojis", "content": "The 1.0 stable release of Emoji2 allows you to use modern emojis in your app.", "url": "https://developer.android.com/jetpack/androidx/releases/emoji2#1.0.0", @@ -1904,7 +1796,6 @@ object FakeDataSource { }, { "id": "108", - "episodeId": "52", "title": "Lifecycle introduces lifecycle-aware coroutine APIs", "content": "The new 2.4 release of Lifecycle introduces repeatOnLifecycle and flowWithLifecycle.", "url": "https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0", @@ -1919,7 +1810,6 @@ object FakeDataSource { }, { "id": "109", - "episodeId": "52", "title": "Paging release brings changes to LoadState", "content": "The new 3.1 release of Paging changes the behavior of LoadState.", "url": "https://developer.android.com/jetpack/androidx/releases/paging#3.1.0", @@ -1934,7 +1824,6 @@ object FakeDataSource { }, { "id": "110", - "episodeId": "52", "title": "Wear tiles released as 1.0 stable", "content": "The library that you use to build custom tiles for Wear OS devices is now stable.", "url": "https://developer.android.com/jetpack/androidx/releases/wear-tiles#1.0.0", @@ -1949,7 +1838,6 @@ object FakeDataSource { }, { "id": "111", - "episodeId": "52", "title": "About Custom Accessibility Actions", "content": "The accessibility series continues on with more information on how to create custom accessibility actions to make your apps more accessible. You can provide a custom action to the accessibility services and implement logic related to the action. For more information, check out the following episode!", "url": "https://youtu.be/wWDYIGk0Kdo", @@ -1965,7 +1853,6 @@ object FakeDataSource { }, { "id": "112", - "episodeId": "52", "title": "Improving App Startup: Lessons from the Facebook App", "content": "Improving app startup time is not a trivial task and requires a deep understanding of things that affect it. This year, the Android team and the Facebook app team have been working together on metrics and sharing approaches to improve app startup. Read more about the findings in this blog post.", "url": "https://android-developers.googleblog.com/2021/11/improving-app-startup-facebook-app.html", @@ -1979,7 +1866,6 @@ object FakeDataSource { }, { "id": "113", - "episodeId": "52", "title": "Gradle series kicks off", "content": "Murat introduces the Gradle series and everything you'll learn in it.", "url": "https://youtu.be/mk0XBWenod8", @@ -1995,7 +1881,6 @@ object FakeDataSource { }, { "id": "114", - "episodeId": "52", "title": "Intro to Gradle and AGP", "content": "In the first episode of the Gradle MAD Skills series, Murat explains how the Android build system works, and how to configure your build.", "url": "https://youtu.be/GjPS4xDMmQY", @@ -2011,7 +1896,6 @@ object FakeDataSource { }, { "id": "115", - "episodeId": "52", "title": "ADB Podcast episode 179 Hosts 3, Guests 0", "content": "Chet, Romain and Tor sit down to chat about the Android Developer Summit, and in particular all the new features arriving in Android Studio, along with a few other topics like Chet’s new jank stats library, the Android 12L release, and more.", "url": "https://adbackstage.libsyn.com/episode-178-hosts-3-guests-0", @@ -2029,7 +1913,6 @@ object FakeDataSource { }, { "id": "116", - "episodeId": "52", "title": "The problem with emojis and how emoji2 can help out", "content": "Meghan wrote about the new emoji2 library that just became stable.", "url": "https://medium.com/androiddevelopers/support-modern-emoji-99f6dea8e57f", @@ -2046,7 +1929,6 @@ object FakeDataSource { }, { "id": "117", - "episodeId": "52", "title": "Paging Q&A", "content": "In this live session, TJ and Dustin answered your questions in the usual live Q&A format.", "url": "https://youtu.be/8i6vrlbIVCc", @@ -2060,7 +1942,6 @@ object FakeDataSource { }, { "id": "118", - "episodeId": "52", "title": "Thanks for helping us reach 1M YouTube Subscribers", "content": "Thank you everyone for following the Now in Android series and everything the Android Developers YouTube channel has to offer. During the Android Developer Summit, our YouTube channel reached 1 million subscribers! Here’s a small video to thank you all.", "url": "https://youtu.be/-fJ6poHQrjM", @@ -2074,7 +1955,6 @@ object FakeDataSource { }, { "id": "119", - "episodeId": "52", "title": "Community tip on Paging", "content": "Tips for using the Paging library from the developer community", "url": "https://youtu.be/r5JgIyS3t3s", @@ -2088,7 +1968,6 @@ object FakeDataSource { }, { "id": "120", - "episodeId": "52", "title": "Transformations and customisations in the Paging Library", "content": "A demonstration of different operations that can be performed with Paging. Transformations like inserting separators, when to create a new pager, and customisation options for consuming PagingData.", "url": "https://youtu.be/ZARz0pjm5YM", @@ -2104,7 +1983,6 @@ object FakeDataSource { }, { "id": "121", - "episodeId": "52", "title": "New Compose for Wear OS codelab", "content": "In this codelab, you can learn how Wear OS can work with Compose, what Wear OS specific composables are available, and more!", "url": "https://developer.android.com/codelabs/compose-for-wear-os", @@ -2121,7 +1999,6 @@ object FakeDataSource { }, { "id": "122", - "episodeId": "52", "title": "Introducing Jetpack Media3", "content": "The first alpha version of this new library is now available. Media3 is a collection of support libraries for media playback, including ExoPlayer. The following article explains why the team created Media3, what it contains, and how it can simplify your app architecture.", "url": "https://developer.android.com/jetpack/androidx/releases/media3", @@ -2138,7 +2015,6 @@ object FakeDataSource { }, { "id": "123", - "episodeId": "50", "title": "Building apps which are private by design", "content": "Sara N-Marandi, product manager, and Yacine Rezgui, developer relations engineer, provided guidelines and best practices on how to build apps that are private by design, covered new privacy features in Android 12 and previewed upcoming Android concepts.", "url": "https://youtu.be/hBVwr2ErQCw", @@ -2152,7 +2028,6 @@ object FakeDataSource { }, { "id": "124", - "episodeId": "50", "title": "Memory Safety Tools", "content": "Serban Constantinescu, product manager, talked about the Memory Safety Tools that became available starting in Android 11 and have continued to evolve in Android 12. These tools can help address memory bugs and improve the quality and security of your application.", "url": "https://youtu.be/JqLcTFpXreg", @@ -2166,7 +2041,6 @@ object FakeDataSource { }, { "id": "125", - "episodeId": "50", "title": "Increasing User Transparency with Privacy Dashboard", "content": "Android is ever evolving in its quest to protect users’ privacy. In Android 12, the platform increases transparency by introducing Privacy Dashboard, which gives users a simple and clear timeline view of the apps that have accessed location, microphone and camera within the past 24 hours. ", "url": "https://medium.com/androiddevelopers/increasing-user-transparency-with-privacy-dashboard-23064f2d7ff6", @@ -2182,7 +2056,6 @@ object FakeDataSource { }, { "id": "126", - "episodeId": "50", "title": "The most unusual and interesting security issues addressed last year", "content": "Lilian Young, software engineer, presented a selection of the most unusual, intricate, and interesting security issues addressed in the last year. Developers and researchers are able to contribute to the security of the Android platform by submitting to the Android Vulnerability Rewards Program.", "url": "https://medium.com/androiddevelopers/now-in-android-50-ads-special-9934422f8dd1", @@ -2198,7 +2071,6 @@ object FakeDataSource { }, { "id": "127", - "episodeId": "50", "title": "New Data Safety section in the Play Console", "content": "The new Data safety section will give you a simple way to showcase your app’s overall safety. It gives you a place to give users deeper insight into your app’s privacy and security practices, and explain the data your app may collect and why — all before users install.", "url": "https://youtu.be/J7TM0Yy0aTQ", @@ -2213,7 +2085,6 @@ object FakeDataSource { }, { "id": "128", - "episodeId": "50", "title": "Building Android UIs for any screen size", "content": "Clara Bayarri, engineering manager and Daniel Jacobson, product manager, talked about the state of the ecosystem, focusing on new design guidance, APIs, and tools to help you make the most of your UI on different screen sizes.", "url": "https://youtu.be/ir3LztqbeRI", @@ -2227,7 +2098,6 @@ object FakeDataSource { }, { "id": "129", - "episodeId": "50", "title": "What's new for large screens & foldables", "content": "Emilie Roberts, Chrome OS developer advocate and Andrii Kulian, Android software engineer, introduced new features focused specifically on making apps look great on large screens, foldables, and Chrome OS. ", "url": "https://youtu.be/6-925K3hMHU", @@ -2241,7 +2111,6 @@ object FakeDataSource { }, { "id": "130", - "episodeId": "50", "title": "Enable great input support for all devices", "content": "Users expect seamless experiences when using keyboards, mice, and stylus. Emilie Roberts taught us how to handle common keyboard and mouse input events and how to get started with more advanced support like keyboard shortcuts, low-latency styluses, MIDI, and more.", "url": "https://youtu.be/piLEZYTc_4g", @@ -2255,7 +2124,6 @@ object FakeDataSource { }, { "id": "131", - "episodeId": "50", "title": "Best practices for video apps on foldable devices", "content": "Francesco Romano, developer advocate, and Will Chan, product manager at Zoom explored new user experiences made possible by the foldable form factor, focusing on video conferencing and media applications. ", "url": "https://youtu.be/DBAek_P0nEw", @@ -2270,7 +2138,6 @@ object FakeDataSource { }, { "id": "132", - "episodeId": "50", "title": "Design beautiful apps on foldables and large screens", "content": "Liam Spradlin, design advocate, and Jonathan Koren, developer relations engineer, talked about how to design and test Android applications that look and feel great across device types and screen sizes, from tablets to foldables to Chrome OS.", "url": "https://youtu.be/DJeJIJKOUbI", @@ -2284,7 +2151,6 @@ object FakeDataSource { }, { "id": "133", - "episodeId": "50", "title": "12L and new Android APIs and tools for large screens", "content": "Dave Burke, vice president of engineering, wrote a post covering the developer preview of 12L, an upcoming feature drop that makes Android 12 even better on large screens. ", "url": "https://android-developers.googleblog.com/2021/10/12L-preview-large-screens.html", @@ -2299,7 +2165,6 @@ object FakeDataSource { }, { "id": "134", - "episodeId": "50", "title": "New features in ML Kit: Text Recognition V2 & Pose Detections", "content": "Zongmin Sun, software engineer, and Valentin Bazarevsky, MediaPipe Engineer, talked about Text Recognition V2 & Pose Detection, recently-released features in ML Kit. ", "url": "https://youtu.be/9EKQ0UC04S8", @@ -2314,7 +2179,6 @@ object FakeDataSource { }, { "id": "135", - "episodeId": "50", "title": "How to retain users with Android backup and restore", "content": "In this talk, Martin Millmore, engineering manager, and Ruslan Tkhakokhov, software engineer, explored the benefits of transferring users’ data to a new device, using Backup and Restore to achieve that in a simple and secure way.", "url": "https://youtu.be/bg2drEhz1_s", @@ -2328,7 +2192,6 @@ object FakeDataSource { }, { "id": "136", - "episodeId": "50", "title": "Compatibility changes in Android 12", "content": "Developer relations engineers Kseniia Shumelchyk and Slava Panasenko talked about new Android 12 features and changes. They shared tools and techniques to ensure that apps are compatible with the next Android release and users can take advantage of new features, along with app developer success stories.", "url": "https://youtu.be/fCMJmV6nqGo", @@ -2342,7 +2205,6 @@ object FakeDataSource { }, { "id": "137", - "episodeId": "50", "title": "Building great experiences for Novice Internet Users", "content": "Learn the principles to help craft great experiences for the novice Internet user segment from Mrinal Sharma, UX manager, and Amrit Sanjeev, developer relations engineer. They highlight the gap between nascent and tech savvy user segments and suggest strategies in areas to improve the overall user experience. Factors like low functional literacy, being multilingual by default, being less digitally confident, and having no prior internet experience requires that we rethink the way we build apps for these users.", "url": "https://youtu.be/Sf_TauUY4LE", @@ -2356,7 +2218,6 @@ object FakeDataSource { }, { "id": "138", - "episodeId": "49", "title": "Android Basics in Kotlin course 🧑‍💻", "content": "Android Basics in Kotlin teaches people with no programming experience how to build simple Android apps. Since the first learning units were released in 2020, over 100,000 beginners have completed it! Today, we’re excited to share that the final unit has been released, and the full Android Basics in Kotlin course is now available.", "url": "https://android-developers.googleblog.com/2021/10/announcing-android-basics-in-kotlin.html", @@ -2372,7 +2233,6 @@ object FakeDataSource { }, { "id": "139", - "episodeId": "49", "title": "WorkManager 2.7 adds setExpedited API to help with Foreground Service restrictions", "content": "As the most outstanding release this time, WorkManager 2.7 was promoted to stable. This new version introduces a new setExpedited API to help with Foreground Service restrictions in Android 12.", "url": "https://developer.android.com/reference/android/app/job/JobInfo.Builder#setExpedited(boolean)", @@ -2386,7 +2246,6 @@ object FakeDataSource { }, { "id": "140", - "episodeId": "49", "title": "Updated Widget docs", "content": "Widgets can make a huge impact on your user’s home screen! We updated the App Widgets documentation with the recent changes in the latest OS versions. New pages about how to create a simple widget, an advanced widget, and how to provide flexible widget layouts.", "url": "https://developer.android.com/guide/topics/appwidgets", @@ -2400,7 +2259,6 @@ object FakeDataSource { }, { "id": "141", - "episodeId": "49", "title": "Extend AGP by creating your own plugins", "content": "The Android Gradle Plugin (AGP) contains extension points for plugins to control build inputs and extend its functionality. Starting in version 7.0, AGP has a set of official, stable APIs that you can rely on. We also have a new documentation page that walks you through this and explains how to create your own plugins.", "url": "https://developer.android.com/studio/build/extend-agp", @@ -2415,7 +2273,6 @@ object FakeDataSource { }, { "id": "142", - "episodeId": "49", "title": "Revamped Compose Basics Codelab", "content": "If you’re planning to start learning Jetpack Compose, our modern toolkit for building native Android UI, it’s your lucky day! We just revamped the Basics Jetpack Compose codelab to help you learn the core concepts of Compose, and only with this, you’ll see how much it improves building Android UIs.", "url": "https://developer.android.com/codelabs/jetpack-compose-basics", @@ -2429,7 +2286,6 @@ object FakeDataSource { }, { "id": "143", - "episodeId": "49", "title": "Start an activity for a result from a Composable", "content": "We expanded the Compose and other libraries page to cover how to start an activity for result, request runtime permissions, and handle the system back button directly from your composables.", "url": "https://developer.android.com/jetpack/compose/libraries", @@ -2443,7 +2299,6 @@ object FakeDataSource { }, { "id": "144", - "episodeId": "49", "title": "Material components in Compose", "content": "We added a new Material Components and layouts page that goes over the different Material components in Compose such as backdrop, app bars, modal drawers, etc.!", "url": "https://developer.android.com/jetpack/compose/layouts/material", @@ -2458,7 +2313,6 @@ object FakeDataSource { }, { "id": "145", - "episodeId": "49", "title": "How to implement a custom design system", "content": "How to implement a custom design system in Compose", "url": "https://developer.android.com/jetpack/compose/themes/custom", @@ -2473,7 +2327,6 @@ object FakeDataSource { }, { "id": "146", - "episodeId": "49", "title": "The anatomy of a theme", "content": "Understanding the anatomy of a Compose theme", "url": "https://developer.android.com/jetpack/compose/themes/anatomy", @@ -2487,7 +2340,6 @@ object FakeDataSource { }, { "id": "147", - "episodeId": "49", "title": "Paging 📑 Displaying data and its loading state", "content": "In the third episode of the Paging video series, TJ adds a local cache to pull from and refresh only when necessary, making use of Room . The local cache acts as the single source of truth for paging data.", "url": "https://www.youtube.com/watch?v=OHH_FPbrjtA", @@ -2504,7 +2356,6 @@ object FakeDataSource { }, { "id": "148", - "episodeId": "49", "title": "Data safety in the Play Console 🔒", "content": "Google Play is rolling out the Data safety form in the Google Play Console. With the new Data safety section, developers will now have a transparent way to show users if and how they collect, share, and protect user data, before users install an app.\nRead the blog post to learn more about how to submit your app information in Play Console, how to get prepared, and what your users will see in your app’s store listing starting February.", "url": "https://android-developers.googleblog.com/2021/10/launching-data-safety-in-play-console.html", @@ -2521,7 +2372,6 @@ object FakeDataSource { }, { "id": "149", - "episodeId": "49", "title": "Honor every photo - How cameras capture images", "content": "Episode 177: Honor every photon. In this episode, Chet, Roman, and Tor have a chat with Bart Wronski from the Google Research team, discussing the camera pipeline that powers the Pixel phones. How cameras capture images, how the algorithms responsible for Pixel’s beautiful images, HDR+ or Night Sight mode works, and more!", "url": "https://adbackstage.libsyn.com/episode-177-honor-every-photon", @@ -2537,7 +2387,6 @@ object FakeDataSource { }, { "id": "150", - "episodeId": "49", "title": "Accessibility series 🌐 - Touch targets", "content": "The accessibility series continues on with more information on how to follow basic accessibility principles to make sure that your app can be used by as many users as possible.\nIn general, you should ensure that interactive elements have a width and height of at least 48dp! In the touch targets episode, you’ll learn about a few ways in which you can make this happen.", "url": "https://www.youtube.com/watch?v=Dqqbe8IFBA4", @@ -2553,7 +2402,6 @@ object FakeDataSource { }, { "id": "151", - "episodeId": "49", "title": "Using the CameraX Exposure Compensation API", "content": "This blog post by Wenhung Teng talks about how to use the CameraX Exposure Compensation that makes it much simpler to quickly take images with exceptional quality.", "url": "https://medium.com/androiddevelopers/using-camerax-exposure-compensation-api-11fd75785bf", @@ -2569,7 +2417,6 @@ object FakeDataSource { }, { "id": "152", - "episodeId": "49", "title": "Compose for Wear OS in Developer preview ⌚", "content": "We’re bringing the best of Compose to Wear OS as well, with built-in support for Material You to help you create beautiful apps with less code. Read the following article to review the main composables for Wear OS we’ve built and point you towards resources to get started using them.", "url": "https://android-developers.googleblog.com/2021/10/compose-for-wear-os-now-in-developer.html", @@ -2586,7 +2433,6 @@ object FakeDataSource { }, { "id": "153", - "episodeId": "49", "title": "Paging 📑 How to fetch data and bind the PagingData to the UI", "content": "The series on Paging continues on with more content! In the second episode, TJ shows how to fetch data and bind the PagingData to the UI, including headers and footers.", "url": "https://www.youtube.com/watch?v=C0H54K63Lww", @@ -2602,7 +2448,6 @@ object FakeDataSource { }, { "id": "154", - "episodeId": "49", "title": "Room adds support for Kotlin Symbol Processing", "content": "Yigit Boyar wrote the story about how Room added support for Kotlin Symbol Processing (KSP). Spoiler: it wasn’t easy, but it was definitely worth it.", "url": "https://medium.com/androiddevelopers/room-kotlin-symbol-processing-24808528a28e", @@ -2618,7 +2463,6 @@ object FakeDataSource { }, { "id": "155", - "episodeId": "49", "title": "Apply special effects to images with the CameraX Extensions API", "content": "Have you ever wanted to apply special effects such as HDR or Night mode when taking pictures from your app? CameraX is here to help you! In this article by Charcoal Chen, learn how to do that using the new ExtensionsManager available in the camera-extensions Jetpack library. ", "url": "https://medium.com/androiddevelopers/apply-special-effects-to-images-with-the-camerax-extensions-api-d1a169b803d3", @@ -2634,7 +2478,6 @@ object FakeDataSource { }, { "id": "156", - "episodeId": "49", "title": "Wear OS Jetpack libraries now in stable", "content": "The Wear OS Jetpack libraries are now in stable.", "url": "https://android-developers.googleblog.com/2021/09/wear-os-jetpack-libraries-now-in-stable.html", @@ -2651,7 +2494,6 @@ object FakeDataSource { }, { "id": "157", - "episodeId": "48", "title": "Android Dev Summit returns on October 27-28, 2021! 📆", "content": "Join us October 27–28 for Android Dev Summit 2021! The show kicks off at 10 AM PST on October 27 with The Android Show: a technical keynote where you’ll hear all the latest developer news and updates. From there, we have over 30 sessions on a range of technical Android development topics, and we’ll be answering your #AskAndroid questions live.", "url": "https://developer.android.com/dev-summit", @@ -2665,7 +2507,6 @@ object FakeDataSource { }, { "id": "158", - "episodeId": "48", "title": "Android 12 is live in AOSP! 🤖", "content": "We released Android 12 and pushed it to the Android Open Source Project (AOSP). It will be coming to devices later on this year. Thank you for your feedback during the beta.\nAndroid 12 introduces a new design language called Material You along with redesigned widgets, notification UI updates, stretch overscroll, and app launch splash screens. We reduced the CPU time used by core system services, added performance class device capabilities, made ML accelerator drivers updatable outside of platform releases, and prevented apps from launching foreground services from the background and using notification trampolines to improve performance. The new Privacy Dashboard, approximate location, microphone and camera indicators/toggles, and nearby device permissions give users more insight into and control over privacy. We improved the user experience with a unified API for rich content insertion, compatible media transcoding, easier blurs and effects, AVIF image support, enhanced haptics, new camera effects/capabilities, improved native crash debugging, support for rounded screen corners, Play as you download, and Game Mode APIs.", "url": "https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html", @@ -2681,7 +2522,6 @@ object FakeDataSource { }, { "id": "159", - "episodeId": "48", "title": "Improved Google Play Console user management 🧑‍💼", "content": "The user and permission tools in Play Console have a new, decluttered interface and new team management features, making it easier to make sure every team member has the right set of permissions to fulfill their responsibilities without overexposing unrelated business data.\nWe’ve rewritten permission names and descriptions, clarified differentiation between account and app-level permissions, added new search, filtering, and batch-editing capabilities, and added the ability to export this information to a CSV file. In addition, Play Console users can request access to actions with a justification, and we’ve introduced permission groups to make it easier to assign multiple permissions at once to users that share the same or similar roles.", "url": "https://android-developers.googleblog.com/2021/09/improved-google-play-console-user.html", @@ -2697,7 +2537,6 @@ object FakeDataSource { }, { "id": "160", - "episodeId": "48", "title": "Making Permissions auto-reset available to billions more devices 🔐", "content": "Android 11 introduced permission auto-reset, automatically resetting an app’s runtime permissions when it isn’t used for a few months. In December 2021, we are starting to roll this feature out to devices with Google Play services running Android 6.0 (API level 23) or higher for apps targeting Android 11 (API level 30) or higher. Users can manually enable permission auto-reset for apps targeting API levels 23 to 29.\nSome apps and permissions are automatically exempted from revocation, like active Device Administrator apps used by enterprises, and permissions fixed by enterprise policy. If your app is expected to work primarily in the background without user interaction, you can ask the user to prevent the system from resetting your app’s permissions.", "url": "https://android-developers.googleblog.com/2021/09/making-permissions-auto-reset-available.html", @@ -2713,7 +2552,6 @@ object FakeDataSource { }, { "id": "161", - "episodeId": "47", "title": "Migrating from Dagger to Hilt", "content": "While you will eventually want to migrate all your existing Dagger modules over to Hilt’s built in components, you can start by migrating application-wide components to Hilt’s singleton component. This episode explains how.", "url": "https://www.youtube.com/watch?v=Xt1_3Nq4lD0&t=15s", @@ -2729,7 +2567,6 @@ object FakeDataSource { }, { "id": "162", - "episodeId": "47", "title": "ADB Podcast Episode 175: Creating delightful user experiences with Lottie animations", "content": "In this episode, Chet, Romain and Tor have a chat with Gabriel Peal from Tonal, well known for his contributions to the Android community on projects such as Mavericks and Lottie. They talked about Lottie and how it helps designers and developers deliver more delightful user experiences by taking complex animations designed in specialized authoring tools such as After Effects, and rendering them efficiently on mobile devices. They also explored the challenges of designing and implementing a rendering engine such as Lottie.", "url": "http://adbackstage.libsyn.com/episode-175-lottie", @@ -2745,7 +2582,6 @@ object FakeDataSource { }, { "id": "163", - "episodeId": "47", "title": "Hilt extensions", "content": "This episode explains how to write your own Hilt Extensions. Hilt Extensions allow you to extend Hilt support to new libraries. Extensions can be created for common patterns in projects, to support non-standard member injection, mirroring bindings, and more.", "url": "https://medium.com/androiddevelopers/hilt-extensions-in-the-mad-skills-series-f2ed6fcba5fe", @@ -2761,7 +2597,6 @@ object FakeDataSource { }, { "id": "164", - "episodeId": "47", "title": "Labeling images for Accessibility", "content": "This Accessibilities series episode covers labeling images for accessibility, such as content descriptions for ImageViews and ImageButtons.", "url": "https://youtu.be/O2DeSITnzFk", @@ -2777,7 +2612,6 @@ object FakeDataSource { }, { "id": "165", - "episodeId": "47", "title": "ADB Podcast Episode 174: Compose in Android Studio", "content": "In this episode, Tor and Nick are joined by Chris Sinco, Diego Perez and Nicolas Roard to discuss the features added to Android Studio for Jetpack Compose. Tune in as they discuss the Compose preview, interactive preview, animation inspector, and additions to the Layout inspector along with their approach to creating tooling to support Compose’s code-centric system.", "url": "http://adbackstage.libsyn.com/episode-174-compose-tooling", @@ -2794,7 +2628,6 @@ object FakeDataSource { }, { "id": "166", - "episodeId": "47", "title": "Hilt under the hood", "content": "This episode dives into how the Hilt annotation processors generate code, and how the Hilt Gradle plugin works behind the scenes to improve the overall experience when using Hilt with Gradle.", "url": "https://medium.com/androiddevelopers/mad-skills-series-hilt-under-the-hood-9d89ee227059", @@ -2810,7 +2643,6 @@ object FakeDataSource { }, { "id": "167", - "episodeId": "47", "title": "Trackr comes to the Big Screen", "content": "A blog post on Trackr, a sample task management app where we showcase Modern Android Development best practices. This post takes you through how applying Material Design and responsive patterns produced a more refined and intuitive user experience on large screen devices.", "url": "https://medium.com/androiddevelopers/trackr-comes-to-the-big-screen-9f13c6f927bf", @@ -2826,7 +2658,6 @@ object FakeDataSource { }, { "id": "168", - "episodeId": "47", "title": "Accessibility services and the Android Accessibility model", "content": "This Accessibilities series episode covers accessibility services like TalkBack, Switch Access and Voice Access and how they help users interact with your apps. Android’s accessibility framework allows you to write one app and the framework takes care of providing the information needed by different accessibility services.", "url": "https://youtu.be/LxKat_m7mHk", @@ -2842,7 +2673,6 @@ object FakeDataSource { }, { "id": "169", - "episodeId": "47", "title": "New Accessibility Pathway", "content": "Want even more accessibility? You are in luck, check out this entire new learning pathway aimed at teaching you how to make your app more accessible.", "url": "https://developer.android.com/courses/pathways/make-your-android-app-accessible", @@ -2856,7 +2686,6 @@ object FakeDataSource { }, { "id": "170", - "episodeId": "47", "title": "AndroidX Activity Library 1.4.0-alpha01 released", "content": "The AndroidX ComponentActivity now implements the MenuHost interface which allows any component to add menu items to the ActionBar by adding a MenuProvider instance to the activity.", "url": "https://developer.android.com/jetpack/androidx/releases/activity#1.4.0-alpha01", @@ -2870,7 +2699,6 @@ object FakeDataSource { }, { "id": "171", - "episodeId": "45", "title": "DataStore released into stable", "content": "Datastore was released, providing a data storage solution that allows you to store key-value pairs or typed objects with protocol buffers.", "url": "https://developer.android.com/jetpack/androidx/releases/datastore#1.0.0", @@ -2884,7 +2712,6 @@ object FakeDataSource { }, { "id": "172", - "episodeId": "44", "title": "Jetpack Compose 1.0 stable is released", "content": "Jetpack Compose, Android’s modern, native UI toolkit is now stable and ready for you to adopt in production. It interoperates with your existing app, integrates with existing Jetpack libraries, implements Material Design with straightforward theming, supports lists with Lazy components using minimal boilerplate, and has a powerful, extensible animation system.", "url": "https://android-developers.googleblog.com/2021/07/jetpack-compose-announcement.html", @@ -2900,7 +2727,6 @@ object FakeDataSource { }, { "id": "173", - "episodeId": "44", "title": "Android Studio Artic Fox stable is released", "content": "Android Studio Arctic Fox is now available in the stable release channel. Arctic Fox brings Jetpack Compose to life with Compose Preview, Deploy Preview, Compose support in the Layout Inspector, and Live Editing of literals. Compose Preview works with the @Preview annotation to let you instantly see the impact of changes across multiple themes, screen sizes, font sizes, and more. Deploy Preview deploys snippets of your Compose code to a device or emulator for quick testing. Layout inspector now works with apps written fully in Compose as well as apps that have Compose alongside Views, allowing you to explore your layouts and troubleshoot. With Live Edit of literals, you can edit literals such as strings, numbers, booleans, etc. and see the immediate results change in previews, the emulator, or on a physical device — all without having to compile.\n", "url": "https://android-developers.googleblog.com/2021/07/android-studio-arctic-fox-202031-stable.html", @@ -2917,7 +2743,6 @@ object FakeDataSource { }, { "id": "174", - "episodeId": "44", "title": "User control, privacy, security, and safety", "content": "Play announced new updates to bolster user control, privacy, and security. The post covered advertising ID updates, including zeroing out the advertising ID when users opt out of interest-based advertising or ads personalization, the developer preview of the app set ID, enhanced protection for kids, and policy updates around dormant accounts and users of the AccessibilityService API.", "url": "https://android-developers.googleblog.com/2021/07/announcing-policy-updates-to-bolster.html", @@ -2933,7 +2758,6 @@ object FakeDataSource { }, { "id": "175", - "episodeId": "44", "title": "Identify performance bottlenecks using system trace", "content": "System trace profiling within Android Studio with a detailed walkthrough of app startup performance.", "url": "https://www.youtube.com/watch?v=aUrqx9AnDUg", @@ -2950,7 +2774,6 @@ object FakeDataSource { }, { "id": "176", - "episodeId": "44", "title": "Testing in Compose", "content": "ADB released episode #171, part of our continuing series on Jetpack Compose. In this episode, Nick and Romain are joined by Filip Pavlis, Jelle Fresen & Jose Alcérreca to talk about Testing in Compose. They discuss how Compose’s testing APIs were developed hand-in-hand with the UI toolkit, making them more deterministic and opening up new possibilities like manipulating time. They go on to discuss the semantics tree, interop testing, screenshot testing and the possibilities for host-side testing.", "url": "https://adbackstage.libsyn.com/episode-171-compose-testing", @@ -2967,7 +2790,6 @@ object FakeDataSource { }, { "id": "177", - "episodeId": "42", "title": "DataStore reached release candidate status", "content": "DataStore has reached release candidate status meaning the 1.0 stable release is right around the corner!", "url": "https://developer.android.com/topic/libraries/architecture/datastore", @@ -2981,7 +2803,6 @@ object FakeDataSource { }, { "id": "178", - "episodeId": "42", "title": "Scope Storage Myths", "content": "Apps will be required to update their targetSdkVersion to API 30 in the second half of the year. That means your app will be required to work with Scoped Storage. In this blog post, Nicole Borrelli busts some Scope storage myths in a Q&A format.", "url": "https://medium.com/androiddevelopers/scope-storage-myths-ca6a97d7ff37", @@ -2998,7 +2819,6 @@ object FakeDataSource { }, { "id": "179", - "episodeId": "41", "title": "Navigation with Multiple back stacks", "content": "As part of the rercommended Material pattern for bottom-navigation, the Jetpack Navigation library makes it easy to implement navigation with multiple back-stacks", "url": "https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f", @@ -3014,7 +2834,6 @@ object FakeDataSource { }, { "id": "180", - "episodeId": "41", "title": "Build sophisticated search features with AppSearch", "content": "AppSearch is an on-device search library which provides high performance and feature-rich full-text search functionality. Learn how to use the new Jetpack AppSearch library for doing high-performance on-device full text searches.", "url": "https://android-developers.googleblog.com/2021/06/sophisticated-search-with-appsearch-in-jetpack.html", @@ -3031,7 +2850,6 @@ object FakeDataSource { }, { "id": "181", - "episodeId": "41", "title": "ADB Podcast Episode 167: Jetpack Compose Layout", "content": "In this second episode of our mini-series on Jetpack Compose (AD/BC), Nick and Romain are joined by Anastasia Soboleva, George Mount and Mihai Popa to talk about Compose’s layout system. They explain how the Compose layout model works and its benefits, introduce common layout composables, discuss how writing your own layout is far simpler than Views, and how you can even animate layout.", "url": "https://adbackstage.libsyn.com/episode-167-jetpack-compose-layout", @@ -3047,7 +2865,6 @@ object FakeDataSource { }, { "id": "182", - "episodeId": "41", "title": "Create an application CoroutineScope using Hilt", "content": "Learn how to create an applicatioon-scoped CoroutineScope using Hilt, and how to inject it as a dependency.", "url": "https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528", @@ -3063,7 +2880,6 @@ object FakeDataSource { }, { "id": "183", - "episodeId": "41", "title": "Android 12 Beta 2 Update", "content": "The second Beta of Android 12 has just been released for you to try. Beta 2 adds new privacy features like the Privacy Dashboard and continues our work of refining the release.", "url": "https://android-developers.googleblog.com/2021/06/android-12-beta-2-update.html", @@ -3079,7 +2895,6 @@ object FakeDataSource { }, { "id": "184", - "episodeId": "41", "title": "Top 3 things in Android 12 | Android @ Google I/O '21", "content": "Did you miss the latest in Android 12 at Google I/O 2021? Android Software Engineer Chet Haase will recap the top three themes in Android 12 from this year’s Google I/O!", "url": "https://www.youtube.com/watch?v=tvf1wmD5H0M", @@ -3095,7 +2910,6 @@ object FakeDataSource { }, { "id": "185", - "episodeId": "41", "title": "ADB Podcast Episode 166: Security Deposit", "content": "In this episode, Chad and Jeff from the Android Security team join Tor and Romain to talk about… security. They explain what the platform does to help preserve user trust and device integrity, why it sometimes means restricting existing APIs, and touch on what apps can do or should worry about.", "url": "http://adbackstage.libsyn.com/episode-166-security-deposit", @@ -3111,7 +2925,6 @@ object FakeDataSource { }, { "id": "186", - "episodeId": "41", "title": "Multiple Back Stacks", "content": "A deep dive into multiple back stacks and some of the work it took to make this feature happen in Fragments and Navigation", "url": "https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134", @@ -3127,7 +2940,6 @@ object FakeDataSource { }, { "id": "187", - "episodeId": "41", "title": "Building across devices | Android @ Google I/O '21", "content": "Did you miss the latest in Building across screens at Google I/O 2021? Product Manager Diana Wong will recap the top three announcements from this year’s Google I/O!", "url": "https://www.youtube.com/watch?v=O5oRiIUk_F4", @@ -3143,7 +2955,6 @@ object FakeDataSource { }, { "id": "188", - "episodeId": "41", "title": "Navigation in Feature Modules", "content": "Feature modules delivered with Play Feature delivery at not downloadedd at install time, but only when the app requestss them. Learn how to use the dynamic features navigation library to include the graph from the feature module.", "url": "https://medium.com/androiddevelopers/navigation-in-feature-modules-322ac3d79334", @@ -3159,7 +2970,6 @@ object FakeDataSource { }, { "id": "189", - "episodeId": "41", "title": "ADB Podcast Episode 165: Material Witnesses", "content": "In this episode, Chet and Romain chattedd with Hunter and Nick from the Material Design team about recent additions and improvements to the Material Design Component libraries: transitions, motion theming, Compose, large screens support and guidance, etc.", "url": "http://adbackstage.libsyn.com/episode-165-material-witnesses", @@ -3175,7 +2985,6 @@ object FakeDataSource { }, { "id": "190", - "episodeId": "41", "title": "Grow Your Indie Game with Help From Google Play", "content": "Google Play is opening submissions for two of our annual developer programs - the Indie Games Accelerator and the Indie Games Festival. These programs are designed to help small games studios grow on Google Play, no matter what stage they are in", "url": "https://developers.googleblog.com/2021/06/grow-your-indie-game-with-help-from-google-play.html", @@ -3192,7 +3001,6 @@ object FakeDataSource { }, { "id": "191", - "episodeId": "41", "title": "Untrusted Touch Events in Android", "content": "Android 12 prevents touch events from being deliverred if these touches first pass through a window from a different app to ensure users can see what they are interacting with. Learn about alternatives, to see if your app will be affected and how you can test to see if your app will be impacted.", "url": "https://medium.com/androiddevelopers/untrusted-touch-events-2c0e0b9c374c", @@ -3208,7 +3016,6 @@ object FakeDataSource { }, { "id": "192", - "episodeId": "41", "title": "Android @ Google I/O: 3 things to know in Modern Android Development", "content": "This year’s Google I/O brought lots of updates for Modern Android Development. Learn about the top 3 things you should know.", "url": "https://android-developers.googleblog.com/2021/05/mad-spotlight.html", diff --git a/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkEpisode.kt b/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkEpisode.kt deleted file mode 100644 index a2420b7d8..000000000 --- a/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkEpisode.kt +++ /dev/null @@ -1,52 +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.network.model - -import com.google.samples.apps.nowinandroid.core.model.data.Episode -import com.google.samples.apps.nowinandroid.core.network.model.util.InstantSerializer -import kotlinx.datetime.Instant -import kotlinx.serialization.Serializable - -/** - * Network representation of [Episode] when fetched from /episodes - */ -@Serializable -data class NetworkEpisode( - val id: String, - val name: String, - @Serializable(InstantSerializer::class) - val publishDate: Instant, - val alternateVideo: String?, - val alternateAudio: String?, - val newsResources: List = listOf(), - val authors: List = listOf(), -) - -/** - * Network representation of [Episode] when fetched from /episodes/{id} - */ -@Serializable -data class NetworkEpisodeExpanded( - val id: String, - val name: String, - @Serializable(InstantSerializer::class) - val publishDate: Instant, - val alternateVideo: String, - val alternateAudio: String, - val newsResources: List = listOf(), - val authors: List = listOf(), -) diff --git a/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt b/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt index 7a5242e41..15df55c35 100644 --- a/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt +++ b/core/network/src/main/java/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt @@ -29,7 +29,6 @@ import kotlinx.serialization.Serializable @Serializable data class NetworkNewsResource( val id: String, - val episodeId: String, val title: String, val content: String, val url: String, @@ -48,7 +47,6 @@ data class NetworkNewsResource( @Serializable data class NetworkNewsResourceExpanded( val id: String, - val episodeId: String, val title: String, val content: String, val url: String, diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt index 0b765bbc1..cdaa3e1b7 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt @@ -72,7 +72,7 @@ import kotlinx.datetime.Instant import kotlinx.datetime.toJavaInstant /** - * [NewsResource] card used on the following screens: For You, Episodes, Saved + * [NewsResource] card used on the following screens: For You, Saved */ @OptIn(ExperimentalMaterial3Api::class) diff --git a/feature/author/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/author/AuthorScreenTest.kt b/feature/author/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/author/AuthorScreenTest.kt index 070226123..8c515e78c 100644 --- a/feature/author/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/author/AuthorScreenTest.kt +++ b/feature/author/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/author/AuthorScreenTest.kt @@ -186,7 +186,6 @@ private val testAuthors = listOf( private val sampleNewsResources = listOf( NewsResource( id = "1", - episodeId = "52", title = "Thanks for helping us reach 1M YouTube Subscribers", content = "Thank you everyone for following the Now in Android series and everything the " + "Android Developers YouTube channel has to offer. During the Android Developer " + diff --git a/feature/author/src/test/java/com/google/samples/apps/nowinandroid/feature/author/AuthorViewModelTest.kt b/feature/author/src/test/java/com/google/samples/apps/nowinandroid/feature/author/AuthorViewModelTest.kt index c5a803b7d..0bf563157 100644 --- a/feature/author/src/test/java/com/google/samples/apps/nowinandroid/feature/author/AuthorViewModelTest.kt +++ b/feature/author/src/test/java/com/google/samples/apps/nowinandroid/feature/author/AuthorViewModelTest.kt @@ -284,7 +284,6 @@ private val testOutputAuthors = listOf( private val sampleNewsResources = listOf( NewsResource( id = "1", - episodeId = "52", title = "Thanks for helping us reach 1M YouTube Subscribers", content = "Thank you everyone for following the Now in Android series and everything the " + "Android Developers YouTube channel has to offer. During the Android Developer " + diff --git a/feature/foryou/src/main/res/values/strings.xml b/feature/foryou/src/main/res/values/strings.xml index 7ed3683d9..dfc451e53 100644 --- a/feature/foryou/src/main/res/values/strings.xml +++ b/feature/foryou/src/main/res/values/strings.xml @@ -16,7 +16,6 @@ --> For you - Episodes Done Loading for you… Navigate up diff --git a/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt index aadaa962b..558b18056 100644 --- a/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt +++ b/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt @@ -1428,7 +1428,6 @@ private val sampleTopics = listOf( private val sampleNewsResources = listOf( NewsResource( id = "1", - episodeId = "52", title = "Thanks for helping us reach 1M YouTube Subscribers", content = "Thank you everyone for following the Now in Android series and everything the " + "Android Developers YouTube channel has to offer. During the Android Developer " + @@ -1461,7 +1460,6 @@ private val sampleNewsResources = listOf( ), NewsResource( id = "2", - episodeId = "52", title = "Transformations and customisations in the Paging Library", content = "A demonstration of different operations that can be performed with Paging. " + "Transformations like inserting separators, when to create a new pager, and " + @@ -1493,7 +1491,6 @@ private val sampleNewsResources = listOf( ), NewsResource( id = "3", - episodeId = "52", title = "Community tip on Paging", content = "Tips for using the Paging library from the developer community", url = "https://youtu.be/r5JgIyS3t3s", diff --git a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt index 855d753b9..9d0dbe3e8 100644 --- a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt +++ b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt @@ -171,7 +171,6 @@ private val testTopics = listOf( private val sampleNewsResources = listOf( NewsResource( id = "1", - episodeId = "52", title = "Thanks for helping us reach 1M YouTube Subscribers", content = "Thank you everyone for following the Now in Android series and everything the " + "Android Developers YouTube channel has to offer. During the Android Developer " + diff --git a/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt b/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt index e29b33054..684ce2323 100644 --- a/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt +++ b/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt @@ -233,7 +233,6 @@ private val testOutputTopics = listOf( private val sampleNewsResources = listOf( NewsResource( id = "1", - episodeId = "52", title = "Thanks for helping us reach 1M YouTube Subscribers", content = "Thank you everyone for following the Now in Android series and everything the " + "Android Developers YouTube channel has to offer. During the Android Developer " + From 303d08dd28f6eb3b5e951925fcdbae624fdc7688 Mon Sep 17 00:00:00 2001 From: Milosz Moczkowski Date: Fri, 23 Sep 2022 16:07:18 +0200 Subject: [PATCH 2/3] Spotless improvements --- .../apps/nowinandroid/core/database/DatabaseMigrations.kt | 4 ++-- .../samples/apps/nowinandroid/core/database/DatabaseModule.kt | 1 - .../nowinandroid/core/database/model/NewsResourceEntity.kt | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt index 7e80f023c..c8ad557c9 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt @@ -43,8 +43,8 @@ object DatabaseMigrations { ) @DeleteTable.Entries( DeleteTable( - tableName = "episodes_authors" - ), + tableName = "episodes_authors" + ), DeleteTable( tableName = "episodes" ) diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt index ca28bef1d..7d4dd67c8 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt @@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.core.database import android.content.Context import androidx.room.Room -import androidx.room.migration.Migration import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt index 690f1fe44..9d33a04d5 100644 --- a/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt +++ b/core/database/src/main/java/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt @@ -18,8 +18,6 @@ package com.google.samples.apps.nowinandroid.core.database.model import androidx.room.ColumnInfo import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.Index import androidx.room.PrimaryKey import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType From 5de3f6156b3252938242cac45af3068e97b4f666 Mon Sep 17 00:00:00 2001 From: Milosz Moczkowski Date: Fri, 23 Sep 2022 16:09:26 +0200 Subject: [PATCH 3/3] Spotless improvements --- .../core/data/repository/OfflineFirstNewsRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt index bf605b9e2..e3f05a455 100644 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt @@ -34,9 +34,9 @@ import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import javax.inject.Inject /** * Disk storage backed implementation of the [NewsRepository].