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

인기영화 목록 보기 #34

Open
wants to merge 20 commits into
base: taeiim
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
66 changes: 66 additions & 0 deletions MovieApp/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분에 대해서 공부 한번 해보시면 좋을꺼 같습니다.

defaultConfig {
applicationId "com.god.taeiim.movieapp"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

buildConfigField "String", "SECRET_KEY" , "\"d8cef1cded16cd9e5bae792da0d70ed7\""
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "androidx.appcompat:appcompat:$app_compat_version"

//test
testImplementation "junit:junit:$junit_version"
androidTestImplementation "androidx.test:runner:$test_runner_version"
androidTestImplementation "androidx.test.espresso:espresso-core:$core_version"

//google
implementation "com.google.android.material:material:$material_version"
implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"

//retrofit
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"

//glide
implementation "com.github.bumptech.glide:glide:$glide_version"
annotationProcessor "com.github.bumptech.glide:compiler:$glide_version"

//ViewModel, LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

//koin
implementation "org.koin:koin-core:$koin_version"
implementation "org.koin:koin-androidx-viewmodel:$koin_version"

//coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.god.taeiim.toyproject
package com.god.taeiim.movieapp

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
Expand All @@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.god.taeiim.toyproject", appContext.packageName)
assertEquals("com.god.taeiim.movieapp", appContext.packageName)
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.god.taeiim.toyproject">
package="com.god.taeiim.movieapp">

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

<application
android:name=".MovieAppApplication"
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/AppTheme">
<activity android:name=".MainActivity">
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".ui.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.god.taeiim.movieapp

import android.app.Application
import com.god.taeiim.movieapp.di.dataSourceModule
import com.god.taeiim.movieapp.di.networkModule
import com.god.taeiim.movieapp.di.viewModelModule
import com.god.taeiim.movieapp.ext.setupKoin

class MovieAppApplication : Application() {

override fun onCreate() {
super.onCreate()
setupKoin(this, viewModelModule, networkModule, dataSourceModule)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.god.taeiim.movieapp.base

interface BaseAction
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.god.taeiim.movieapp.base

import android.os.Bundle
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding

abstract class BaseActivity<B : ViewDataBinding>(
@LayoutRes private val layoutResId: Int
) : AppCompatActivity() {

protected lateinit var binding: B

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, layoutResId)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.god.taeiim.movieapp.base

import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView


open class BaseRecyclerAdapter<ITEM, B : ViewDataBinding>(
@LayoutRes val layoutResId: Int,
val bindingVariableId: Int? = null
) : RecyclerView.Adapter<BaseRecyclerViewHolder<B>>() {

private val items = mutableListOf<ITEM>()

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<B> =
object : BaseRecyclerViewHolder<B>(
layoutResId,
parent,
bindingVariableId
) {}

override fun getItemCount(): Int = items.size

override fun onBindViewHolder(holder: BaseRecyclerViewHolder<B>, position: Int) =
holder.onBindViewHolder(items[position])

protected fun getItem(position: Int): ITEM =
items.getOrNull(position) ?: throw ArrayIndexOutOfBoundsException()

fun updateItems(items: List<ITEM>?) {
this.items.run {
clear()
items?.let {
addAll(it)
}
}

notifyDataSetChanged()
}

fun clearItems() {
val size = items.size
this.items.clear()
notifyItemRangeRemoved(0, size)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.god.taeiim.movieapp.base

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView

open class BaseRecyclerViewHolder<B : ViewDataBinding>(
@LayoutRes layoutRes: Int,
parent: ViewGroup,
private val bindingVariableId: Int?
) : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(layoutRes, parent, false)) {

private val binding: B = DataBindingUtil.bind(itemView)!!

fun onBindViewHolder(item: Any?) {
if (bindingVariableId == null)
return

binding.run {
setVariable(bindingVariableId, item)
executePendingBindings()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.god.taeiim.movieapp.base

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.god.taeiim.movieapp.ext.toLiveData
import kotlin.properties.Delegates

abstract class BaseViewModel<ViewState : BaseViewState, ViewAction : BaseAction>(
initialState: ViewState
) : ViewModel() {

private val stateMutableLiveData = MutableLiveData<ViewState>()
val stateLiveData = stateMutableLiveData.toLiveData()

protected var state by Delegates.observable(initialState) { _, old, new ->
stateMutableLiveData.value = new
}

fun sendAction(viewAction: ViewAction) {
state = onReduceState(viewAction)
}

fun loadData() {
onLoadData()
}

protected open fun onLoadData() {}

protected abstract fun onReduceState(viewAction: ViewAction): ViewState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.god.taeiim.movieapp.base

interface BaseViewState
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.god.taeiim.movieapp.binding

import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners

@BindingAdapter("loadPosterImg")
fun ImageView.loadPosterImg(url: String?) {
Glide.with(context)
.load("https://image.tmdb.org/t/p/w300$url")
.transform(RoundedCorners(8))
.into(this)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.god.taeiim.movieapp.data.model

import com.google.gson.annotations.SerializedName

data class MoviePopular(
@SerializedName("page")
val page: Int = 0,
@SerializedName("results")
val movies: List<Movie> = listOf(),
@SerializedName("total_pages")
val totalPages: Int = 0,
@SerializedName("total_results")
val totalResults: Int = 0
)

data class Movie(
@SerializedName("adult")
val adult: Boolean = false,
@SerializedName("backdrop_path")
val backdropPath: String = "",
@SerializedName("genre_ids")
val genreIds: List<Int> = listOf(),
@SerializedName("id")
val id: Int = 0,
@SerializedName("original_language")
val originalLanguage: String = "",
@SerializedName("original_title")
val originalTitle: String = "",
@SerializedName("overview")
val overview: String = "",
@SerializedName("popularity")
val popularity: Double = 0.0,
@SerializedName("poster_path")
val posterPath: String = "",
@SerializedName("release_date")
val releaseDate: String = "",
@SerializedName("title")
val title: String = "",
@SerializedName("video")
val video: Boolean = false,
@SerializedName("vote_average")
val voteAverage: Double = 0.0,
@SerializedName("vote_count")
val voteCount: Int = 0
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.god.taeiim.movieapp.data.repository

import com.god.taeiim.movieapp.data.model.Movie


interface MovieRepository {

suspend fun getPopularMovies(page: Int): List<Movie>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.god.taeiim.movieapp.data.repository

import com.god.taeiim.movieapp.data.model.Movie
import com.god.taeiim.movieapp.data.source.remote.MovieRemoteDataSource


class MovieRepositoryImpl(
private val movieRemoteDataSource: MovieRemoteDataSource
) : MovieRepository {

override suspend fun getPopularMovies(page: Int): List<Movie> {
return movieRemoteDataSource.getPopularMovies(page)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.god.taeiim.movieapp.data.source

import com.god.taeiim.movieapp.data.model.MoviePopular
import retrofit2.http.GET
import retrofit2.http.Query

interface MovieApi {
@GET("movie/popular")
suspend fun getMoviePopular(@Query("page") page: Int): MoviePopular
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.god.taeiim.movieapp.data.source.remote

import com.god.taeiim.movieapp.data.model.Movie

interface MovieRemoteDataSource {

suspend fun getPopularMovies(page: Int): List<Movie>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.god.taeiim.movieapp.data.source.remote

import com.god.taeiim.movieapp.data.model.Movie
import com.god.taeiim.movieapp.data.source.MovieApi
import retrofit2.HttpException

class MovieRemoteDataSourceImpl(private val movieApi: MovieApi) :
MovieRemoteDataSource {

override suspend fun getPopularMovies(page: Int): List<Movie> {
return try {
movieApi.getMoviePopular(page).movies
} catch (e: HttpException) {
listOf()
}
}

}
Loading