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

PR submit for Open Table challenge #17

Open
wants to merge 7 commits into
base: master
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
38 changes: 38 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
buildToolsVersion '34.0.0'

viewBinding {
enabled = true
}
}

dependencies {
Expand All @@ -42,20 +47,53 @@ dependencies {
implementation libs.androidx.constraintlayout
// dagger
implementation libs.dagger
implementation libs.core.ktx
kapt libs.dagger.compiler

//retrofit
implementation libs.retrofit
implementation libs.converter.gson
implementation libs.retrofit.rx.adapter

//glide
implementation libs.glide
kapt libs.compiler

//reactive x
implementation libs.rx.android
implementation libs.rx.java
implementation libs.rx.kotlin

//Room database
implementation libs.androidx.room.runtime.android
kapt libs.room.compiler
implementation libs.androidx.room.ktx
implementation libs.androidx.room.rxjava2

//RecyclerView
implementation libs.androidx.recyclerview

//CardView
implementation libs.androidx.cardview

// Mockito for mocking dependencies
testImplementation libs.mockito.core
testImplementation libs.mockito.inline

// Mockito for Android instrumented tests
androidTestImplementation libs.mockito.android

// MockWebServer
testImplementation libs.mockwebserver

// For room DB test
testImplementation libs.androidx.core.testing // For LiveData and Room testing
testImplementation libs.androidx.room.testing // For Room in-memory database testing

// Material Design
implementation libs.material

androidTestImplementation libs.androidx.runner
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
69 changes: 62 additions & 7 deletions app/src/main/java/com/example/otchallenge/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,77 @@
package com.example.otchallenge

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.otchallenge.databinding.ActivityMainBinding
import com.example.otchallenge.model.Book
import com.example.otchallenge.presenter.BooksContract
import com.example.otchallenge.presenter.BooksPresenter
import com.example.otchallenge.repository.BookRepository
import com.example.otchallenge.view.BooksAdapter
import javax.inject.Inject

class MainActivity : AppCompatActivity() {
class MainActivity : AppCompatActivity(), BooksContract.View {

@Inject
lateinit var repository: BookRepository

private lateinit var presenter: BooksPresenter
private lateinit var adapter: BooksAdapter

//viewBinding
private lateinit var mainBinding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
(application as MyApplication).appComponent.inject(this)
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets

mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)

presenter = BooksPresenter(repository)
presenter.attach(this)

setupRecyclerView()

// Fetch the books when the activity is created
presenter.fetchBooks()
}

// RecyclerView, Adapter setting
private fun setupRecyclerView(){
adapter = BooksAdapter(emptyList())
mainBinding.recyclerView.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = [email protected]
}
}

override fun showProgress() {
mainBinding.progressBar.visibility = View.VISIBLE
}

override fun hideProgress() {
println("hideProgress()")
mainBinding.progressBar.visibility = View.GONE
}

override fun displayBooks(books: List<Book>) {
adapter.updateBooks(books)
mainBinding.recyclerView.visibility = View.VISIBLE
}

override fun showError(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

override fun onDestroy() {
presenter.detach()
super.onDestroy()
}
}
10 changes: 9 additions & 1 deletion app/src/main/java/com/example/otchallenge/MyApplication.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package com.example.otchallenge

import android.app.Application
import android.net.Network
import com.example.otchallenge.di.AppComponent
import com.example.otchallenge.di.DaggerAppComponent
import com.example.otchallenge.di.DatabaseModule
import com.example.otchallenge.di.NetworkModule
import com.example.otchallenge.di.RepositoryModule

class MyApplication : Application() {

lateinit var appComponent: AppComponent

override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder().build()
appComponent = DaggerAppComponent.builder()
.networkModule(NetworkModule())
.databaseModule(DatabaseModule(this))
.repositoryModule(RepositoryModule())
.build()
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/com/example/otchallenge/dao/BookDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.otchallenge.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import com.example.otchallenge.model.Book
import io.reactivex.Single
import io.reactivex.Completable

@Dao
interface BookDao {
@Query("SELECT * FROM books ORDER BY rank ASC")
fun getAllBooks(): Single<List<Book>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertBooks(books: List<Book>): Completable

@Query("DELETE FROM books")
fun deleteAllBooks(): Completable
}
4 changes: 3 additions & 1 deletion app/src/main/java/com/example/otchallenge/di/AppComponent.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.example.otchallenge.di

import android.content.Context
import com.example.otchallenge.MainActivity
import dagger.BindsInstance
import dagger.Component
import javax.inject.Singleton

@Singleton
@Component
@Component(modules = [NetworkModule::class, DatabaseModule::class, RepositoryModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
25 changes: 25 additions & 0 deletions app/src/main/java/com/example/otchallenge/di/DatabaseModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.otchallenge.di

import android.content.Context
import androidx.room.Room
import com.example.otchallenge.dao.BookDao
import com.example.otchallenge.repository.BookDatabase
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

@Module
class DatabaseModule(private val context: Context) {

@Provides
@Singleton
fun provideDatabase(): BookDatabase {
return Room.databaseBuilder(context, BookDatabase::class.java, "books_db")
.build()
}

@Provides
fun provideBookDao(bookDatabase: BookDatabase): BookDao{
return bookDatabase.bookDao()
}
}
28 changes: 28 additions & 0 deletions app/src/main/java/com/example/otchallenge/di/NetworkModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.otchallenge.di

import com.example.otchallenge.libs.ApiService
import dagger.Module
import dagger.Provides
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import javax.inject.Singleton

@Module
class NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.nytimes.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}

@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/com/example/otchallenge/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.otchallenge.di

import android.content.Context
import com.example.otchallenge.dao.BookDao
import com.example.otchallenge.libs.ApiService
import com.example.otchallenge.repository.BookRepository
import dagger.Module
import dagger.Provides

@Module
class RepositoryModule {

@Provides
fun provideBookRepository(apiService: ApiService, bookDao: BookDao): BookRepository{
return BookRepository(apiService, bookDao)
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/com/example/otchallenge/libs/ApiService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.otchallenge.libs

import com.example.otchallenge.model.ApiResponse
import io.reactivex.Single
import retrofit2.http.GET
import retrofit2.http.Query

interface ApiService {
@GET("svc/books/v3/lists/current/hardcover-fiction.json")
fun getBooks(
@Query("api-key") apiKey: String,
@Query("offset") offset: Int=0
): Single<ApiResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.otchallenge.model

data class ApiResponse(
val results: ApiResults
)
5 changes: 5 additions & 0 deletions app/src/main/java/com/example/otchallenge/model/ApiResults.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.otchallenge.model

data class ApiResults(
val books: List<Book>
)
16 changes: 16 additions & 0 deletions app/src/main/java/com/example/otchallenge/model/Book.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.otchallenge.model

import androidx.room.Entity
import androidx.room.PrimaryKey
//import androidx.room.ColumnInfo

@Entity(tableName = "books")
data class Book(
@PrimaryKey
val primary_isbn13: String,
val rank: Int,
val title: String,
val author: String,
val description: String,
val book_image: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.otchallenge.presenter

import com.example.otchallenge.model.Book

interface BooksContract {
interface View {
fun showProgress()
fun hideProgress()
fun displayBooks(books: List<Book>)
fun showError(message: String)
}

interface Presenter {
fun attach(view: View)
fun detach()
fun fetchBooks()
}
}
Loading