- Add Repository and Dao for recent searches - Add recent searches body in the Search UI - Add relevant testsrecent_search
parent
55483e82df
commit
63cd8901fb
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.data.model
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
|
||||||
|
data class RecentSearchQuery(
|
||||||
|
val query: String,
|
||||||
|
val queriedDate: Instant = Clock.System.now(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun RecentSearchQueryEntity.asExternalModel() = RecentSearchQuery(
|
||||||
|
query = query,
|
||||||
|
queriedDate = queriedDate,
|
||||||
|
)
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.data.model
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
|
||||||
|
import com.google.samples.apps.nowinandroid.core.model.data.Topic
|
||||||
|
|
||||||
|
/** */
|
||||||
|
data class SearchResult(
|
||||||
|
val topics: List<Topic> = emptyList(),
|
||||||
|
val newsResources: List<NewsResource> = emptyList(),
|
||||||
|
)
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.data.repository
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.asExternalModel
|
||||||
|
import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao
|
||||||
|
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
|
||||||
|
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
|
||||||
|
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class DefaultRecentSearchRepository @Inject constructor(
|
||||||
|
private val recentSearchQueryDao: RecentSearchQueryDao,
|
||||||
|
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
|
||||||
|
) : RecentSearchRepository {
|
||||||
|
override suspend fun insertOrReplaceRecentSearch(searchQuery: String) {
|
||||||
|
withContext(ioDispatcher) {
|
||||||
|
recentSearchQueryDao.insertOrReplaceRecentSearchQuery(
|
||||||
|
RecentSearchQueryEntity(
|
||||||
|
query = searchQuery,
|
||||||
|
queriedDate = Clock.System.now(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> {
|
||||||
|
return recentSearchQueryDao.getRecentSearchQueryEntities(limit).map { searchQueries ->
|
||||||
|
searchQueries.map {
|
||||||
|
it.asExternalModel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clearRecentSearches() = recentSearchQueryDao.clearRecentSearchQueries()
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.data.repository
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data layer interface for the recent searches.
|
||||||
|
*/
|
||||||
|
interface RecentSearchRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the recent search queries up to the number of queries specified as [limit].
|
||||||
|
*/
|
||||||
|
fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert or replace the [searchQuery] as part of the recent searches.
|
||||||
|
*/
|
||||||
|
suspend fun insertOrReplaceRecentSearch(searchQuery: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the recent searches.
|
||||||
|
*/
|
||||||
|
suspend fun clearRecentSearches()
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.data.repository.fake
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fake implementation of the [RecentSearchRepository]
|
||||||
|
*/
|
||||||
|
class FakeRecentSearchRepository @Inject constructor() : RecentSearchRepository {
|
||||||
|
override suspend fun insertOrReplaceRecentSearch(searchQuery: String) { /* no-op */ }
|
||||||
|
|
||||||
|
override fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> =
|
||||||
|
flowOf(emptyList())
|
||||||
|
|
||||||
|
override suspend fun clearRecentSearches() { /* no-op */ }
|
||||||
|
}
|
@ -0,0 +1,308 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 14,
|
||||||
|
"identityHash": "51271b81bde7c7997d67fb23c8f31780",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"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": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"news_resource_id",
|
||||||
|
"topic_id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ftsVersion": "FTS4",
|
||||||
|
"ftsOptions": {
|
||||||
|
"tokenizer": "simple",
|
||||||
|
"tokenizerArgs": [],
|
||||||
|
"contentTable": "",
|
||||||
|
"languageIdColumnName": "",
|
||||||
|
"matchInfo": "FTS4",
|
||||||
|
"notIndexedColumns": [],
|
||||||
|
"prefixSizes": [],
|
||||||
|
"preferredOrder": "ASC"
|
||||||
|
},
|
||||||
|
"contentSyncTriggers": [],
|
||||||
|
"tableName": "newsResourcesFts",
|
||||||
|
"createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`newsResourceId` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "newsResourceId",
|
||||||
|
"columnName": "newsResourceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": []
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ftsVersion": "FTS4",
|
||||||
|
"ftsOptions": {
|
||||||
|
"tokenizer": "simple",
|
||||||
|
"tokenizerArgs": [],
|
||||||
|
"contentTable": "",
|
||||||
|
"languageIdColumnName": "",
|
||||||
|
"matchInfo": "FTS4",
|
||||||
|
"notIndexedColumns": [],
|
||||||
|
"prefixSizes": [],
|
||||||
|
"preferredOrder": "ASC"
|
||||||
|
},
|
||||||
|
"contentSyncTriggers": [],
|
||||||
|
"tableName": "topicsFts",
|
||||||
|
"createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`topicId` TEXT NOT NULL, `name` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `longDescription` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "topicId",
|
||||||
|
"columnName": "topicId",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": []
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "recentSearchQueries",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`query` TEXT NOT NULL, `queriedDate` INTEGER NOT NULL, PRIMARY KEY(`query`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "query",
|
||||||
|
"columnName": "query",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "queriedDate",
|
||||||
|
"columnName": "queriedDate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"query"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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, '51271b81bde7c7997d67fb23c8f31780')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.database.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Upsert
|
||||||
|
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DAO for [RecentSearchQueryEntity] access
|
||||||
|
*/
|
||||||
|
@Dao
|
||||||
|
interface RecentSearchQueryDao {
|
||||||
|
@Query(value = "SELECT * FROM recentSearchQueries ORDER BY queriedDate DESC LIMIT :limit")
|
||||||
|
fun getRecentSearchQueryEntities(limit: Int): Flow<List<RecentSearchQueryEntity>>
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
suspend fun insertOrReplaceRecentSearchQuery(recentSearchQuery: RecentSearchQueryEntity)
|
||||||
|
|
||||||
|
@Query(value = "DELETE FROM recentSearchQueries")
|
||||||
|
suspend fun clearRecentSearchQueries()
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.database.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines an database entity that stored recent search queries.
|
||||||
|
*/
|
||||||
|
@Entity(
|
||||||
|
tableName = "recentSearchQueries",
|
||||||
|
)
|
||||||
|
data class RecentSearchQueryEntity(
|
||||||
|
@PrimaryKey
|
||||||
|
val query: String,
|
||||||
|
@ColumnInfo
|
||||||
|
val queriedDate: Instant,
|
||||||
|
)
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.domain
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A use case which returns the recent search queries.
|
||||||
|
*/
|
||||||
|
class GetRecentSearchQueriesUseCase @Inject constructor(
|
||||||
|
private val recentSearchRepository: RecentSearchRepository,
|
||||||
|
) {
|
||||||
|
operator fun invoke(limit: Int = 10): Flow<List<RecentSearchQuery>> =
|
||||||
|
recentSearchRepository.getRecentSearchQueries(limit)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.domain.model
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.SearchResult
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entity of [SearchResult] with additional user information such as whether the user is
|
||||||
|
* following a topic.
|
||||||
|
*/
|
||||||
|
data class UserSearchResult(
|
||||||
|
val topics: List<FollowableTopic> = emptyList(),
|
||||||
|
val newsResources: List<UserNewsResource> = emptyList(),
|
||||||
|
)
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.core.testing.repository
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
|
class TestRecentSearchRepository : RecentSearchRepository {
|
||||||
|
|
||||||
|
private val cachedRecentSearches: MutableList<RecentSearchQuery> = mutableListOf()
|
||||||
|
|
||||||
|
override fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> {
|
||||||
|
return flow {
|
||||||
|
emit(cachedRecentSearches.sortedByDescending { it.queriedDate }.take(limit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insertOrReplaceRecentSearch(searchQuery: String) {
|
||||||
|
cachedRecentSearches.add(RecentSearchQuery(searchQuery))
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clearRecentSearches() {
|
||||||
|
cachedRecentSearches.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.feature.search
|
||||||
|
|
||||||
|
import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery
|
||||||
|
|
||||||
|
sealed interface RecentSearchQueriesUiState {
|
||||||
|
object Loading : RecentSearchQueriesUiState
|
||||||
|
|
||||||
|
data class Success(
|
||||||
|
val recentQueries: List<RecentSearchQuery> = emptyList(),
|
||||||
|
) : RecentSearchQueriesUiState
|
||||||
|
}
|
Loading…
Reference in new issue