Replace Koin with Dagger Hilt

This commit is contained in:
William Brawner 2021-06-03 19:47:06 -06:00
parent bdacaaae34
commit 3f98551b9d
15 changed files with 102 additions and 70 deletions

View file

@ -5,6 +5,8 @@ import java.io.FileNotFoundException
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
}
val keystoreProperties = Properties()
@ -64,7 +66,8 @@ android {
dependencies {
implementation(project(":piholeclient"))
implementation(libs.bundles.coroutines)
implementation("io.insert-koin:koin-androidx-viewmodel:${libs.versions.koin.get()}")
implementation(libs.hilt.android.core)
kapt(libs.hilt.android.kapt)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.material)

View file

@ -7,17 +7,20 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.wbrawner.piholeclient.PiHoleApiService
import com.wbrawner.piholeclient.VersionResponse
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.ConnectException
import java.net.SocketTimeoutException
import javax.inject.Inject
const val KEY_BASE_URL = "baseUrl"
const val KEY_API_KEY = "apiKey"
const val IP_MIN = 0
const val IP_MAX = 255
class AddPiHelperViewModel(
@HiltViewModel
class AddPiHelperViewModel @Inject constructor(
private val sharedPreferences: SharedPreferences,
private val apiService: PiHoleApiService
) : ViewModel() {

View file

@ -10,15 +10,17 @@ import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import com.wbrawner.pihelper.databinding.FragmentAddPiHoleBinding
import org.koin.android.ext.android.inject
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AddPiHoleFragment : Fragment() {
private val viewModel: AddPiHelperViewModel by inject()
private val viewModel: AddPiHelperViewModel by activityViewModels()
private var _binding: FragmentAddPiHoleBinding? = null
private val binding get() = _binding!!

View file

@ -13,13 +13,15 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.wbrawner.pihelper.MainActivity.Companion.ACTION_FORGET_PIHOLE
import com.wbrawner.pihelper.databinding.FragmentInfoBinding
import org.koin.android.ext.android.inject
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class InfoFragment : Fragment() {
private val viewModel: AddPiHelperViewModel by inject()
private val viewModel: AddPiHelperViewModel by activityViewModels()
private var _binding: FragmentInfoBinding? = null
private val binding get() = _binding!!

View file

@ -3,17 +3,20 @@ package com.wbrawner.pihelper
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import androidx.navigation.findNavController
import com.wbrawner.pihelper.MainFragment.Companion.ACTION_DISABLE
import com.wbrawner.pihelper.MainFragment.Companion.ACTION_ENABLE
import com.wbrawner.pihelper.MainFragment.Companion.EXTRA_DURATION
import com.wbrawner.pihelper.databinding.ActivityMainBinding
import org.koin.android.ext.android.inject
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val addPiHoleViewModel: AddPiHelperViewModel by inject()
private val addPiHoleViewModel: AddPiHelperViewModel by viewModels()
private val navController: NavController by lazy {
findNavController(R.id.content_main)
}
@ -22,7 +25,14 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
window.setBackgroundDrawable(ColorDrawable(getColor(R.color.colorSurface)))
window.setBackgroundDrawable(
ColorDrawable(
ContextCompat.getColor(
this,
R.color.colorSurface
)
)
)
val args = when (intent.action) {
ACTION_ENABLE -> {
if (addPiHoleViewModel.apiKey == null) {

View file

@ -16,16 +16,18 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.getColor
import androidx.core.text.set
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.wbrawner.pihelper.databinding.DialogDisableCustomTimeBinding
import com.wbrawner.pihelper.databinding.FragmentMainBinding
import com.wbrawner.piholeclient.Status
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class MainFragment : Fragment() {
private val viewModel: PiHelperViewModel by inject()
private val viewModel: PiHelperViewModel by activityViewModels()
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!

View file

@ -1,23 +1,7 @@
package com.wbrawner.pihelper
import android.app.Application
import android.content.Context
import com.wbrawner.piholeclient.piHoleClientModule
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import dagger.hilt.android.HiltAndroidApp
@Suppress("unused")
class PiHelperApplication: Application() {
override fun onCreate() {
super.onCreate()
startKoin{
androidLogger()
androidContext(this@PiHelperApplication)
modules(listOf(
piHoleClientModule,
piHelperModule
))
}
}
}
@HiltAndroidApp
class PiHelperApplication : Application()

View file

@ -1,33 +1,36 @@
package com.wbrawner.pihelper
import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import com.wbrawner.piholeclient.NAME_BASE_URL
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Named
import javax.inject.Singleton
const val ENCRYPTED_SHARED_PREFS_FILE_NAME = "pihelper.prefs"
val piHelperModule = module {
single {
@Module
@InstallIn(SingletonComponent::class)
object PiHelperModule {
@Provides
@Singleton
fun providesSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
EncryptedSharedPreferences.create(
ENCRYPTED_SHARED_PREFS_FILE_NAME,
"pihelper",
get(),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
viewModel {
AddPiHelperViewModel(get(), get())
}
viewModel {
PiHelperViewModel(get())
}
single(named(NAME_BASE_URL)) {
get<EncryptedSharedPreferences>().getString(KEY_BASE_URL, "")
}
@Provides
@Singleton
@Named(NAME_BASE_URL)
fun providesBaseUrl(sharedPreferences: SharedPreferences) = sharedPreferences
.getString(KEY_BASE_URL, "")
}

View file

@ -5,11 +5,14 @@ import androidx.lifecycle.ViewModel
import com.wbrawner.piholeclient.PiHoleApiService
import com.wbrawner.piholeclient.Status
import com.wbrawner.piholeclient.StatusProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import javax.inject.Inject
import kotlin.coroutines.coroutineContext
class PiHelperViewModel(
@HiltViewModel
class PiHelperViewModel @Inject constructor(
private val apiService: PiHoleApiService
) : ViewModel() {
val status = MutableLiveData<Status>()

View file

@ -10,15 +10,17 @@ import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionInflater
import com.wbrawner.pihelper.databinding.FragmentRetrieveApiKeyBinding
import org.koin.android.ext.android.inject
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class RetrieveApiKeyFragment : Fragment() {
private val viewModel: AddPiHelperViewModel by inject()
private val viewModel: AddPiHelperViewModel by activityViewModels()
private var _binding: FragmentRetrieveApiKeyBinding? = null
private val binding get() = _binding!!
@ -50,7 +52,8 @@ class RetrieveApiKeyFragment : Fragment() {
viewModel.authenticateWithPassword(binding.password.text.toString())
} catch (ignored: Exception) {
Log.e("Pi-helper", "Failed to authenticate with password", ignored)
binding.password.error = "Failed to authenticate with given password. Please verify " +
binding.password.error =
"Failed to authenticate with given password. Please verify " +
"you've entered it correctly and try again."
showProgress(false)
}

View file

@ -15,6 +15,7 @@ import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
@ -22,15 +23,16 @@ import androidx.navigation.fragment.findNavController
import androidx.transition.Transition
import androidx.transition.TransitionInflater
import com.wbrawner.pihelper.databinding.FragmentScanNetworkBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import java.net.Inet4Address
@AndroidEntryPoint
class ScanNetworkFragment : Fragment() {
private val viewModel: AddPiHelperViewModel by inject()
private val viewModel: AddPiHelperViewModel by activityViewModels()
private var _binding: FragmentScanNetworkBinding? = null
private val binding get() = _binding!!

View file

@ -3,9 +3,11 @@ buildscript {
google()
mavenCentral()
}
val hiltVersion by extra("2.36")
val kotlinVersion by extra("1.4.32")
dependencies {
classpath("com.android.tools.build:gradle:7.0.0-beta03")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("com.google.dagger:hilt-android-gradle-plugin:$hiltVersion")
}
}

View file

@ -3,13 +3,13 @@ androidx-core = "1.3.2"
androidx-appcompat = "1.2.0"
coroutines = "1.4.3"
espresso = "3.3.0"
koin = "2.2.3"
hilt-android = "2.36"
kotlin = "1.4.32"
lifecycle = "2.2.0"
material = "1.3.0"
maxSdk = "30"
moshi = "1.9.2"
minSdk = "21"
minSdk = "23"
navigation = "2.3.2"
okhttp = "4.2.2"
versionCode = "1"
@ -18,6 +18,8 @@ versionName = "1.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
hilt-android-core = { module = "com.google.dagger:hilt-android", version.ref = "hilt-android" }
hilt-android-kapt = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt-android" }
material = { module = "com.google.android.material:material", version.ref = "material" }
moshi-core = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }

View file

@ -2,6 +2,7 @@ plugins {
id("com.android.library")
id("kotlin-android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
}
android {
@ -30,14 +31,15 @@ android {
}
dependencies {
implementation("io.insert-koin:koin-core:${libs.versions.koin.get()}")
implementation("com.squareup.okhttp3:okhttp:${libs.versions.okhttp.get()}")
api("com.squareup.okhttp3:okhttp:${libs.versions.okhttp.get()}")
implementation("com.squareup.okhttp3:logging-interceptor:${libs.versions.okhttp.get()}")
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.bundles.coroutines)
implementation(libs.hilt.android.core)
kapt(libs.hilt.android.kapt)
implementation(libs.kotlin.reflect)
implementation(libs.moshi.core)
api(libs.moshi.core)
kapt(libs.moshi.codegen)
testImplementation(libs.junit)
}

View file

@ -2,23 +2,31 @@ package com.wbrawner.piholeclient
import com.squareup.moshi.JsonReader
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
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
import javax.inject.Singleton
const val NAME_BASE_URL = "baseUrl"
val piHoleClientModule = module {
single {
Moshi.Builder().build()
}
@Module
@InstallIn(SingletonComponent::class)
object PiHoleClientModule {
@Provides
@Singleton
fun providesMoshi(): Moshi = Moshi.Builder().build()
single {
@Provides
@Singleton
fun providesOkHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
.connectTimeout(500, TimeUnit.MILLISECONDS)
.cookieJar(object : CookieJar {
@ -53,10 +61,11 @@ val piHoleClientModule = module {
}
)
}
client.build()
return client.build()
}
single<PiHoleApiService> {
OkHttpPiHoleApiService(get(), get())
}
@Provides
@Singleton
fun providesPiHoleApiService(okHttpClient: OkHttpClient, moshi: Moshi): PiHoleApiService =
OkHttpPiHoleApiService(okHttpClient, moshi)
}