Migrate Wikipedia settings, add wikipedia base url setting
This commit is contained in:
parent
a5cb2e3314
commit
b1412d24f1
@ -68,7 +68,7 @@ val favoritesModule = module {
|
||||
return@factory ContactDeserializer(androidContext())
|
||||
}
|
||||
if (type == "wikipedia") {
|
||||
return@factory WikipediaDeserializer()
|
||||
return@factory WikipediaDeserializer(androidContext())
|
||||
}
|
||||
if (type == "gdrive") {
|
||||
return@factory GDriveFileDeserializer()
|
||||
|
||||
@ -454,6 +454,8 @@
|
||||
<string name="preference_search_websearch">Websuchen</string>
|
||||
<string name="preference_search_websearch_summary">Shortcuts zu verschiedenen Websuch-Engines anzeigen</string>
|
||||
|
||||
<string name="preference_wikipedia_customurl">Wikipedia-URL</string>
|
||||
|
||||
<string name="music_widget_default_title">%1$s spielt Medien</string>
|
||||
<string name="music_widget_no_data">Bisher wurden keine Medien abgespielt</string>
|
||||
</resources>
|
||||
@ -492,6 +492,8 @@
|
||||
<string name="preference_music_filter_sources">Restrict to music apps</string>
|
||||
<string name="preference_music_filter_sources_summary">Ignore media sessions of apps that are not music apps</string>
|
||||
|
||||
<string name="preference_wikipedia_customurl">Wikipedia URL</string>
|
||||
|
||||
<string name="music_widget_default_title">%1$s is playing media</string>
|
||||
|
||||
<string name="music_widget_no_data">No media has been played yet</string>
|
||||
|
||||
@ -62,6 +62,7 @@ fun createFactorySettings(context: Context): Settings {
|
||||
.setWikipediaSearch(Settings.WikipediaSearchSettings
|
||||
.newBuilder()
|
||||
.setEnabled(false)
|
||||
.setImages(false)
|
||||
.setCustomUrl(null)
|
||||
)
|
||||
.setWebsiteSearch(Settings.WebsiteSearchSettings
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
package de.mm20.launcher2.ui.component.preferences
|
||||
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
@Composable
|
||||
fun TextPreference(
|
||||
title: String,
|
||||
value: String,
|
||||
summary: String? = value,
|
||||
onValueChanged: (String) -> Unit,
|
||||
placeholder: String? = null
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
title = title,
|
||||
summary = summary,
|
||||
onClick = { showDialog = true }
|
||||
)
|
||||
|
||||
if (showDialog) {
|
||||
var textFieldValue by remember { mutableStateOf(value) }
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
title = {
|
||||
Text(text = title, style = MaterialTheme.typography.titleLarge)
|
||||
},
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
value = textFieldValue,
|
||||
onValueChange = { textFieldValue = it },
|
||||
placeholder = placeholder?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onValueChanged(textFieldValue)
|
||||
showDialog = false
|
||||
}) {
|
||||
Text(
|
||||
text = stringResource(android.R.string.ok),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = {
|
||||
showDialog = false
|
||||
}) {
|
||||
Text(
|
||||
text = stringResource(android.R.string.cancel),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -32,6 +32,7 @@ import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.weatherwidget.WeatherWidgetSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.widgets.WidgetsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.wikipedia.WikipediaSettingsScreen
|
||||
|
||||
class SettingsActivity : BaseActivity() {
|
||||
|
||||
@ -90,6 +91,9 @@ class SettingsActivity : BaseActivity() {
|
||||
composable("settings/search") {
|
||||
SearchSettingsScreen()
|
||||
}
|
||||
composable("settings/search/wikipedia") {
|
||||
WikipediaSettingsScreen()
|
||||
}
|
||||
composable("settings/widgets") {
|
||||
WidgetsSettingsScreen()
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.*
|
||||
import de.mm20.launcher2.ui.icons.Wikipedia
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
|
||||
@Composable
|
||||
fun SearchSettingsScreen() {
|
||||
@ -24,6 +25,8 @@ fun SearchSettingsScreen() {
|
||||
val viewModel: SearchSettingsScreenVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
|
||||
val navController = LocalNavController.current
|
||||
|
||||
PreferenceScreen(title = stringResource(R.string.preference_screen_search)) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
@ -116,6 +119,9 @@ fun SearchSettingsScreen() {
|
||||
switchValue = wikipedia == true,
|
||||
onSwitchChanged = {
|
||||
viewModel.setWikipedia(it)
|
||||
},
|
||||
onClick = {
|
||||
navController?.navigate("settings/search/wikipedia")
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package de.mm20.launcher2.ui.settings.wikipedia
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.TextPreference
|
||||
|
||||
@Composable
|
||||
fun WikipediaSettingsScreen() {
|
||||
val viewModel: WikipediaSettingsScreenVM = viewModel()
|
||||
PreferenceScreen(title = stringResource(R.string.preference_search_wikipedia)) {
|
||||
item {
|
||||
val wikipedia by viewModel.wikipedia.observeAsState()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_search_wikipedia),
|
||||
summary = stringResource(R.string.preference_search_wikipedia_summary),
|
||||
value = wikipedia == true,
|
||||
onValueChanged = {
|
||||
viewModel.setWikipedia(it)
|
||||
}
|
||||
)
|
||||
val images by viewModel.images.observeAsState()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_search_wikipedia_pictures),
|
||||
summary = stringResource(R.string.preference_search_wikipedia_pictures_summary),
|
||||
enabled = wikipedia == true,
|
||||
value = images == true,
|
||||
onValueChanged = {
|
||||
viewModel.setImages(it)
|
||||
}
|
||||
)
|
||||
val customUrl by viewModel.customUrl.observeAsState("")
|
||||
TextPreference(
|
||||
title = stringResource(R.string.preference_wikipedia_customurl),
|
||||
value = customUrl,
|
||||
placeholder = stringResource(id = R.string.wikipedia_url),
|
||||
summary = customUrl.takeIf { !it.isNullOrBlank() }
|
||||
?: stringResource(id = R.string.wikipedia_url),
|
||||
onValueChanged = {
|
||||
viewModel.setCustomUrl(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package de.mm20.launcher2.ui.settings.wikipedia
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class WikipediaSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled }.asLiveData()
|
||||
fun setWikipedia(wikipedia: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder()
|
||||
.setEnabled(wikipedia)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val images = dataStore.data.map { it.wikipediaSearch.images }.asLiveData()
|
||||
fun setImages(images: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder()
|
||||
.setImages(images)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val customUrl = dataStore.data.map { it.wikipediaSearch.customUrl }.asLiveData()
|
||||
fun setCustomUrl(customUrl: String) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder()
|
||||
.setCustomUrl(customUrl)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,9 +25,10 @@ class Wikipedia(
|
||||
override val label: String,
|
||||
val id: Long,
|
||||
val text: String,
|
||||
val image: String?
|
||||
val image: String?,
|
||||
val wikipediaUrl: String,
|
||||
) : Searchable() {
|
||||
override val key = "wikipedia://$id"
|
||||
override val key = "wikipedia://$wikipediaUrl:$id"
|
||||
|
||||
override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? {
|
||||
return null
|
||||
@ -47,70 +48,12 @@ class Wikipedia(
|
||||
.enableUrlBarHiding()
|
||||
.setShowTitle(true)
|
||||
.build()
|
||||
val uri = "${context.getString(R.string.wikipedia_url)}/wiki?curid=$id"
|
||||
val uri = "${wikipediaUrl}/wiki?curid=$id"
|
||||
intent.intent.data = Uri.parse(uri)
|
||||
return intent.intent
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun search(context: Context, query: String, client: OkHttpClient): Wikipedia? {
|
||||
mutableListOf<Searchable>()
|
||||
if (query.length < 4) return null
|
||||
val prefs = LauncherPreferences.instance
|
||||
if (!prefs.searchWikipedia ||
|
||||
NetworkUtils.isOffline(context, prefs.searchWikipediaMobileData)) return null
|
||||
val url = (context.getString(R.string.wikipedia_url) + "/w/api.php?action=query&"
|
||||
+ "generator=search&redirects=true&gsrlimit=1&prop=extracts&format=json&gsrsearch="
|
||||
+ query)
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.tag("onlinesearch")
|
||||
.build()
|
||||
try {
|
||||
val response = client.newCall(request).execute()
|
||||
val json = JSONObject(response.body?.string() ?: return null)
|
||||
val pages = json.getJSONObject("query")
|
||||
.getJSONObject("pages")
|
||||
val it = pages.keys()
|
||||
if (it.hasNext()) {
|
||||
val key = it.next()
|
||||
val id = pages.getJSONObject(key).getLong("pageid")
|
||||
val title = pages.getJSONObject(key).getString("title")
|
||||
val text = pages.getJSONObject(key).getString("extract").also{
|
||||
it.substring(0, min(500, it.length)) + "…"
|
||||
}
|
||||
val image = getArticleImage(context, id, client)
|
||||
return Wikipedia(
|
||||
label = title,
|
||||
text = text,
|
||||
id = id,
|
||||
image = image
|
||||
)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
} catch (e: JSONException) {
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getArticleImage(context: Context, id: Long, client: OkHttpClient): String? {
|
||||
if (!LauncherPreferences.instance.searchWikipediaPictures) return null
|
||||
val width = context.resources.displayMetrics.widthPixels / 2
|
||||
val url = (context.getString(R.string.wikipedia_url) + "/w/api.php?action=query&"
|
||||
+ "prop=pageimages&format=json&pageids=$id&pithumbsize=$width")
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.tag("onlinesearch")
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
val json = JSONObject(response.body?.string() ?: return null)
|
||||
return json.getJSONObject("query")
|
||||
.getJSONObject("pages")
|
||||
.getJSONObject(id.toString())
|
||||
.optJSONObject("thumbnail")
|
||||
?.getString("source")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,12 @@
|
||||
package de.mm20.launcher2.wikipedia
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.data.Wikipedia
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
@ -25,28 +22,40 @@ class WikipediaRepositoryImpl(
|
||||
private val context: Context
|
||||
) : WikipediaRepository, KoinComponent {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
private val httpClient by lazy {
|
||||
OkHttpClient
|
||||
.Builder()
|
||||
.connectTimeout(200, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(3000, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(1000, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
private val httpClient = OkHttpClient
|
||||
.Builder()
|
||||
.connectTimeout(200, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(3000, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(1000, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
private lateinit var retrofit: Retrofit
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
dataStore.data
|
||||
.map { it.wikipediaSearch.customUrl }
|
||||
.distinctUntilChanged()
|
||||
.collectLatest {
|
||||
retrofit = Retrofit.Builder()
|
||||
.client(httpClient)
|
||||
.baseUrl(it.takeIf { !it.isNullOrBlank() }
|
||||
?: context.getString(R.string.wikipedia_url))
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
wikipediaService = retrofit.create(WikipediaApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val retrofit by lazy {
|
||||
Retrofit.Builder()
|
||||
.client(httpClient)
|
||||
.baseUrl(context.getString(R.string.wikipedia_url))
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
|
||||
private val wikipediaService by lazy {
|
||||
private lateinit var wikipediaService: WikipediaApi
|
||||
/*by lazy {
|
||||
retrofit.create(WikipediaApi::class.java)
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
override fun search(query: String): Flow<Wikipedia?> = channelFlow {
|
||||
@ -54,6 +63,8 @@ class WikipediaRepositoryImpl(
|
||||
withContext(Dispatchers.IO) {
|
||||
httpClient.dispatcher.cancelAll()
|
||||
}
|
||||
|
||||
if (!::wikipediaService.isInitialized) return@channelFlow
|
||||
if (query.isBlank()) return@channelFlow
|
||||
|
||||
dataStore.data.map { it.wikipediaSearch }.collectLatest {
|
||||
@ -67,6 +78,8 @@ class WikipediaRepositoryImpl(
|
||||
|
||||
private suspend fun queryWikipedia(query: String, loadImages: Boolean): Wikipedia? {
|
||||
|
||||
val wikipediaService = wikipediaService
|
||||
val wikipediaUrl = retrofit.baseUrl().toString()
|
||||
|
||||
val result = try {
|
||||
wikipediaService.search(query)
|
||||
@ -92,7 +105,8 @@ class WikipediaRepositoryImpl(
|
||||
label = page.title,
|
||||
id = page.pageid,
|
||||
text = page.extract,
|
||||
image = image
|
||||
image = image,
|
||||
wikipediaUrl = wikipediaUrl
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package de.mm20.launcher2.wikipedia
|
||||
|
||||
import android.content.Context
|
||||
import de.mm20.launcher2.search.SearchableDeserializer
|
||||
import de.mm20.launcher2.search.SearchableSerializer
|
||||
import de.mm20.launcher2.search.data.Searchable
|
||||
@ -14,6 +15,7 @@ class WikipediaSerializer : SearchableSerializer {
|
||||
json.put("text", searchable.text)
|
||||
json.put("id", searchable.id)
|
||||
json.put("image", searchable.image)
|
||||
json.put("wikipedia_url", searchable.wikipediaUrl)
|
||||
return json.toString()
|
||||
}
|
||||
|
||||
@ -21,14 +23,15 @@ class WikipediaSerializer : SearchableSerializer {
|
||||
get() = "wikipedia"
|
||||
}
|
||||
|
||||
class WikipediaDeserializer : SearchableDeserializer {
|
||||
class WikipediaDeserializer(val context: Context) : SearchableDeserializer {
|
||||
override fun deserialize(serialized: String): Searchable? {
|
||||
val json = JSONObject(serialized)
|
||||
return Wikipedia(
|
||||
label = json.getString("label"),
|
||||
text = json.getString("text"),
|
||||
id = json.getLong("id"),
|
||||
image = json.optString("image")
|
||||
image = json.optString("image"),
|
||||
wikipediaUrl = json.optString("wikipedia_url").takeIf { !it.isNullOrBlank() } ?: return null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user