diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt index e4d8f50d..f04615c3 100644 --- a/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt @@ -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() diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml index 65de1496..5e359cbb 100644 --- a/i18n/src/main/res/values-de/strings.xml +++ b/i18n/src/main/res/values-de/strings.xml @@ -454,6 +454,8 @@ Websuchen Shortcuts zu verschiedenen Websuch-Engines anzeigen + Wikipedia-URL + %1$s spielt Medien Bisher wurden keine Medien abgespielt \ No newline at end of file diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 7306ee28..7e3f4c6b 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -492,6 +492,8 @@ Restrict to music apps Ignore media sessions of apps that are not music apps + Wikipedia URL + %1$s is playing media No media has been played yet diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt index 02e563c7..016385b9 100644 --- a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt +++ b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt @@ -62,6 +62,7 @@ fun createFactorySettings(context: Context): Settings { .setWikipediaSearch(Settings.WikipediaSearchSettings .newBuilder() .setEnabled(false) + .setImages(false) .setCustomUrl(null) ) .setWebsiteSearch(Settings.WebsiteSearchSettings diff --git a/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/TextPreference.kt b/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/TextPreference.kt new file mode 100644 index 00000000..d15ec75c --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/TextPreference.kt @@ -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 + ) + } + } + ) + } +} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt index 9c53a5a2..17004db8 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt @@ -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() } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt index 7c625292..b4dc0356 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt @@ -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") } ) diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt new file mode 100644 index 00000000..11a17c06 --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt @@ -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) + }) + } + } +} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt new file mode 100644 index 00000000..f93997ce --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt @@ -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() + } + } + } +} \ No newline at end of file diff --git a/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt b/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt index 7dcbe705..fc264f3f 100644 --- a/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt +++ b/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt @@ -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() - 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") - } } } \ No newline at end of file diff --git a/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt b/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt index 81a99961..9440229b 100644 --- a/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt +++ b/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt @@ -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 = 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 ) } diff --git a/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaSerialization.kt b/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaSerialization.kt index 6d602913..0746f9a9 100644 --- a/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaSerialization.kt +++ b/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaSerialization.kt @@ -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, ) } } \ No newline at end of file