Skip to content

Commit

Permalink
Merge pull request #4 from ratanparai/feature/content-provider
Browse files Browse the repository at this point in the history
Content provider to receive and publish search suggestion
  • Loading branch information
ratanparai authored Apr 25, 2019
2 parents 324cfa1 + 33aba4f commit 85d4fc1
Show file tree
Hide file tree
Showing 9 changed files with 1,397 additions and 9 deletions.
26 changes: 24 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
android:required="false"/>
<uses-feature
android:name="android.software.leanback"
android:required="true"/>
android:required="false"/>

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

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:banner="@drawable/app_icon_your_company"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
Expand All @@ -27,13 +28,34 @@
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>

<activity
android:name=".SearchableActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>

<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>

<activity android:name=".DetailsActivity"/>
<activity android:name=".PlaybackActivity"/>
<activity android:name=".BrowseErrorActivity"/>

<provider android:name=".provider.VideoProvider"
android:authorities="com.ratanparai.moviedog"
android:exported="true"
tools:ignore="ExportedContentProvider"/>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ratanparai.moviedog

import android.app.Activity

class SearchableActivity: Activity() {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ratanparai.moviedog.db.dao

import android.database.Cursor
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
Expand Down
124 changes: 124 additions & 0 deletions app/src/main/java/com/ratanparai/moviedog/provider/VideoProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.ratanparai.moviedog.provider

import android.app.SearchManager
import android.content.ContentProvider
import android.content.ContentValues
import android.content.UriMatcher
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.provider.BaseColumns
import android.util.Log
import com.ratanparai.moviedog.db.AppDatabase
import com.ratanparai.moviedog.db.dao.MovieDao
import com.ratanparai.moviedog.db.entity.Movie
import com.ratanparai.moviedog.scrapper.DekhvhaiScrapper

class VideoProvider : ContentProvider() {

private val TAG = "VideoContentProvider"


private lateinit var movieDao: MovieDao

private lateinit var dekhvhaiScrapper: DekhvhaiScrapper

private lateinit var uriMatcher: UriMatcher

private val AUTHORITY = "com.ratanparai.moviedog"
private val SEARCH_SUGGEST = 1

private val queryProjection = arrayOf(
BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE,
SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR,
SearchManager.SUGGEST_COLUMN_DURATION,
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
)

override fun onCreate(): Boolean {
movieDao = AppDatabase.getInstance(context).movieDao()
uriMatcher = buildUriMatcher()
dekhvhaiScrapper = DekhvhaiScrapper()
return true
}


override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?
): Cursor? {
Log.d(TAG, uri.toString())

if (uriMatcher.match(uri) == SEARCH_SUGGEST) {
Log.d(TAG, "Search suggestions requested.")

return search(uri.lastPathSegment)

} else {
Log.d(TAG, "Unknown uri to query: $uri")
throw IllegalArgumentException("Unknown Uri: $uri")
}
}

private fun search(query: String?): Cursor? {
if(query != null) {
val movies = dekhvhaiScrapper.search(query)
val matrixCursor = MatrixCursor(queryProjection)

for (movie in movies) {
matrixCursor.addRow(convertMovieIntoRow(movie))
}
return matrixCursor
}
return null
}

private fun convertMovieIntoRow(movie: Movie): Array<Any> {
return arrayOf(
movie.id,
movie.title,
movie.description,
movie.cardImage,
movie.productionYear,
movie.duration,
"GLOBALSEARCH",
movie.id
)
}

private fun buildUriMatcher(): UriMatcher {
val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
uriMatcher.addURI(
AUTHORITY, "/search/" + SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST
)
uriMatcher.addURI(
AUTHORITY,
"/search/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
SEARCH_SUGGEST
)
return uriMatcher
}

override fun insert(uri: Uri, values: ContentValues?): Uri? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun getType(uri: Uri): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.ratanparai.moviedog.scrapper

import com.ratanparai.moviedog.Movie
import com.ratanparai.moviedog.db.entity.Movie
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.nodes.Document
import java.io.IOException
import java.util.ArrayList
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList

class DekhvhaiScrapper {

Expand Down Expand Up @@ -53,10 +54,25 @@ class DekhvhaiScrapper {

val yearInInt = Integer.parseInt(year)

var duration = (TimeUnit.HOURS.toMillis(getHourFromTime(movieTime)) + TimeUnit.MINUTES.toMillis(getMinuteFromTime(movieTime))).toInt()


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

var movie = Movie(
id = "1",
cardImage = cardImage,
description = description,
duration = duration,
productionYear = yearInInt,
title = title,
videoUrl = href1,
imdbId = ""
)

results.add(movie)

} catch (e: IOException) {
e.printStackTrace()
}
Expand All @@ -67,6 +83,16 @@ class DekhvhaiScrapper {

}

fun getListOfMovieLinksFromSearchResult(document: Document): List<String>{
val result = ArrayList<String>()
val elements = document.select("#tabs_i2-pane1 > div > a")
for (elem in elements) {
result.add(elem.attr("href"))
}

return result
}


fun getOnlyTitleFromTitleAndYear(titleWithyear: String): String {
return titleWithyear.substring(0, titleWithyear.indexOf("(")).trim { it <= ' ' }
Expand All @@ -76,15 +102,15 @@ class DekhvhaiScrapper {
return titleWithYear.substring(titleWithYear.indexOf("(") + 1, titleWithYear.indexOf(")"))
}

fun getHourFromTime(time: String): Int {
fun getHourFromTime(time: String): Long {

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

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

}
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
<string name="buy_2">AT $9.99</string>
<string name="movie">Movie</string>


<string name="search_hint">Search for Videos</string>
<string name="search_label">Movie Dog</string>
<string name="settings_description">Movie Dog: Videos</string>


<!-- Error messages -->
<string name="error_fragment_message">An error occurred</string>
<string name="dismiss_error">Dismiss</string>
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/res/xml/searchable.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The attributes below configure the Android search box appearance
and the search suggestions settings.
See the Developer Guide for more information
http://developer.android.com/guide/topics/search/
The Assistant's priority order for intent actions are as follows:
1) Intent specified in cursor(SUGGEST_COLUMN_INTENT_ACTION)
2) Intent specific in this file (searchSuggestIntentAction)
3) ACTION_SEARCH(default)
It is recommended to set it to ACTION_VIEW in searchable.xml and only override in code if you
want to handle certain pieces of content differently.
-->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:includeInGlobalSearch="true"
android:label="@string/search_label"
android:icon="@drawable/lb_ic_in_app_search"
android:searchSettingsDescription="@string/settings_description"
android:searchSuggestAuthority="com.ratanparai.moviedog"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestIntentData="content://com.rataparai.moviedog/video"
android:queryAfterZeroResults="true"
android:searchSuggestPath="search"
android:searchSuggestThreshold="0" />
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
package com.ratanparai.moviedog.scrapper

import com.google.common.truth.Truth.assertThat
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.junit.Before
import org.junit.Test
import java.io.File

class DekhvhaiScrapperTest {

private lateinit var dekhvhai: Document

@Before
fun loadHtml() {
val file = ClassLoader.getSystemResource("HarryPotter_Dekhvhai.html").readText()

dekhvhai = Jsoup.parse(file, "UTF-8")
}

@Test
fun shouldGetCorrectTitleFromTitleAndYear() {
// Arrange
Expand Down Expand Up @@ -51,4 +65,25 @@ class DekhvhaiScrapperTest {
assertThat(actual).isEqualTo(expected)

}

@Test
fun shouldLoadHtmlFileAsJsoupDocument() {
//val file = File("html/HarryPotter_Dekhvhai.html")
val file = ClassLoader.getSystemResource("HarryPotter_Dekhvhai.html").readText()

val document = Jsoup.parse(file, "UTF-8")

assertThat(document).isNotNull()
}

@Test
fun getMovieUrlsToBrowse() {
val scrapper = DekhvhaiScrapper()
val movieLinks = scrapper.getListOfMovieLinksFromSearchResult(dekhvhai)
assertThat(movieLinks.size).isEqualTo(4)

assertThat(movieLinks[0]).isEqualTo("http://dekhvhai.com/movie.php?imdbid=tt1201607&cat=English%20Movie")

}

}
Loading

0 comments on commit 85d4fc1

Please sign in to comment.