Initial commit

This commit is contained in:
MM20
2021-09-18 23:37:52 +02:00
commit 749e4e3073
938 changed files with 50475 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+54
View File
@@ -0,0 +1,54 @@
plugins {
id("com.android.library")
id("kotlin-android")
id("kotlin-android-extensions")
}
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 {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation(libs.bundles.kotlin)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.browser)
implementation(libs.bundles.androidx.lifecycle)
implementation(libs.okhttp)
implementation(libs.bundles.retrofit)
implementation(project(":preferences"))
implementation(project(":search"))
implementation(project(":base"))
implementation(project(":crashreporter"))
}
View File
+21
View File
@@ -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.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
+4
View File
@@ -0,0 +1,4 @@
# :wikipedia
Provides Wikipedia search. In theory every Mediawiki instance could be used instead of Wikipedia.
The URL is set in `@string/wikipedia_url` in the `:i18n` module.
+5
View File
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.mm20.launcher2.wikipedia">
/
</manifest>
@@ -0,0 +1,135 @@
package de.mm20.launcher2.search.data
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Bundle
import android.text.Spanned
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.text.HtmlCompat
import androidx.core.text.toHtml
import de.mm20.launcher2.wikipedia.R
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.helper.NetworkUtils
import de.mm20.launcher2.preferences.LauncherPreferences
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
import kotlin.math.min
class Wikipedia(
override val label: String,
val id: Long,
val text: String,
val image: String?
) : Searchable() {
override val key = "wikipedia://$id"
override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? {
return null
}
override fun getPlaceholderIcon(context: Context): LauncherIcon {
return LauncherIcon(
foreground = context.getDrawable(R.drawable.ic_wikipedia)!!,
background = ColorDrawable(0xFFF0F0F0.toInt())
)
}
override fun getLaunchIntent(context: Context): Intent? {
val intent = CustomTabsIntent
.Builder()
.setToolbarColor(Color.BLACK)
.enableUrlBarHiding()
.setShowTitle(true)
.build()
val uri = "${context.getString(R.string.wikipedia_url)}/wiki?curid=$id"
intent.intent.data = Uri.parse(uri)
return intent.intent
}
override fun serialize(): String {
val json = JSONObject()
json.put("label", label)
json.put("text", text)
json.put("id", id)
json.put("image", image)
return json.toString()
}
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")
}
fun deserialize(serialized: String): Wikipedia {
val json = JSONObject(serialized)
return Wikipedia(
label = json.getString("label"),
text = json.getString("text"),
id = json.getLong("id"),
image = json.optString("image")
)
}
}
}
@@ -0,0 +1,42 @@
package de.mm20.launcher2.wikipedia
import retrofit2.http.GET
import retrofit2.http.Query
data class WikipediaSearchResult(
val query: WikipediaSearchResultQuery?,
)
data class WikipediaSearchResultQuery(
val pages: Map<String, WikipediaSearchResultQueryPage>,
)
data class WikipediaSearchResultQueryPage(
val pageid: Long,
val title: String,
val extract: String,
)
data class WikipediaGetPageImageResult(
val query: WikipediaGetPageImageResultQuery?,
)
data class WikipediaGetPageImageResultQuery(
val pages: Map<String, WikipediaGetPageImageResultQueryPage>
)
data class WikipediaGetPageImageResultQueryPage(
val thumbnail: WikipediaGetPageImageResultQueryPageThumnail?
)
data class WikipediaGetPageImageResultQueryPageThumnail(
val source: String
)
interface WikipediaApi {
@GET("w/api.php?action=query&generator=search&redirects=true&gsrlimit=1&explaintext=true&exchars=500&prop=extracts&exintro=true&format=json")
suspend fun search(@Query("gsrsearch") query: String): WikipediaSearchResult
@GET("w/api.php?action=query&prop=pageimages&format=json")
suspend fun getPageImage(@Query("pageids") pageId: Long, @Query("pithumbsize") thumbnailSize: Int): WikipediaGetPageImageResult
}
@@ -0,0 +1,98 @@
package de.mm20.launcher2.wikipedia
import android.content.Context
import androidx.core.text.HtmlCompat
import androidx.lifecycle.MutableLiveData
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.search.BaseSearchableRepository
import de.mm20.launcher2.search.data.Wikipedia
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.IOException
import java.util.concurrent.TimeUnit
import kotlin.math.min
class WikipediaRepository private constructor(val context: Context) : BaseSearchableRepository() {
val wikipedia = MutableLiveData<Wikipedia?>()
private val httpClient by lazy {
OkHttpClient
.Builder()
.connectTimeout(200, TimeUnit.MILLISECONDS)
.readTimeout(3000, TimeUnit.MILLISECONDS)
.writeTimeout(1000, TimeUnit.MILLISECONDS)
.build()
}
val retrofit by lazy {
Retrofit.Builder()
.client(httpClient)
.baseUrl(context.getString(R.string.wikipedia_url))
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val wikipediaService by lazy {
retrofit.create(WikipediaApi::class.java)
}
override fun onCancel() {
super.onCancel()
httpClient.dispatcher.run {
runningCalls().forEach {
it.cancel()
}
queuedCalls().forEach {
it.cancel()
}
}
}
override suspend fun search(query: String) {
wikipedia.value = null
if (query.isBlank()) return
val result = try {
wikipediaService.search(query)
} catch (e: Exception) {
CrashReporter.logException(e)
return
}
val page = result.query?.pages?.values?.toList()?.getOrNull(0) ?: return
val image = if (LauncherPreferences.instance.searchWikipediaPictures) {
val width = context.resources.displayMetrics.widthPixels / 2
val imageResult = try {
wikipediaService.getPageImage(page.pageid, width)
} catch (e: Exception) {
CrashReporter.logException(e)
return
}
imageResult.query?.pages?.values?.toList()?.getOrNull(0)?.thumbnail?.source
} else null
val wiki = Wikipedia(
label = page.title,
id = page.pageid,
text = page.extract,
image = image
)
wikipedia.value = wiki
}
companion object {
private lateinit var instance: WikipediaRepository
fun getInstance(context: Context): WikipediaRepository {
if (!::instance.isInitialized) instance =
WikipediaRepository(context.applicationContext)
return instance
}
}
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.wikipedia
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import de.mm20.launcher2.search.data.Wikipedia
class WikipediaViewModel(val app: Application): AndroidViewModel(app) {
val wikipedia: LiveData<Wikipedia?> = WikipediaRepository.getInstance(app).wikipedia
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB