Compare commits
4 commits
Author | SHA1 | Date | |
---|---|---|---|
b4c7400a9b | |||
f155bdfa42 | |||
0b336610df | |||
414ac50234 |
|
@ -1 +1 @@
|
||||||
Pi-Helper
|
Pi-helper
|
|
@ -1,6 +1,13 @@
|
||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
|
<value>
|
||||||
|
<package name="java.util" withSubpackages="false" static="false" />
|
||||||
|
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
|
||||||
|
<package name="io.ktor" withSubpackages="true" static="false" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
|
|
6
.idea/compiler.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="1.8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -10,11 +10,12 @@
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/android" />
|
||||||
<option value="$PROJECT_DIR$/piholeclient" />
|
<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>
|
||||||
|
|
35
.idea/jarRepositories.xml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="BintrayJCenter" />
|
||||||
|
<option name="name" value="BintrayJCenter" />
|
||||||
|
<option name="url" value="https://jcenter.bintray.com/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="Google" />
|
||||||
|
<option name="name" value="Google" />
|
||||||
|
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="MavenRepo" />
|
||||||
|
<option name="name" value="MavenRepo" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="Gradle Central Plugin Repository" />
|
||||||
|
<option name="name" value="Gradle Central Plugin Repository" />
|
||||||
|
<option name="url" value="https://plugins.gradle.org/m2" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
9
.idea/kotlinScripting.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinScriptingSettings">
|
||||||
|
<scriptDefinition className="org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate" definitionName="KotlinBuildScript">
|
||||||
|
<order>2147483647</order>
|
||||||
|
<autoReloadConfigurations>true</autoReloadConfigurations>
|
||||||
|
</scriptDefinition>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -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_1_8" 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">
|
||||||
|
|
0
app/.gitignore → android/.gitignore
vendored
|
@ -50,7 +50,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':piholeclient')
|
implementation project(':shared')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||||
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
|
@ -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">
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
@ -5,8 +5,9 @@ import android.util.Log
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.wbrawner.piholeclient.PiHoleApiService
|
import com.wbrawner.pihelper.shared.KtorPiHoleApiService
|
||||||
import com.wbrawner.piholeclient.VersionResponse
|
import com.wbrawner.pihelper.shared.PiHoleApiService
|
||||||
|
import com.wbrawner.pihelper.shared.VersionResponse
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.net.ConnectException
|
import java.net.ConnectException
|
|
@ -20,7 +20,7 @@ import androidx.core.text.set
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.wbrawner.piholeclient.Status
|
import com.wbrawner.pihelper.shared.Status
|
||||||
import kotlinx.android.synthetic.main.fragment_main.*
|
import kotlinx.android.synthetic.main.fragment_main.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
|
@ -2,7 +2,6 @@ package com.wbrawner.pihelper
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.wbrawner.piholeclient.piHoleClientModule
|
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.android.ext.koin.androidLogger
|
import org.koin.android.ext.koin.androidLogger
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
|
@ -15,7 +14,6 @@ class PiHelperApplication: Application() {
|
||||||
androidLogger()
|
androidLogger()
|
||||||
androidContext(this@PiHelperApplication)
|
androidContext(this@PiHelperApplication)
|
||||||
modules(listOf(
|
modules(listOf(
|
||||||
piHoleClientModule,
|
|
||||||
piHelperModule
|
piHelperModule
|
||||||
))
|
))
|
||||||
}
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
package com.wbrawner.pihelper
|
package com.wbrawner.pihelper
|
||||||
|
|
||||||
import androidx.security.crypto.EncryptedSharedPreferences
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import com.wbrawner.piholeclient.NAME_BASE_URL
|
import com.wbrawner.pihelper.shared.KtorPiHoleApiService
|
||||||
|
import com.wbrawner.pihelper.shared.PiHoleApiService
|
||||||
|
import com.wbrawner.pihelper.shared.httpClient
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
const val ENCRYPTED_SHARED_PREFS_FILE_NAME = "pihelper.prefs"
|
const val ENCRYPTED_SHARED_PREFS_FILE_NAME = "pihelper.prefs"
|
||||||
|
const val NAME_BASE_URL = "baseUrl"
|
||||||
|
|
||||||
val piHelperModule = module {
|
val piHelperModule = module {
|
||||||
single {
|
single {
|
||||||
|
@ -30,4 +33,8 @@ val piHelperModule = module {
|
||||||
single(named(NAME_BASE_URL)) {
|
single(named(NAME_BASE_URL)) {
|
||||||
get<EncryptedSharedPreferences>().getString(KEY_BASE_URL, "")
|
get<EncryptedSharedPreferences>().getString(KEY_BASE_URL, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single<PiHoleApiService> {
|
||||||
|
KtorPiHoleApiService(httpClient())
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,9 +2,9 @@ package com.wbrawner.pihelper
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.wbrawner.piholeclient.PiHoleApiService
|
import com.wbrawner.pihelper.shared.PiHoleApiService
|
||||||
import com.wbrawner.piholeclient.Status
|
import com.wbrawner.pihelper.shared.Status
|
||||||
import com.wbrawner.piholeclient.StatusProvider
|
import com.wbrawner.pihelper.shared.StatusProvider
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
Before Width: | Height: | Size: 561 B After Width: | Height: | Size: 561 B |
Before Width: | Height: | Size: 395 B After Width: | Height: | Size: 395 B |
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 741 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -1,14 +1,15 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.61'
|
ext.kotlin_version = '1.4.20'
|
||||||
ext.coroutines_version = '1.3.2'
|
ext.coroutines_version = '1.3.2'
|
||||||
ext.koin_version = '2.0.1'
|
ext.koin_version = '2.0.1'
|
||||||
ext.okhttp_version = '4.2.2'
|
ext.okhttp_version = '4.2.2'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,3 +19,6 @@ android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
kotlin.incremental.multiplatform=true
|
||||||
|
kotlin.mpp.stability.nowarn=true
|
||||||
|
xcodeproj=../Pi-Helper
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||||
|
|
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>;
|
|
||||||
}
|
|
|
@ -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,146 +0,0 @@
|
||||||
package com.wbrawner.piholeclient
|
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
interface PiHoleApiService {
|
|
||||||
var baseUrl: String?
|
|
||||||
var apiKey: String?
|
|
||||||
|
|
||||||
suspend fun getSummary(
|
|
||||||
version: Boolean = false,
|
|
||||||
type: Boolean = false
|
|
||||||
): Summary
|
|
||||||
|
|
||||||
suspend fun getVersion(): VersionResponse
|
|
||||||
|
|
||||||
suspend fun getTopItems(): TopItemsResponse
|
|
||||||
|
|
||||||
suspend fun enable(): StatusResponse
|
|
||||||
|
|
||||||
suspend fun disable(duration: Long? = null): StatusResponse
|
|
||||||
|
|
||||||
/**
|
|
||||||
@Query("overTimeData10mins") overTimeData10mins: Boolean = true,
|
|
||||||
@Query("topItems") topItems: Int? = null,
|
|
||||||
@Query("topClients") topClients: Int? = null,
|
|
||||||
@Query("getForwardDestinations") getForwardDestinations: Boolean = true,
|
|
||||||
@Query("getQueryTypes") getQueryTypes: Boolean = true,
|
|
||||||
@Query("getAllQueries") getAllQueries: Boolean = true
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// suspend fun login(password: String): Response<String>
|
|
||||||
//
|
|
||||||
// @GET("/admin/scripts/pi-hole/php/api_token.php")
|
|
||||||
// suspend fun apiKey(phpSession: String): Response<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
const val BASE_PATH = "/admin/api.php"
|
|
||||||
|
|
||||||
class OkHttpPiHoleApiService(
|
|
||||||
private val okHttpClient: OkHttpClient,
|
|
||||||
private val moshi: Moshi
|
|
||||||
) : PiHoleApiService {
|
|
||||||
override var baseUrl: String? = null
|
|
||||||
@Synchronized
|
|
||||||
get
|
|
||||||
@Synchronized
|
|
||||||
set
|
|
||||||
override var apiKey: String? = null
|
|
||||||
@Synchronized
|
|
||||||
get
|
|
||||||
@Synchronized
|
|
||||||
set
|
|
||||||
|
|
||||||
private val urlBuilder: HttpUrl.Builder
|
|
||||||
get() {
|
|
||||||
val host = baseUrl ?: throw IllegalStateException("No base URL defined")
|
|
||||||
return HttpUrl.Builder()
|
|
||||||
.scheme("http")
|
|
||||||
.host(host)
|
|
||||||
.addPathSegments(BASE_PATH)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getSummary(version: Boolean, type: Boolean): Summary {
|
|
||||||
val url = urlBuilder
|
|
||||||
.addQueryParameter("summary", "")
|
|
||||||
if (version) {
|
|
||||||
url.addQueryParameter("version", "")
|
|
||||||
}
|
|
||||||
if (type) {
|
|
||||||
url.addQueryParameter("type", "")
|
|
||||||
}
|
|
||||||
val request = Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url.build())
|
|
||||||
return sendRequest(request.build(), Summary::class)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getVersion(): VersionResponse {
|
|
||||||
val url = urlBuilder
|
|
||||||
.addQueryParameter("version", "")
|
|
||||||
val request = Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url.build())
|
|
||||||
return sendRequest(request.build(), VersionResponse::class)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getTopItems(): TopItemsResponse {
|
|
||||||
val apiToken = this.apiKey ?: throw java.lang.IllegalStateException("No API Token provided")
|
|
||||||
val url = urlBuilder
|
|
||||||
.addQueryParameter("topItems", "25")
|
|
||||||
.addQueryParameter("auth", apiToken)
|
|
||||||
val request = Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url.build())
|
|
||||||
return sendRequest(request.build(), TopItemsResponse::class)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun enable(): StatusResponse {
|
|
||||||
val apiToken = this.apiKey ?: throw java.lang.IllegalStateException("No API Token provided")
|
|
||||||
val url = urlBuilder
|
|
||||||
.addQueryParameter("enable", "")
|
|
||||||
.addQueryParameter("auth", apiToken)
|
|
||||||
val request = Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url.build())
|
|
||||||
return sendRequest(request.build(), StatusResponse::class)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun disable(duration: Long?): StatusResponse {
|
|
||||||
val apiToken = this.apiKey ?: throw java.lang.IllegalStateException("No API Token provided")
|
|
||||||
val url = urlBuilder
|
|
||||||
.addQueryParameter("disable", duration?.toString()?: "")
|
|
||||||
.addQueryParameter("auth", apiToken)
|
|
||||||
val request = Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url.build())
|
|
||||||
return sendRequest(request.build(), StatusResponse::class)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun <T : Any> sendRequest(request: Request, responseType: KClass<T>?): T? {
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val response = okHttpClient.newCall(request).execute()
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
when (responseType) {
|
|
||||||
null -> null
|
|
||||||
String::class -> response.body?.string() as T
|
|
||||||
else -> response.body?.let {
|
|
||||||
moshi
|
|
||||||
.adapter(responseType.javaObjectType)
|
|
||||||
.fromJson(it.source())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +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
|
|
||||||
|
|
||||||
const val NAME_BASE_URL = "baseUrl"
|
|
||||||
|
|
||||||
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,85 +0,0 @@
|
||||||
package com.wbrawner.piholeclient
|
|
||||||
|
|
||||||
import androidx.annotation.Keep
|
|
||||||
import com.squareup.moshi.Json
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class Summary(
|
|
||||||
@Json(name = "domains_being_blocked")
|
|
||||||
val domainsBeingBlocked: String,
|
|
||||||
@Json(name = "dns_queries_today")
|
|
||||||
val dnsQueriesToday: String,
|
|
||||||
@Json(name = "ads_blocked_today")
|
|
||||||
val adsBlockedToday: String,
|
|
||||||
@Json(name = "ads_percentage_today")
|
|
||||||
val adsPercentageToday: String,
|
|
||||||
@Json(name = "unique_domains")
|
|
||||||
val uniqueDomains: String,
|
|
||||||
@Json(name = "queries_forwarded")
|
|
||||||
val queriesForwarded: String,
|
|
||||||
@Json(name = "clients_ever_seen")
|
|
||||||
val clientsEverSeen: String,
|
|
||||||
@Json(name = "unique_clients")
|
|
||||||
val uniqueClients: String,
|
|
||||||
@Json(name = "dns_queries_all_types")
|
|
||||||
val dnsQueriesAllTypes: String,
|
|
||||||
@Json(name = "queries_cached")
|
|
||||||
val queriesCached: String,
|
|
||||||
@Json(name = "no_data_replies")
|
|
||||||
val noDataReplies: String?,
|
|
||||||
@Json(name = "nx_domain_replies")
|
|
||||||
val nxDomainReplies: String?,
|
|
||||||
@Json(name = "cname_replies")
|
|
||||||
val cnameReplies: String?,
|
|
||||||
@Json(name = "in_replies")
|
|
||||||
val ipReplies: String?,
|
|
||||||
@Json(name = "privacy_level")
|
|
||||||
val privacyLevel: String,
|
|
||||||
override val status: Status,
|
|
||||||
@Json(name = "gravity_last_updated")
|
|
||||||
val gravity: Gravity?,
|
|
||||||
val type: String?,
|
|
||||||
val version: Int?
|
|
||||||
) : StatusProvider
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
enum class Status {
|
|
||||||
@Json(name = "enabled")
|
|
||||||
ENABLED,
|
|
||||||
@Json(name = "disabled")
|
|
||||||
DISABLED
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class Gravity(
|
|
||||||
@Json(name = "file_exists")
|
|
||||||
val fileExists: Boolean,
|
|
||||||
val absolute: Int,
|
|
||||||
val relative: Relative
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class Relative(
|
|
||||||
val days: String,
|
|
||||||
val hours: String,
|
|
||||||
val minutes: String
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class VersionResponse(val version: Int)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class TopItemsResponse(
|
|
||||||
@Json(name = "top_queries") val topQueries: Map<String, String>,
|
|
||||||
@Json(name = "top_ads") val topAds: Map<String, Double>
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class StatusResponse(
|
|
||||||
override val status: Status
|
|
||||||
) : StatusProvider
|
|
||||||
|
|
||||||
interface StatusProvider {
|
|
||||||
val status: Status
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
<resources>
|
|
||||||
<string name="app_name">PiHoleClient</string>
|
|
||||||
</resources>
|
|
|
@ -1,2 +1,2 @@
|
||||||
include ':app', ':piholeclient'
|
include ':android', ':shared'
|
||||||
rootProject.name='Pi-helper'
|
rootProject.name='Pi-helper'
|
||||||
|
|
1
shared/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
88
shared/build.gradle.kts
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
id("com.android.library")
|
||||||
|
id("kotlin-android-extensions")
|
||||||
|
kotlin("plugin.serialization") version "1.4.10"
|
||||||
|
}
|
||||||
|
group = "com.wbrawner.pihelper"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
kotlin {
|
||||||
|
android()
|
||||||
|
ios {
|
||||||
|
binaries {
|
||||||
|
framework {
|
||||||
|
baseName = "pihelper"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation("io.ktor:ktor-client-core:1.4.2")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
|
||||||
|
implementation("io.ktor:ktor-client-serialization:1.4.2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-common"))
|
||||||
|
implementation(kotlin("test-annotations-common"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val androidMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation("com.google.android.material:material:1.2.0")
|
||||||
|
api("io.ktor:ktor-client-android:1.4.2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val androidTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-junit"))
|
||||||
|
implementation("junit:junit:4.12")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val iosMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation("io.ktor:ktor-client-ios:1.4.2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val iosTest by getting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
android {
|
||||||
|
compileSdkVersion(30)
|
||||||
|
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion(23)
|
||||||
|
targetSdkVersion(30)
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
getByName("release") {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val packForXcode by tasks.creating(Sync::class) {
|
||||||
|
group = "build"
|
||||||
|
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
||||||
|
val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
|
||||||
|
val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
|
||||||
|
val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
|
||||||
|
inputs.property("mode", mode)
|
||||||
|
dependsOn(framework.linkTask)
|
||||||
|
val targetDir = File(buildDir, "xcode-frameworks")
|
||||||
|
from({ framework.outputDirectory })
|
||||||
|
into(targetDir)
|
||||||
|
}
|
||||||
|
tasks.getByName("build").dependsOn(packForXcode)
|
|
@ -1,6 +1,6 @@
|
||||||
# Add project specific ProGuard rules here.
|
# Add project specific ProGuard rules here.
|
||||||
# You can control the set of applied configuration files using the
|
# You can control the set of applied configuration files using the
|
||||||
# proguardFiles setting in build.gradle.
|
# proguardFiles setting in build.gradle.kts.
|
||||||
#
|
#
|
||||||
# For more details, see
|
# For more details, see
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
@ -19,5 +19,3 @@
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
-keepclassmembers @com.squareup.moshi.JsonClass class *
|
|
2
shared/src/androidMain/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.wbrawner.pihelper.android"/>
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.wbrawner.pihelper.shared
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
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) {
|
||||||
|
install(JsonFeature) {
|
||||||
|
serializer = KotlinxSerializer(Json {
|
||||||
|
isLenient = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package com.wbrawner.pihelper.shared
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.jvm.Synchronized
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
interface PiHoleApiService {
|
||||||
|
var baseUrl: String?
|
||||||
|
var apiKey: String?
|
||||||
|
|
||||||
|
suspend fun getSummary(
|
||||||
|
version: Boolean = false,
|
||||||
|
type: Boolean = false
|
||||||
|
): Summary
|
||||||
|
|
||||||
|
suspend fun getVersion(): VersionResponse
|
||||||
|
|
||||||
|
suspend fun getTopItems(): TopItemsResponse
|
||||||
|
|
||||||
|
suspend fun enable(): StatusResponse
|
||||||
|
|
||||||
|
suspend fun disable(duration: Long? = null): StatusResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
@Query("overTimeData10mins") overTimeData10mins: Boolean = true,
|
||||||
|
@Query("topItems") topItems: Int? = null,
|
||||||
|
@Query("topClients") topClients: Int? = null,
|
||||||
|
@Query("getForwardDestinations") getForwardDestinations: Boolean = true,
|
||||||
|
@Query("getQueryTypes") getQueryTypes: Boolean = true,
|
||||||
|
@Query("getAllQueries") getAllQueries: Boolean = true
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// suspend fun login(password: String): Response<String>
|
||||||
|
//
|
||||||
|
// @GET("/admin/scripts/pi-hole/php/api_token.php")
|
||||||
|
// suspend fun apiKey(phpSession: String): Response<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
const val BASE_PATH = "/admin/api.php"
|
||||||
|
|
||||||
|
expect fun httpClient(): HttpClient
|
||||||
|
|
||||||
|
class KtorPiHoleApiService(private val httpClient: HttpClient = httpClient()) : PiHoleApiService {
|
||||||
|
override var baseUrl: String? = null
|
||||||
|
@Synchronized
|
||||||
|
get
|
||||||
|
@Synchronized
|
||||||
|
set
|
||||||
|
override var apiKey: String? = null
|
||||||
|
@Synchronized
|
||||||
|
get
|
||||||
|
@Synchronized
|
||||||
|
set
|
||||||
|
|
||||||
|
private fun urlBuilder(configuration: URLBuilder.() -> Unit): Url {
|
||||||
|
val host = baseUrl ?: throw IllegalStateException("No base URL defined")
|
||||||
|
return URLBuilder(
|
||||||
|
protocol = URLProtocol.HTTP,
|
||||||
|
host = host,
|
||||||
|
encodedPath = BASE_PATH
|
||||||
|
).run {
|
||||||
|
configuration()
|
||||||
|
build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getSummary(version: Boolean, type: Boolean): Summary = httpClient.get {
|
||||||
|
url(urlBuilder {
|
||||||
|
parameters["summary"] = ""
|
||||||
|
if (type) {
|
||||||
|
parameters["type"] = ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getVersion(): VersionResponse = httpClient.get {
|
||||||
|
url(urlBuilder {
|
||||||
|
parameters["version"] = ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTopItems(): TopItemsResponse = httpClient.get {
|
||||||
|
val apiToken = apiKey ?: throw Error("No API Token provided")
|
||||||
|
url(urlBuilder {
|
||||||
|
parameters["topItems"] = "25"
|
||||||
|
parameters["auth"] = apiToken
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun enable(): StatusResponse = httpClient.get {
|
||||||
|
val apiToken = apiKey ?: throw Error("No API Token provided")
|
||||||
|
url(urlBuilder {
|
||||||
|
parameters["enable"] = ""
|
||||||
|
parameters["auth"] = apiToken
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun disable(duration: Long?): StatusResponse = httpClient.get {
|
||||||
|
val apiToken = apiKey ?: throw Error("No API Token provided")
|
||||||
|
url(urlBuilder {
|
||||||
|
parameters["disable"] = duration?.toString() ?: ""
|
||||||
|
parameters["auth"] = apiToken
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.wbrawner.pihelper.shared
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Summary(
|
||||||
|
@SerialName("domains_being_blocked")
|
||||||
|
val domainsBeingBlocked: String,
|
||||||
|
@SerialName("dns_queries_today")
|
||||||
|
val dnsQueriesToday: String,
|
||||||
|
@SerialName("ads_blocked_today")
|
||||||
|
val adsBlockedToday: String,
|
||||||
|
@SerialName("ads_percentage_today")
|
||||||
|
val adsPercentageToday: String,
|
||||||
|
@SerialName("unique_domains")
|
||||||
|
val uniqueDomains: String,
|
||||||
|
@SerialName("queries_forwarded")
|
||||||
|
val queriesForwarded: String,
|
||||||
|
@SerialName("clients_ever_seen")
|
||||||
|
val clientsEverSeen: String,
|
||||||
|
@SerialName("unique_clients")
|
||||||
|
val uniqueClients: String,
|
||||||
|
@SerialName("dns_queries_all_types")
|
||||||
|
val dnsQueriesAllTypes: String,
|
||||||
|
@SerialName("queries_cached")
|
||||||
|
val queriesCached: String,
|
||||||
|
@SerialName("no_data_replies")
|
||||||
|
val noDataReplies: String? = null,
|
||||||
|
@SerialName("nx_domain_replies")
|
||||||
|
val nxDomainReplies: String? = null,
|
||||||
|
@SerialName("cname_replies")
|
||||||
|
val cnameReplies: String? = null,
|
||||||
|
@SerialName("in_replies")
|
||||||
|
val ipReplies: String? = null,
|
||||||
|
@SerialName("privacy_level")
|
||||||
|
val privacyLevel: String,
|
||||||
|
override val status: Status,
|
||||||
|
@SerialName("gravity_last_updated")
|
||||||
|
val gravity: Gravity? = null,
|
||||||
|
val type: String? = null,
|
||||||
|
val version: Int? = null
|
||||||
|
) : StatusProvider
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class Status {
|
||||||
|
@SerialName("enabled")
|
||||||
|
ENABLED,
|
||||||
|
@SerialName("disabled")
|
||||||
|
DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Gravity(
|
||||||
|
@SerialName("file_exists")
|
||||||
|
val fileExists: Boolean,
|
||||||
|
val absolute: Int,
|
||||||
|
val relative: Relative
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Relative(
|
||||||
|
val days: String,
|
||||||
|
val hours: String,
|
||||||
|
val minutes: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class VersionResponse(val version: Int)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TopItemsResponse(
|
||||||
|
@SerialName("top_queries") val topQueries: Map<String, String>,
|
||||||
|
@SerialName("top_ads") val topAds: Map<String, Double>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StatusResponse(
|
||||||
|
override val status: Status
|
||||||
|
) : StatusProvider
|
||||||
|
|
||||||
|
interface StatusProvider {
|
||||||
|
val status: Status
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.wbrawner.pihelper.shared
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.ios.*
|
||||||
|
|
||||||
|
actual fun httpClient(): HttpClient = HttpClient(Ios)
|