Finish Android KMP migration
Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
parent
0b336610df
commit
f155bdfa42
16 changed files with 31 additions and 229 deletions
|
@ -3,18 +3,9 @@
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
<value>
|
<value>
|
||||||
<package name="java.util" alias="false" withSubpackages="false" />
|
<package name="java.util" withSubpackages="false" static="false" />
|
||||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
|
||||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
<package name="io.ktor" withSubpackages="true" static="false" />
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
|
||||||
<value>
|
|
||||||
<package name="" alias="false" withSubpackages="true" />
|
|
||||||
<package name="java" alias="false" withSubpackages="true" />
|
|
||||||
<package name="javax" alias="false" withSubpackages="true" />
|
|
||||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
|
||||||
<package name="" alias="true" withSubpackages="true" />
|
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="11" />
|
<bytecodeTargetLevel target="1.8" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -11,11 +11,11 @@
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/android" />
|
<option value="$PROJECT_DIR$/android" />
|
||||||
<option value="$PROJECT_DIR$/piholeclient" />
|
|
||||||
<option value="$PROJECT_DIR$/shared" />
|
<option value="$PROJECT_DIR$/shared" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
<option name="useQualifiedModuleNames" value="true" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
||||||
</configurations>
|
</configurations>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package="com.wbrawner.pihelper">
|
package="com.wbrawner.pihelper">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".PiHelperApplication"
|
android:name=".PiHelperApplication"
|
||||||
|
@ -12,7 +13,8 @@
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
|
1
piholeclient/.gitignore
vendored
1
piholeclient/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,45 +0,0 @@
|
||||||
apply plugin: 'com.android.library'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply plugin: 'kotlin-android-extensions'
|
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdkVersion 29
|
|
||||||
defaultConfig {
|
|
||||||
minSdkVersion 23
|
|
||||||
targetSdkVersion 29
|
|
||||||
versionCode 1
|
|
||||||
versionName "1.0"
|
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
consumerProguardFiles 'consumer-rules.pro'
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
|
||||||
implementation "org.koin:koin-core:$koin_version"
|
|
||||||
implementation "com.squareup.okhttp3:okhttp:${okhttp_version}"
|
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:${okhttp_version}"
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
|
||||||
implementation 'androidx.core:core-ktx:1.1.0'
|
|
||||||
implementation "com.squareup.moshi:moshi:1.9.2"
|
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
|
|
||||||
testImplementation 'junit:junit:4.12'
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
# JSR 305 annotations are for embedding nullability information.
|
|
||||||
-dontwarn javax.annotation.**
|
|
||||||
|
|
||||||
-keepclasseswithmembers class * {
|
|
||||||
@com.squareup.moshi.* <methods>;
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep @com.squareup.moshi.JsonQualifier interface *
|
|
||||||
|
|
||||||
# Enum field names are used by the integrated EnumJsonAdapter.
|
|
||||||
# values() is synthesized by the Kotlin compiler and is used by EnumJsonAdapter indirectly
|
|
||||||
# Annotate enums with @JsonClass(generateAdapter = false) to use them with Moshi.
|
|
||||||
-keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
|
|
||||||
<fields>;
|
|
||||||
**[] values();
|
|
||||||
}
|
|
||||||
|
|
||||||
# The name of @JsonClass types is used to look up the generated adapter.
|
|
||||||
-keepnames @com.squareup.moshi.JsonClass class *
|
|
||||||
|
|
||||||
# Retain generated target class's synthetic defaults constructor and keep DefaultConstructorMarker's
|
|
||||||
# name. We will look this up reflectively to invoke the type's constructor.
|
|
||||||
#
|
|
||||||
# We can't _just_ keep the defaults constructor because Proguard/R8's spec doesn't allow wildcard
|
|
||||||
# matching preceding parameters.
|
|
||||||
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker
|
|
||||||
-keepclassmembers @com.squareup.moshi.JsonClass @kotlin.Metadata class * {
|
|
||||||
synthetic <init>(...);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Retain generated JsonAdapters if annotated type is retained.
|
|
||||||
-if @com.squareup.moshi.JsonClass class *
|
|
||||||
-keep class <1>JsonAdapter {
|
|
||||||
<init>(...);
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
-if @com.squareup.moshi.JsonClass class **$*
|
|
||||||
-keep class <1>_<2>JsonAdapter {
|
|
||||||
<init>(...);
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
-if @com.squareup.moshi.JsonClass class **$*$*
|
|
||||||
-keep class <1>_<2>_<3>JsonAdapter {
|
|
||||||
<init>(...);
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
-if @com.squareup.moshi.JsonClass class **$*$*$*
|
|
||||||
-keep class <1>_<2>_<3>_<4>JsonAdapter {
|
|
||||||
<init>(...);
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
-if @com.squareup.moshi.JsonClass class **$*$*$*$*
|
|
||||||
-keep class <1>_<2>_<3>_<4>_<5>JsonAdapter {
|
|
||||||
<init>(...);
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
-if @com.squareup.moshi.JsonClass class **$*$*$*$*$*
|
|
||||||
-keep class <1>_<2>_<3>_<4>_<5>_<6>JsonAdapter {
|
|
||||||
<init>(...);
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl
|
|
||||||
|
|
||||||
-keepclassmembers class kotlin.Metadata {
|
|
||||||
public <methods>;
|
|
||||||
}
|
|
23
piholeclient/proguard-rules.pro
vendored
23
piholeclient/proguard-rules.pro
vendored
|
@ -1,23 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
-keepclassmembers @com.squareup.moshi.JsonClass class *
|
|
|
@ -1,5 +0,0 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="com.wbrawner.piholeclient">
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<application android:usesCleartextTraffic="true" />
|
|
||||||
</manifest>
|
|
|
@ -1,60 +0,0 @@
|
||||||
package com.wbrawner.piholeclient
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonReader
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.CookieJar
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import okio.Buffer
|
|
||||||
import org.koin.dsl.module
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
val piHoleClientModule = module {
|
|
||||||
single {
|
|
||||||
Moshi.Builder().build()
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
val client = OkHttpClient.Builder()
|
|
||||||
.connectTimeout(500, TimeUnit.MILLISECONDS)
|
|
||||||
.cookieJar(object : CookieJar {
|
|
||||||
val cookies = mutableMapOf<String, List<Cookie>>()
|
|
||||||
override fun loadForRequest(url: HttpUrl): List<Cookie> = cookies[url.host]
|
|
||||||
?: emptyList()
|
|
||||||
|
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
|
||||||
this.cookies[url.host] = cookies
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
client.addInterceptor(HttpLoggingInterceptor(
|
|
||||||
object : HttpLoggingInterceptor.Logger {
|
|
||||||
val moshi = Moshi.Builder()
|
|
||||||
.build()
|
|
||||||
.adapter(Any::class.java)
|
|
||||||
.indent(" ")
|
|
||||||
|
|
||||||
override fun log(message: String) {
|
|
||||||
val prettyMessage = try {
|
|
||||||
val json = JsonReader.of(Buffer().writeUtf8(message))
|
|
||||||
moshi.toJson(json.readJsonValue())
|
|
||||||
} catch (ignored: Exception) {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
HttpLoggingInterceptor.Logger.DEFAULT.log(prettyMessage)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.apply {
|
|
||||||
level = HttpLoggingInterceptor.Level.BODY
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
client.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
single<PiHoleApiService> {
|
|
||||||
OkHttpPiHoleApiService(get(), get())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
<resources>
|
|
||||||
<string name="app_name">PiHoleClient</string>
|
|
||||||
</resources>
|
|
|
@ -1,2 +1,2 @@
|
||||||
include ':android', ':piholeclient', ':shared'
|
include ':android', ':shared'
|
||||||
rootProject.name='Pi-helper'
|
rootProject.name='Pi-helper'
|
||||||
|
|
|
@ -41,6 +41,7 @@ kotlin {
|
||||||
val androidMain by getting {
|
val androidMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.google.android.material:material:1.2.0")
|
implementation("com.google.android.material:material:1.2.0")
|
||||||
|
implementation("io.ktor:ktor-client-serialization-jvm:$ktor_version")
|
||||||
api("io.ktor:ktor-client-android:$ktor_version")
|
api("io.ktor:ktor-client-android:$ktor_version")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +54,7 @@ kotlin {
|
||||||
val iosMain by getting {
|
val iosMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("io.ktor:ktor-client-ios:$ktor_version")
|
implementation("io.ktor:ktor-client-ios:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-client-serialization-native:$ktor_version")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val iosTest by getting
|
val iosTest by getting
|
||||||
|
|
|
@ -2,5 +2,15 @@ package com.wbrawner.pihelper.shared
|
||||||
|
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.android.*
|
import io.ktor.client.engine.android.*
|
||||||
|
import io.ktor.client.features.json.JsonFeature
|
||||||
|
import io.ktor.client.features.json.serializer.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
actual fun httpClient(): HttpClient = HttpClient(Android)
|
actual fun httpClient(): HttpClient = HttpClient(Android) {
|
||||||
|
install(JsonFeature) {
|
||||||
|
serializer = KotlinxSerializer(Json {
|
||||||
|
isLenient = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,22 +26,23 @@ data class Summary(
|
||||||
@SerialName("queries_cached")
|
@SerialName("queries_cached")
|
||||||
val queriesCached: String,
|
val queriesCached: String,
|
||||||
@SerialName("no_data_replies")
|
@SerialName("no_data_replies")
|
||||||
val noDataReplies: String?,
|
val noDataReplies: String? = null,
|
||||||
@SerialName("nx_domain_replies")
|
@SerialName("nx_domain_replies")
|
||||||
val nxDomainReplies: String?,
|
val nxDomainReplies: String? = null,
|
||||||
@SerialName("cname_replies")
|
@SerialName("cname_replies")
|
||||||
val cnameReplies: String?,
|
val cnameReplies: String? = null,
|
||||||
@SerialName("in_replies")
|
@SerialName("in_replies")
|
||||||
val ipReplies: String?,
|
val ipReplies: String? = null,
|
||||||
@SerialName("privacy_level")
|
@SerialName("privacy_level")
|
||||||
val privacyLevel: String,
|
val privacyLevel: String,
|
||||||
override val status: Status,
|
override val status: Status,
|
||||||
@SerialName("gravity_last_updated")
|
@SerialName("gravity_last_updated")
|
||||||
val gravity: Gravity?,
|
val gravity: Gravity? = null,
|
||||||
val type: String?,
|
val type: String? = null,
|
||||||
val version: Int?
|
val version: Int? = null
|
||||||
) : StatusProvider
|
) : StatusProvider
|
||||||
|
|
||||||
|
@Serializable
|
||||||
enum class Status {
|
enum class Status {
|
||||||
@SerialName("enabled")
|
@SerialName("enabled")
|
||||||
ENABLED,
|
ENABLED,
|
||||||
|
|
Loading…
Reference in a new issue