Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
6b378cf
initial commit
elec60 Dec 24, 2021
06d58a4
add dependencies
elec60 Dec 24, 2021
eaa3f49
add Dtos(Data Transfer Objects)
elec60 Dec 24, 2021
c50ca20
define repository
elec60 Dec 24, 2021
2742787
define domain data models
elec60 Dec 24, 2021
75069d8
add hilt structure
elec60 Dec 24, 2021
5a97aa7
add api infrastructure(Retrofit and OkHttp configuration)
elec60 Dec 24, 2021
0294043
add dto to domain data mapping functions
elec60 Dec 24, 2021
acd2f66
implement network calls
elec60 Dec 24, 2021
5ecf3ec
create use cases
elec60 Dec 24, 2021
b4e8a84
impl popular movies page
elec60 Dec 24, 2021
9c4630b
move business logic to usecase
elec60 Dec 24, 2021
da72e67
remove unused data
elec60 Dec 24, 2021
c1f1973
remove business logic from viewModel
elec60 Dec 24, 2021
70b0d4f
impl details fragment
elec60 Dec 24, 2021
bf45bdb
add room database infrastructure
elec60 Dec 25, 2021
3ab98c3
impl favorite page
elec60 Dec 25, 2021
48e3b4f
date format bug fixes
elec60 Dec 25, 2021
83029bd
add google truth dependency
elec60 Dec 25, 2021
a1954b8
add reviews dto(Data Transfer Object)
elec60 Dec 25, 2021
0124a72
add get reviews api functionality
elec60 Dec 25, 2021
33d8fcb
impl reviews ui and functionality
elec60 Dec 25, 2021
1e027b3
resolve scrolling issue in api 23 devices(some of devices!)
elec60 Dec 26, 2021
e4057a8
write unit tests
elec60 Dec 26, 2021
dbd52d5
resolve crash issue
elec60 Dec 26, 2021
6ed0a44
correct data model
elec60 Dec 26, 2021
746efd0
add empty state to reviews
elec60 Dec 26, 2021
9238e44
resolve loading issue
elec60 Dec 26, 2021
a57bbd7
handle no more data in pagination
elec60 Dec 26, 2021
f6eff6f
use interface for better abstraction
elec60 Dec 26, 2021
c728b70
add retry for error handling
elec60 Dec 26, 2021
4ca8298
handle navigationView reselect for better ux
elec60 Dec 26, 2021
fa3001c
using stringProvider for simplifying testing using mockito
elec60 Dec 26, 2021
e032baa
refactor StringProvider
elec60 Dec 26, 2021
f3f3769
refactor DateUtil and write test for it
elec60 Dec 26, 2021
5988c29
add Read More functionality to reviews
elec60 Dec 26, 2021
78486d7
add comment
elec60 Dec 26, 2021
b4c8750
fixes error type view issue
elec60 Dec 26, 2021
6e2d2db
initial commit
elec60 Dec 24, 2021
ea8491a
add dependencies
elec60 Dec 24, 2021
4d26af6
add Dtos(Data Transfer Objects)
elec60 Dec 24, 2021
57846cf
define repository
elec60 Dec 24, 2021
22e27fa
define domain data models
elec60 Dec 24, 2021
38a098c
add hilt structure
elec60 Dec 24, 2021
caecacf
add api infrastructure(Retrofit and OkHttp configuration)
elec60 Dec 24, 2021
ce90007
add dto to domain data mapping functions
elec60 Dec 24, 2021
d9a7d59
implement network calls
elec60 Dec 24, 2021
316341c
create use cases
elec60 Dec 24, 2021
cf69f37
impl popular movies page
elec60 Dec 24, 2021
384561a
move business logic to usecase
elec60 Dec 24, 2021
cc8576c
remove unused data
elec60 Dec 24, 2021
110549d
remove business logic from viewModel
elec60 Dec 24, 2021
5907659
impl details fragment
elec60 Dec 24, 2021
43fa280
add room database infrastructure
elec60 Dec 25, 2021
2d6db2a
impl favorite page
elec60 Dec 25, 2021
98d0c31
date format bug fixes
elec60 Dec 25, 2021
81c0584
add google truth dependency
elec60 Dec 25, 2021
2644df3
add reviews dto(Data Transfer Object)
elec60 Dec 25, 2021
d0402eb
add get reviews api functionality
elec60 Dec 25, 2021
11eae1e
impl reviews ui and functionality
elec60 Dec 25, 2021
954cbe0
resolve scrolling issue in api 23 devices(some of devices!)
elec60 Dec 26, 2021
fc3fea9
write unit tests
elec60 Dec 26, 2021
8b59353
resolve crash issue
elec60 Dec 26, 2021
0335e7c
correct data model
elec60 Dec 26, 2021
5f824f6
add empty state to reviews
elec60 Dec 26, 2021
d8b0deb
resolve loading issue
elec60 Dec 26, 2021
c7a5acc
handle no more data in pagination
elec60 Dec 26, 2021
e7ba5bb
use interface for better abstraction
elec60 Dec 26, 2021
9f27a7c
add retry for error handling
elec60 Dec 26, 2021
4348de2
handle navigationView reselect for better ux
elec60 Dec 26, 2021
8d12481
using stringProvider for simplifying testing using mockito
elec60 Dec 26, 2021
c54b202
refactor StringProvider
elec60 Dec 26, 2021
fb01f2f
refactor DateUtil and write test for it
elec60 Dec 26, 2021
eacdf59
add Read More functionality to reviews
elec60 Dec 26, 2021
39e9f72
add comment
elec60 Dec 26, 2021
b39cc40
fixes error type view issue
elec60 Dec 26, 2021
6ce9f52
Remove unused codes
elec60 Dec 26, 2021
75609a8
Merge branch 'develop' of https://github.com/elec60/tech-work-sample
elec60 Dec 26, 2021
05eb750
Merge branch 'master' of https://github.com/elec60/tech-work-sample
elec60 Dec 26, 2021
7c9a23d
rename test functions
elec60 Dec 27, 2021
15b6e8f
Merge branch 'develop'
elec60 Dec 27, 2021
3a05404
fix error handling issue
elec60 Dec 27, 2021
c3b69ab
fix error handling issue
elec60 Dec 27, 2021
199a25c
Merge branch 'develop'
elec60 Dec 27, 2021
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
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
/.idea/.gitignore
/.idea/compiler.xml
/.idea/gradle.xml
/.idea/misc.xml
/.idea/vcs.xml
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
92 changes: 92 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
id 'kotlin-android-extensions'
}

android {
compileSdk 31

defaultConfig {
applicationId "com.mousavi.hashem.mymoviesapp"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

dependencies {

//androidx dependencies
implementation 'androidx.fragment:fragment-ktx:1.4.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'

//material
implementation 'com.google.android.material:material:1.4.0'

//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'

//Gson
implementation 'com.google.code.gson:gson:2.8.9'

// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'

// Coroutine Lifecycle Scopes
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"

//Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.38.1"
kapt "com.google.dagger:hilt-android-compiler:2.38.1"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"

//Navigation Component
def nav_version = "2.4.0-rc01"
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

// Room
implementation "androidx.room:room-runtime:2.4.0"
kapt "androidx.room:room-compiler:2.4.0"
// Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:2.3.0"

//ImageLoader
implementation("io.coil-kt:coil:1.4.0")

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "com.google.truth:truth:1.1.3"
testImplementation "com.google.truth:truth:1.1.3"
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
testImplementation "org.mockito:mockito-core:4.0.0"

}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.mousavi.hashem.mymoviesapp

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.mousavi.hashem.mymoviesapp", appContext.packageName)
}
}
26 changes: 26 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mousavi.hashem.mymoviesapp">

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

<application
android:name=".MoviesApp"
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.MyMoviesApp">
<activity
android:name=".presentaion.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
7 changes: 7 additions & 0 deletions app/src/main/java/com/mousavi/hashem/common/Either.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mousavi.hashem.common

sealed class Either<out T, out E> {
data class Success<T, E>(val data: T) : Either<T, E>()

data class Error<T, E>(val error: E) : Either<T, E>()
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/mousavi/hashem/mymoviesapp/MoviesApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mousavi.hashem.mymoviesapp

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class MoviesApp: Application()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.mousavi.hashem.mymoviesapp.data.local

import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

class Converters {

@TypeConverter
fun listToString(list: List<String>) = Gson().toJson(list)

@TypeConverter
fun stringToList(value: String): List<String> {
val type = object : TypeToken<List<String>>() {}.type
return Gson().fromJson(value, type)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.mousavi.hashem.mymoviesapp.data.local

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface MovieDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMovieEntity(movieEntity: MovieEntity)

@Query("SELECT * FROM movies_table")
fun getMovieEntities(): Flow<List<MovieEntity>>

@Delete
suspend fun deleteMovieEntity(movieEntity: MovieEntity)

@Query("SELECT * FROM movies_table WHERE id=:id")
suspend fun getMovieEntity(id: Int): MovieEntity?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.mousavi.hashem.mymoviesapp.data.local

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.mousavi.hashem.mymoviesapp.domain.model.Movie

@Entity(tableName = "movies_table")
class MovieEntity(
@PrimaryKey(autoGenerate = false)
val id: Int,
val backdropPath: String?,
val genreIds: List<String>,
val overview: String,
val posterPath: String?,
val releaseDate: String?,
val title: String,
val voteAverage: Double,
val voteCount: Int,
) {
fun toMovie(): Movie {
return Movie(
backdropPath = backdropPath,
genreNames = genreIds.toMutableList(),
id = id,
overview = overview,
posterPath = posterPath,
releaseDate = releaseDate,
title = title,
voteAverage = voteAverage,
voteCount = voteCount,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.mousavi.hashem.mymoviesapp.data.local

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(
entities = [MovieEntity::class],
version = 1
)
@TypeConverters(Converters::class)
abstract class MoviesDatabase: RoomDatabase() {
abstract val dao: MovieDao

companion object {
const val DATABASE_NAME = "movies_db"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.mousavi.hashem.mymoviesapp.data.remote

import com.mousavi.hashem.mymoviesapp.data.remote.dto.GenresDto
import com.mousavi.hashem.mymoviesapp.data.remote.dto.PageDataDto
import com.mousavi.hashem.mymoviesapp.data.remote.dto.ReviewsDto
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface Api {

companion object {
const val BASE_URL = "https://api.themoviedb.org/3/"
const val API_KEY = "7457b8e2de9fbd83fe4e24eaa79912f2"
const val IMAGE_BASE_URL = "https://image.tmdb.org/t/p/w500"
}

@GET("movie/popular")
suspend fun getPopularMovies(
@Query("language") language: String,
@Query("page") page: Int,
): PageDataDto

@GET("genre/movie/list")
suspend fun getGenres(): GenresDto

@GET("movie/{movie_id}/reviews")
suspend fun getReviews(
@Path("movie_id") movieId: Int,
@Query("language") language: String,
@Query("page") page: Int,
): ReviewsDto
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.mousavi.hashem.mymoviesapp.data.remote

import com.mousavi.hashem.common.Either
import com.mousavi.hashem.mymoviesapp.data.remote.dto.GenresDto
import com.mousavi.hashem.mymoviesapp.data.remote.dto.PageDataDto
import com.mousavi.hashem.mymoviesapp.data.remote.dto.ReviewsDto

interface NetworkDataSource {

suspend fun getPopularMovies(
language: String,
page: Int,
): Either<PageDataDto, String>

suspend fun getGenres(): Either<GenresDto, String>

suspend fun getReviews(
movieId: Int,
language: String,
page: Int,
): Either<ReviewsDto, String>
}
Loading