Reorganize and group modules
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,67 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = sdk.versions.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
minSdk = sdk.versions.minSdk.get().toInt()
|
||||
targetSdk = sdk.versions.targetSdk.get().toInt()
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
namespace = "de.mm20.launcher2.search"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.bundles.kotlin)
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.appcompat)
|
||||
|
||||
implementation(libs.bundles.androidx.lifecycle)
|
||||
|
||||
implementation(libs.koin.android)
|
||||
|
||||
implementation(libs.jsoup)
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.coil.core)
|
||||
|
||||
implementation(project(":data:applications"))
|
||||
implementation(project(":data:appshortcuts"))
|
||||
implementation(project(":data:calculator"))
|
||||
implementation(project(":data:calendar"))
|
||||
implementation(project(":data:contacts"))
|
||||
implementation(project(":data:files"))
|
||||
implementation(project(":data:unitconverter"))
|
||||
implementation(project(":data:websites"))
|
||||
implementation(project(":data:wikipedia"))
|
||||
implementation(project(":data:customattrs"))
|
||||
implementation(project(":data:search-actions"))
|
||||
|
||||
implementation(project(":core:base"))
|
||||
implementation(project(":core:database"))
|
||||
implementation(project(":core:preferences"))
|
||||
implementation(project(":core:crashreporter"))
|
||||
implementation(project(":core:ktx"))
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.kts.kts.kts.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1 @@
|
||||
<manifest />
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.mm20.launcher2.search
|
||||
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
val searchModule = module {
|
||||
single<SearchService> {
|
||||
SearchServiceImpl(
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package de.mm20.launcher2.search
|
||||
|
||||
import de.mm20.launcher2.applications.AppRepository
|
||||
import de.mm20.launcher2.appshortcuts.AppShortcutRepository
|
||||
import de.mm20.launcher2.calculator.CalculatorRepository
|
||||
import de.mm20.launcher2.calendar.CalendarRepository
|
||||
import de.mm20.launcher2.contacts.ContactRepository
|
||||
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
|
||||
import de.mm20.launcher2.data.customattrs.utils.withCustomLabels
|
||||
import de.mm20.launcher2.files.FileRepository
|
||||
import de.mm20.launcher2.preferences.Settings.AppShortcutSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.CalculatorSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.CalendarSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.ContactsSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.FilesSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.UnitConverterSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.WebsiteSearchSettings
|
||||
import de.mm20.launcher2.preferences.Settings.WikipediaSearchSettings
|
||||
import de.mm20.launcher2.search.data.AppShortcut
|
||||
import de.mm20.launcher2.search.data.Calculator
|
||||
import de.mm20.launcher2.search.data.CalendarEvent
|
||||
import de.mm20.launcher2.search.data.Contact
|
||||
import de.mm20.launcher2.search.data.File
|
||||
import de.mm20.launcher2.search.data.GDriveFile
|
||||
import de.mm20.launcher2.search.data.LauncherApp
|
||||
import de.mm20.launcher2.search.data.LocalFile
|
||||
import de.mm20.launcher2.search.data.NextcloudFile
|
||||
import de.mm20.launcher2.search.data.OneDriveFile
|
||||
import de.mm20.launcher2.search.data.OwncloudFile
|
||||
import de.mm20.launcher2.search.data.UnitConverter
|
||||
import de.mm20.launcher2.search.data.Website
|
||||
import de.mm20.launcher2.search.data.Wikipedia
|
||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||
import de.mm20.launcher2.searchactions.SearchActionService
|
||||
import de.mm20.launcher2.unitconverter.UnitConverterRepository
|
||||
import de.mm20.launcher2.websites.WebsiteRepository
|
||||
import de.mm20.launcher2.wikipedia.WikipediaRepository
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
|
||||
interface SearchService {
|
||||
fun search(
|
||||
query: String,
|
||||
shortcuts: AppShortcutSearchSettings,
|
||||
contacts: ContactsSearchSettings,
|
||||
calendars: CalendarSearchSettings,
|
||||
files: FilesSearchSettings,
|
||||
calculator: CalculatorSearchSettings,
|
||||
unitConverter: UnitConverterSearchSettings,
|
||||
websites: WebsiteSearchSettings,
|
||||
wikipedia: WikipediaSearchSettings,
|
||||
): Flow<ImmutableList<Searchable>>
|
||||
}
|
||||
|
||||
internal class SearchServiceImpl(
|
||||
private val appRepository: AppRepository,
|
||||
private val appShortcutRepository: AppShortcutRepository,
|
||||
private val calendarRepository: CalendarRepository,
|
||||
private val contactRepository: ContactRepository,
|
||||
private val fileRepository: FileRepository,
|
||||
private val wikipediaRepository: WikipediaRepository,
|
||||
private val unitConverterRepository: UnitConverterRepository,
|
||||
private val calculatorRepository: CalculatorRepository,
|
||||
private val websiteRepository: WebsiteRepository,
|
||||
private val searchActionService: SearchActionService,
|
||||
private val customAttributesRepository: CustomAttributesRepository,
|
||||
) : SearchService {
|
||||
|
||||
override fun search(
|
||||
query: String,
|
||||
shortcuts: AppShortcutSearchSettings,
|
||||
contacts: ContactsSearchSettings,
|
||||
calendars: CalendarSearchSettings,
|
||||
files: FilesSearchSettings,
|
||||
calculator: CalculatorSearchSettings,
|
||||
unitConverter: UnitConverterSearchSettings,
|
||||
websites: WebsiteSearchSettings,
|
||||
wikipedia: WikipediaSearchSettings,
|
||||
): Flow<ImmutableList<Searchable>> = channelFlow {
|
||||
var searchActionsReady = false
|
||||
supervisorScope {
|
||||
val results = MutableStateFlow(SearchResults())
|
||||
launch {
|
||||
appRepository.search(query)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(apps = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shortcuts.enabled) {
|
||||
launch {
|
||||
appShortcutRepository.search(query)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(shortcuts = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contacts.enabled) {
|
||||
launch {
|
||||
contactRepository.search(query)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(contacts = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (calendars.enabled) {
|
||||
launch {
|
||||
calendarRepository.search(query)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(calendars = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (calculator.enabled) {
|
||||
launch {
|
||||
calculatorRepository.search(query).collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(calculators = r?.let { persistentListOf(it) }
|
||||
?: persistentListOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unitConverter.enabled) {
|
||||
launch {
|
||||
unitConverterRepository.search(query, unitConverter.currencies).collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(unitConverters = r?.let { persistentListOf(it) }
|
||||
?: persistentListOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (websites.enabled) {
|
||||
launch {
|
||||
websiteRepository.search(query)
|
||||
.map { it?.let { listOf(it) } ?: listOf() }
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(websites = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wikipedia.enabled) {
|
||||
launch {
|
||||
wikipediaRepository.search(query, loadImages = wikipedia.images)
|
||||
.map { it?.let { listOf(it) } ?: listOf() }
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(wikipedia = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (files.localFiles || files.owncloud || files.onedrive || files.gdrive || files.nextcloud) {
|
||||
launch {
|
||||
fileRepository.search(
|
||||
query,
|
||||
local = files.localFiles,
|
||||
nextcloud = files.nextcloud,
|
||||
owncloud = files.owncloud,
|
||||
onedrive = files.onedrive,
|
||||
gdrive = files.gdrive,
|
||||
)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(files = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
customAttributesRepository.search(query)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
it.copy(
|
||||
other = r
|
||||
.filter {
|
||||
it is LauncherApp ||
|
||||
shortcuts.enabled && it is AppShortcut ||
|
||||
files.localFiles && it is LocalFile ||
|
||||
files.nextcloud && it is NextcloudFile ||
|
||||
files.owncloud && it is OwncloudFile ||
|
||||
files.onedrive && it is OneDriveFile ||
|
||||
files.gdrive && it is GDriveFile ||
|
||||
wikipedia.enabled && it is Wikipedia ||
|
||||
websites.enabled && it is Website ||
|
||||
calendars.enabled && it is CalendarEvent ||
|
||||
contacts.enabled && it is Contact
|
||||
}.toImmutableList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
searchActionService.search(query)
|
||||
.collectLatest { r ->
|
||||
results.update {
|
||||
searchActionsReady = true
|
||||
it.copy(searchActions = r)
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
results
|
||||
.map { it.toList().sortedBy { it as? SavableSearchable }.toImmutableList() }
|
||||
.collectLatest {
|
||||
if (searchActionsReady) send(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal data class SearchResults(
|
||||
val apps: List<LauncherApp> = emptyList(),
|
||||
val shortcuts: List<AppShortcut> = emptyList(),
|
||||
val contacts: List<Contact> = emptyList(),
|
||||
val calendars: List<CalendarEvent> = emptyList(),
|
||||
val files: List<File> = emptyList(),
|
||||
val calculators: List<Calculator> = emptyList(),
|
||||
val unitConverters: List<UnitConverter> = emptyList(),
|
||||
val websites: List<Website> = emptyList(),
|
||||
val wikipedia: List<Wikipedia> = emptyList(),
|
||||
val searchActions: List<SearchAction> = emptyList(),
|
||||
val other: List<SavableSearchable> = emptyList(),
|
||||
) {
|
||||
fun toList(): List<Searchable> {
|
||||
return searchActions + (apps + shortcuts + contacts + calendars + files + websites + wikipedia + other).distinctBy { it.key } + calculators + unitConverters
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package de.mm20.launcher2.search.data
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import de.mm20.launcher2.database.entities.WebsearchEntity
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class Websearch(
|
||||
var urlTemplate: String,
|
||||
var label: String,
|
||||
var color: Int,
|
||||
var icon: String?,
|
||||
var id: Long? = null,
|
||||
var encoding: QueryEncoding = QueryEncoding.UrlEncode,
|
||||
val query: String? = null,
|
||||
) {
|
||||
|
||||
constructor(entity: WebsearchEntity, query: String? = null) : this(
|
||||
urlTemplate = entity.urlTemplate,
|
||||
label = entity.label,
|
||||
icon = entity.icon,
|
||||
color = entity.color,
|
||||
id = entity.id,
|
||||
query = query,
|
||||
encoding = QueryEncoding.fromInt(entity.encoding)
|
||||
)
|
||||
|
||||
fun toDatabaseEntity(): WebsearchEntity {
|
||||
return WebsearchEntity(
|
||||
urlTemplate = urlTemplate,
|
||||
color = color,
|
||||
icon = icon,
|
||||
label = label,
|
||||
id = id,
|
||||
encoding = encoding.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
fun getLaunchIntent(): Intent? {
|
||||
if (query == null) return null
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val url = urlTemplate.replace("\${1}", encodeQuery(query, encoding))
|
||||
intent.data = Uri.parse(url)
|
||||
return intent
|
||||
}
|
||||
|
||||
private fun encodeQuery(query: String, encoding: QueryEncoding): String {
|
||||
return when(encoding) {
|
||||
QueryEncoding.UrlEncode -> Uri.encode(query)
|
||||
QueryEncoding.FormData -> URLEncoder.encode(query, "UTF-8")
|
||||
QueryEncoding.None -> query
|
||||
}
|
||||
}
|
||||
|
||||
enum class QueryEncoding {
|
||||
UrlEncode,
|
||||
FormData,
|
||||
None;
|
||||
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
UrlEncode -> 0
|
||||
FormData -> 1
|
||||
None -> 2
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int?): QueryEncoding {
|
||||
return when (value) {
|
||||
1 -> FormData
|
||||
2 -> None
|
||||
else -> UrlEncode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user