Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mvvm livedata koin #6

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[*.{kt,kts}]
[*.{kt,kts,xml}]
indent_size=4
insert_final_newline=true
max_line_length=120
disabled_rules=no-wildcard-imports,experimental:annotation, import-ordering
disabled_rules=no-wildcard-imports,experimental:annotation, import-ordering, trailing-comma-on-declaration-site, wrapping, experimental:property-naming, experimental:function-signature
10 changes: 10 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 52 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
id(Plugins.android_application)
kotlin(Plugins.kotlin_android)
id(Plugins.kotlin_parcelize)
id(Plugins.detekt).version(Versions.detekt)
id(Plugins.ksp).version(Versions.ksp)
}

buildscript {
apply(from = "ktlint.gradle.kts")
}

android {
namespace = "com.sun.structure_android"
namespace = "com.sun.android"
compileSdk = AppConfigs.compile_sdk_version

defaultConfig {
Expand All @@ -18,6 +22,9 @@ android {
targetSdk = AppConfigs.target_sdk_version
versionCode = AppConfigs.version_code
versionName = AppConfigs.version_name

buildConfigField("String", "API_KEY", gradleLocalProperties(rootDir).getProperty("api_key"))
buildConfigField("String", "BASE_URL_IMAGE", gradleLocalProperties(rootDir).getProperty("base_url_image"))
}

@Suppress("UnstableApiUsage")
Expand All @@ -27,9 +34,11 @@ android {
create("dev") {
applicationIdSuffix = ".dev"
resValue("string", "app_name", "Structure-Dev")
buildConfigField("String", "BASE_URL", "\"https://api.themoviedb.org/3/\"")
}
create("prd") {
resValue("string", "app_name", "Structure")
buildConfigField("String", "BASE_URL", "\"https://api.themoviedb.org/3/\"")
versionCode = AppConfigs.version_code_release
versionName = AppConfigs.version_name_release
}
Expand All @@ -52,11 +61,14 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "11"
jvmTarget = "17"
}
buildFeatures {
viewBinding = true
}
}

Expand Down Expand Up @@ -90,6 +102,42 @@ dependencies {
implementation(Deps.material)
implementation(Deps.constraint_layout)

//Navigation
implementation(Deps.navigation_fragment)
implementation(Deps.navigation_ui)
implementation(Deps.navigation_fragment_ktx)
implementation(Deps.navigation_ui_ktx)

//Lifecycle
implementation(Deps.lifecycle_livedata_ktx)
implementation(Deps.lifecycle_viewmodel_ktx)
implementation(Deps.lifecycle_runtime)

//Coroutine
implementation(Deps.coroutines_core)
implementation(Deps.coroutines_android)
testImplementation(Deps.coroutines_test)

//Retrofit
implementation(Deps.okHttp)
implementation(Deps.retrofit_runtime)
implementation(Deps.retrofit_gson)
implementation(Deps.okhttp_logging_interceptor)

//Koin
implementation(Deps.koin)

//Glide
implementation(Deps.glide_runtime)
implementation(Deps.glide_compiler)

// Room
implementation(Deps.room_runtime)
ksp(Deps.room_ksp)
implementation(Deps.room_ktx)

//Test
testImplementation(Deps.junit)
testImplementation(Deps.mockk)

}
9 changes: 6 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sun.structure_android">
package="com.sun.android">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".AndroidApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Structure_Android">
<activity
android:name="com.sun.android.MainActivity"
android:name="com.sun.android.ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -20,4 +23,4 @@
</activity>
</application>

</manifest>
</manifest>
26 changes: 26 additions & 0 deletions app/src/main/java/com/sun/android/AndroidApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sun.android

import android.app.Application
import com.sun.android.di.DataSourceModule
import com.sun.android.di.NetworkModule
import com.sun.android.di.RepositoryModule
import com.sun.android.di.AppModule
import com.sun.android.di.ViewModelModule
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidFileProperties
import org.koin.core.context.startKoin

class AndroidApplication : Application() {

private val rootModule = listOf(AppModule, NetworkModule, DataSourceModule, RepositoryModule, ViewModelModule)

override fun onCreate() {
super.onCreate()

startKoin {
androidContext(this@AndroidApplication)
androidFileProperties()
modules(rootModule)
}
}
}
1 change: 0 additions & 1 deletion app/src/main/java/com/sun/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.sun.android

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.sun.structure_android.R

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
30 changes: 30 additions & 0 deletions app/src/main/java/com/sun/android/base/BaseActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.sun.android.base

import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.viewbinding.ViewBinding

typealias ActivityInflate<T> = (LayoutInflater) -> T

abstract class BaseActivity<VB : ViewBinding>(private val inflate: ActivityInflate<VB>) : AppCompatActivity() {
private var _binding: VB? = null

val binding get() = _binding!!
abstract val viewModel: ViewModel

protected abstract fun initialize()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = inflate.invoke(layoutInflater)
setContentView(binding.root)
initialize()
}

override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
44 changes: 44 additions & 0 deletions app/src/main/java/com/sun/android/base/BaseFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.sun.android.base

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import androidx.viewbinding.ViewBinding

typealias FragmentInflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T

/**
* class HomeFragment() : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
*/
abstract class BaseFragment<VB : ViewBinding>(private val inflate: FragmentInflate<VB>) : Fragment() {
private var _binding: VB? = null

val binding get() = _binding!!
abstract val viewModel: ViewModel

protected abstract fun initView()

protected abstract fun initData()

protected abstract fun bindData()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = inflate.invoke(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initData()
bindData()
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/sun/android/data/MovieRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sun.android.data

import com.sun.android.data.model.Movie
import kotlinx.coroutines.flow.Flow

interface MovieRepository {
suspend fun getMovies(): Flow<List<Movie>>

suspend fun getDetailMovies(movieId: Int): Flow<Movie>
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/sun/android/data/TokenRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sun.android.data

interface TokenRepository {
fun getToken(): String?

fun saveToken(token: String)

fun clearToken()
}
38 changes: 38 additions & 0 deletions app/src/main/java/com/sun/android/data/model/Movie.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.sun.android.data.model

import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize

@Parcelize
@Entity(tableName = "movies")
data class Movie(
@PrimaryKey
@SerializedName("id")
@Expose
var id: Int = -1,
@SerializedName("backdrop_path")
@Expose
var backDropImage: String = "",
@SerializedName("overview")
@Expose
var overView: String = "",
@SerializedName("vote_average")
@Expose
var vote: Double = 0.0,
@SerializedName("vote_count")
@Expose
var voteCount: Int = 0,
@SerializedName("title")
@Expose
var title: String = "",
@SerializedName("poster_path")
@Expose
var urlImage: String = "",
@SerializedName("original_title")
@Expose
var originalTitle: String = ""
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.sun.android.data.repository

import android.util.Log
import com.sun.android.data.MovieRepository
import com.sun.android.data.model.Movie
import com.sun.android.data.source.MovieDataSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import org.koin.core.component.KoinComponent
import java.io.IOException

class MovieRepositoryImpl(
private val remote: MovieDataSource.Remote,
private val local: MovieDataSource.Local
) : KoinComponent, MovieRepository {

override suspend fun getMovies(): Flow<List<Movie>> = flow {
try {
val movies = remote.getMovies().data
local.updateMovies(movies)
} catch (e: IOException) {
Log.e("MovieRepository", "getMovies failed, using local data \n Detail error:\n $e")
}
emit(local.getMoviesLocal())
}

override suspend fun getDetailMovies(movieId: Int) = flow {
try {
emit(local.getMovieDetailLocal(movieId))
} catch (e: IOException) {
Log.e("MovieRepository", "getDetailMovies failed, retry with network \n Detail error:\n $e")
val movie = remote.getMovieDetail(movieId = movieId)
local.updateMovies(arrayListOf(movie))
emit(local.getMovieDetailLocal(movieId))
}
}
}
Loading