Reorganize and group modules

This commit is contained in:
MM20
2022-12-13 17:37:26 +01:00
parent bac24baad2
commit 3f8880a90a
995 changed files with 501 additions and 298 deletions
+1
View File
@@ -0,0 +1 @@
/build
+47
View File
@@ -0,0 +1,47 @@
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.webdav"
}
dependencies {
implementation(libs.bundles.kotlin)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.okhttp)
implementation(project(":core:crashreporter"))
implementation(project(":core:ktx"))
}
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.
#
# 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 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
/
</manifest>
@@ -0,0 +1,192 @@
package de.mm20.launcher2.webdav
import com.balsikandar.crashreporter.CrashReporter
import de.mm20.launcher2.ktx.castToOrNull
import de.mm20.launcher2.ktx.decodeUrl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.w3c.dom.Element
import org.xml.sax.SAXException
import java.io.IOException
import java.io.InputStream
import java.lang.Exception
import java.net.URLDecoder
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.parsers.ParserConfigurationException
object WebDavApi {
suspend fun search(webDavUrl: String, username: String, query: String, client: OkHttpClient): List<WebDavFile> {
val requestBody = """
<?xml version="1.0" encoding="UTF-8"?>
<d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:basicsearch>
<d:select>
<d:prop>
<oc:fileid/>
<d:displayname/>
<d:getcontenttype/>
<d:resourcetype/>
<oc:size/>
<oc:owner-display-name/>
</d:prop>
</d:select>
<d:from>
<d:scope>
<d:href><![CDATA[/files/$username]]></d:href>
<d:depth>infinity</d:depth>
</d:scope>
</d:from>
<d:where>
<d:like>
<d:prop>
<d:displayname/>
</d:prop>
<d:literal><![CDATA[%$query%]]></d:literal>
</d:like>
</d:where>
<d:orderby/>
</d:basicsearch>
</d:searchrequest>
""".trimIndent()
val request = Request.Builder()
.url(webDavUrl)
.method("SEARCH", requestBody.toRequestBody("text/xml".toMediaType()))
.build()
return withContext(Dispatchers.IO) {
val results = mutableListOf<WebDavFile>()
try {
val response = client.newCall(request).execute()
val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(response.body?.byteStream()
?: return@withContext emptyList<WebDavFile>())
val responses = document.getElementsByTagName("d:response")
for (i in 0 until responses.length) {
val res = responses.item(i) as? Element ?: continue
val url = res.getElementsByTagName("d:href")
.takeIf { it.length > 0 }?.item(0)
?.textContent?.takeIf { it.isNotEmpty() } ?: continue
val fileId = res.getElementsByTagName("oc:fileid")
.takeIf { it.length > 0 }?.item(0)
?.textContent?.toLongOrNull() ?: continue
val displayName = res.getElementsByTagName("d:displayname")
.takeIf { it.length > 0 }?.item(0)?.textContent
?.takeIf { it.isNotEmpty() }
?: url.trimEnd('/').substringAfterLast("/").decodeUrl("utf8")
?: continue
val isDirectory = res.getElementsByTagName("d:resourcetype")
.takeIf { it.length > 0 }
?.item(0)?.childNodes?.length == 1
val mimeType = res.getElementsByTagName("d:getcontenttype")
.takeIf { it.length > 0 }?.item(0)?.textContent?.takeIf { it.isNotEmpty() }
?: if (isDirectory) "inode/directory" else "application/octet-stream"
val size = res.getElementsByTagName("oc:size")
.takeIf { it.length > 0 }?.item(0)?.textContent?.toLongOrNull()
?: 0L
val owner = res.getElementsByTagName("oc:owner-display-name")
.takeIf { it.length > 0 }?.item(0)?.textContent
?.takeIf { it.isNotEmpty() }
results += WebDavFile(
name = displayName,
id = fileId,
isDirectory = isDirectory,
mimeType = mimeType,
size = size,
owner = owner,
url = url
)
}
} catch (e: Exception) {
CrashReporter.logException(e)
}
return@withContext results
}
}
fun getSearchRequestBody(query: String): String {
return """
<?xml version="1.0" encoding="UTF-8"?>
<oc:search-files
xmlns:a="DAV:"
xmlns:oc="http://owncloud.org/ns">
<a:prop>
<oc:fileid/>
<a:displayname/>
<a:getcontenttype/>
<a:resourcetype/>
<oc:size/>
<oc:owner-display-name/>
</a:prop>
<oc:search>
<oc:pattern><![CDATA[$query]]></oc:pattern>
<oc:limit>20</oc:limit>
</oc:search>
</oc:search-files>
""".trimIndent()
}
suspend fun searchReport(webDavUrl: String, username: String, query: String, client: OkHttpClient): List<WebDavFile> {
val requestBody = getSearchRequestBody(query)
val request = Request.Builder()
.url("${webDavUrl}files/$username")
.method("REPORT", requestBody.toRequestBody())
.build()
return withContext(Dispatchers.IO) {
val results = mutableListOf<WebDavFile>()
try {
val response = client.newCall(request).execute()
val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(response.body?.byteStream()
?: return@withContext emptyList<WebDavFile>())
val responses = document.getElementsByTagName("d:response")
for (i in 0 until responses.length) {
val res = responses.item(i) as? Element ?: continue
val url = res.getElementsByTagName("d:href")
.takeIf { it.length > 0 }?.item(0)
?.textContent?.takeIf { it.isNotEmpty() } ?: continue
val fileId = res.getElementsByTagName("oc:fileid")
.takeIf { it.length > 0 }?.item(0)
?.textContent?.toLongOrNull() ?: continue
val displayName = res.getElementsByTagName("d:displayname")
.takeIf { it.length > 0 }?.item(0)?.textContent
?.takeIf { it.isNotEmpty() }
?: url.trimEnd('/').substringAfterLast("/").decodeUrl("utf8")
?: continue
val isDirectory = res.getElementsByTagName("d:resourcetype")
.takeIf { it.length > 0 }
?.item(0)?.childNodes?.length == 1
val mimeType = res.getElementsByTagName("d:getcontenttype")
.takeIf { it.length > 0 }?.item(0)?.textContent?.takeIf { it.isNotEmpty() }
?: if (isDirectory) "inode/directory" else "application/octet-stream"
val size = res.getElementsByTagName("oc:size")
.takeIf { it.length > 0 }?.item(0)?.textContent?.toLongOrNull()
?: 0L
val owner = res.getElementsByTagName("oc:owner-display-name")
.takeIf { it.length > 0 }?.item(0)?.textContent
?.takeIf { it.isNotEmpty() }
results += WebDavFile(
name = displayName,
id = fileId,
isDirectory = isDirectory,
mimeType = mimeType,
size = size,
owner = owner,
url = url
)
}
} catch (e: Exception) {
CrashReporter.logException(e)
}
return@withContext results
}
}
}
@@ -0,0 +1,11 @@
package de.mm20.launcher2.webdav
data class WebDavFile(
val name: String,
val id: Long,
val url: String,
val isDirectory: Boolean,
val mimeType: String,
val size: Long,
val owner: String?
)