Skip to content

Commit

Permalink
Merge pull request #3 from ratanparai/feature/room-architecture
Browse files Browse the repository at this point in the history
Android architecture implementation
  • Loading branch information
ratanparai authored Apr 23, 2019
2 parents 00ac50b + a036b8b commit 1930cce
Show file tree
Hide file tree
Showing 13 changed files with 312 additions and 4 deletions.
18 changes: 18 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

ext {
roomVersion = '1.1.1'
archLifecycleVersion = '1.1.1'
coroutines = '0.26.1'
}

android {
compileSdkVersion 28
defaultConfig {
Expand All @@ -28,6 +36,16 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
implementation 'com.github.bumptech.glide:glide:3.8.0'

implementation 'org.jsoup:jsoup:1.11.3'

// Room components
implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion"
kapt "android.arch.persistence.room:compiler:$rootProject.roomVersion"
androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion"

implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion"

// Unit testing dependencies.
testImplementation 'junit:junit:4.12'
testImplementation 'com.google.truth:truth:0.44'
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
android:name="android.software.leanback"
android:required="true"/>

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

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down
34 changes: 34 additions & 0 deletions app/src/main/java/com/ratanparai/moviedog/db/AppDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.ratanparai.moviedog.db

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.ratanparai.moviedog.db.dao.MovieDao
import com.ratanparai.moviedog.db.entity.Movie
import com.ratanparai.moviedog.utilities.DATABASE_NAME


@Database(entities = [Movie::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun movieDao(): MovieDao

companion object {

// For Singleton instantiation
@Volatile private var instance: AppDatabase? = null

fun getInstance(context: Context): AppDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}

// Create and pre-populate the database. See this article for more details:
// https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.build()
}
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/com/ratanparai/moviedog/db/dao/MovieDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.ratanparai.moviedog.db.dao

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.ratanparai.moviedog.db.entity.Movie

@Dao
interface MovieDao {
@Query("SELECT * FROM movies ORDER BY title")
fun getMovies() : LiveData<List<Movie>>

@Query("SELECT * FROM movies WHERE id = :movieId")
fun getMovie(movieId : Int) : LiveData<Movie>

@Query("SELECT * FROM movies WHERE imdbId = :imdbId")
fun getMovie(imdbId : String) : LiveData<Movie>

@Insert
fun insertMovie(movie: Movie): Long
}
17 changes: 17 additions & 0 deletions app/src/main/java/com/ratanparai/moviedog/db/entity/Movie.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ratanparai.moviedog.db.entity

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

@Entity(tableName = "movies")
data class Movie (
@PrimaryKey @ColumnInfo(name = "id") val id: String,
val title : String,
val description : String,
val imdbId : String,
val cardImage : String,
val videoUrl : String,
val productionYear : Int,
val duration: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.ratanparai.moviedog.db.repository

import com.ratanparai.moviedog.db.dao.MovieDao

class MovieRepository private constructor(private val movieDao : MovieDao){

companion object {
@Volatile private var instance : MovieRepository? = null

fun getInstance(movieDao: MovieDao) = instance ?: synchronized(this) {
instance ?: MovieRepository(movieDao).also { instance = it }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.ratanparai.moviedog.scrapper

import com.ratanparai.moviedog.Movie
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.io.IOException
import java.util.ArrayList
import java.util.concurrent.TimeUnit

class DekhvhaiScrapper {

val DEKHVHAI_SEARCH_URL = "http://dekhvhai.com/msearch.php?q=&searchquery=%s&q=M"

fun search(query: String): List<Movie> {

var query = query.toLowerCase()
val results = ArrayList<Movie>()

val searchUrl = String.format(DEKHVHAI_SEARCH_URL, query)

try {
val document = Jsoup.connect(searchUrl).get()
val elements = document.select("#tabs_i2-pane1 > div:nth-child(3)")
val a = elements.select("a")
var href = a.attr("href")

href = href.replace(" ", "%20")

href = "http://dekhvhai.com/$href"


val document1 = Jsoup.connect(href).get()
val select = document1.select("#sidebar-widget > div:nth-child(1) > div > div > a:nth-child(5)")


val movieTitle =
document1.select("#page_header > div.ph-content-wrap > div > div > div > div.col-md-3 > div.subheader-titles > h2")
.text()
val description =
document1.select("#page_header > div.ph-content-wrap > div > div > div > div.col-md-3 > div.portfolio-item-desc > div > p")
.text()
val movieTime =
document1.select("#sidebar-widget > div:nth-child(1) > div > div > table > tbody > tr:nth-child(6) > td:nth-child(2)")
.text()
val cardImage =
document1.select("#page_header > div.ph-content-wrap > div > div > div > div.col-md-2 > div > a > img")
.attr("src")


// clean up everything
val title = getOnlyTitleFromTitleAndYear(movieTitle)
val year = getOnlyYearFromTitleAndYear(movieTitle)

val yearInInt = Integer.parseInt(year)


val href1 = select.attr("href")
println(href1)

} catch (e: IOException) {
e.printStackTrace()
}



return results

}


fun getOnlyTitleFromTitleAndYear(titleWithyear: String): String {
return titleWithyear.substring(0, titleWithyear.indexOf("(")).trim { it <= ' ' }
}

fun getOnlyYearFromTitleAndYear(titleWithYear: String): String {
return titleWithYear.substring(titleWithYear.indexOf("(") + 1, titleWithYear.indexOf(")"))
}

fun getHourFromTime(time: String): Int {

val stringHour = time.substring(0, time.indexOf("H")).trim { it <= ' ' }
return Integer.parseInt(stringHour)
}

fun getMinuteFromTime(time: String): Int {
val stringMinute = time.substring(time.indexOf("H") + 1, time.indexOf("M")).trim { it <= ' ' }
return Integer.parseInt(stringMinute)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.ratanparai.moviedog.scrapper

import com.ratanparai.moviedog.db.entity.Movie
import org.jsoup.Jsoup
import org.jsoup.nodes.Document

class WowMovieZoneScrapper {
val SEARCH_URL = "http://172.27.27.84/ajax_search?search_value=%s"

fun getSearchResult(query : String): Document? {
var query = query.toLowerCase()
val results = ArrayList<Movie>()

val searchUrl = String.format(SEARCH_URL, query)

return Jsoup.connect(searchUrl)
.header("X-Requested-With", " XMLHttpRequest")
.get()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ratanparai.moviedog.utilities

/**
* Constants used throughout the app.
*/
const val DATABASE_NAME = "moviedog-db"
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.ratanparai.moviedog.scrapper

import com.google.common.truth.Truth.assertThat
import org.junit.Test

class DekhvhaiScrapperTest {
@Test
fun shouldGetCorrectTitleFromTitleAndYear() {
// Arrange
val dekhvhaiScrapper = DekhvhaiScrapper()
val expected = "How to Train Your Dragon: The Hidden World"

// Act
val actual = dekhvhaiScrapper
.getOnlyTitleFromTitleAndYear("How to Train Your Dragon: The Hidden World (2019)")

// Assert
assertThat(actual).isEqualTo(expected)
}

@Test
fun shouldGetCorrectYearFromTitleAndYear() {
// Arrange
val dekhvhaiScrapper = DekhvhaiScrapper()
val expected = "2019"

// Act
val actual = dekhvhaiScrapper
.getOnlyYearFromTitleAndYear("How to Train Your Dragon: The Hidden World (2019)")

// Assert
assertThat(actual).isEqualTo(expected)
}

@Test
fun shouldReturnCorrectHour() {
val dekhvhaiProvider = DekhvhaiScrapper()
val actual = dekhvhaiProvider.getHourFromTime("02 H 00 M")
val expected = 2

assertThat(actual).isEqualTo(expected)

}

@Test
fun shouldReturnCorrectMinute() {
val dekhvhaiProvider = DekhvhaiScrapper()
val actual = dekhvhaiProvider.getMinuteFromTime("02 H 15 M")
val expected = 15

assertThat(actual).isEqualTo(expected)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.ratanparai.moviedog.scrapper

import com.google.common.truth.Truth.assertThat
import org.junit.Ignore
import org.junit.Test

class WowMovieZoneScrapperTest {

@Ignore
@Test
fun shouldGetHtmlWhenSearchPageRequested() {
// Arrange
var scrapper = WowMovieZoneScrapper()
val expected = 1000

// Act
val searchResult = scrapper.getSearchResult("Harry Potter")
val actual = searchResult.toString().length

// Assert
assertThat(actual).isGreaterThan(expected)

}
}
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ steps:
gradleWrapperFile: 'gradlew'
gradleOptions: '-Xmx3072m'
publishJUnitResults: true
testResultsFiles: '**/testReleaseUnitTest/TEST-*.xml'
testResultsFiles: '**/testDebugUnitTest/TEST-*.xml'
tasks: 'build'
13 changes: 10 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.3.20'

ext {
kotlin_version = '1.3.20'
roomVersion = '1.1.1'
archLifecycleVersion = '1.1.1'
coroutines = '0.26.1'
lifecycleVersion = '2.1.0-alpha04'
}
repositories {
google()
jcenter()

}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
Expand All @@ -19,7 +26,7 @@ allprojects {
repositories {
google()
jcenter()

}
}

Expand Down

0 comments on commit 1930cce

Please sign in to comment.