Migrate Wikipedia settings, add wikipedia base url setting

This commit is contained in:
MM20 2022-01-11 22:10:43 +01:00
parent a5cb2e3314
commit b1412d24f1
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
12 changed files with 238 additions and 88 deletions

View File

@ -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()

View File

@ -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>

View File

@ -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>

View File

@ -62,6 +62,7 @@ fun createFactorySettings(context: Context): Settings {
.setWikipediaSearch(Settings.WikipediaSearchSettings
.newBuilder()
.setEnabled(false)
.setImages(false)
.setCustomUrl(null)
)
.setWebsiteSearch(Settings.WebsiteSearchSettings

View File

@ -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
)
}
}
)
}
}

View File

@ -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()
}

View File

@ -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")
}
)

View File

@ -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)
})
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -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")
}
}
}

View File

@ -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
)
}

View File

@ -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,
)
}
}