Skip to content

Commit

Permalink
add search activity
Browse files Browse the repository at this point in the history
  • Loading branch information
deva666 committed Jan 12, 2018
1 parent 181909b commit dcd1a32
Show file tree
Hide file tree
Showing 15 changed files with 218 additions and 15 deletions.
29 changes: 20 additions & 9 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity
android:name=".startup.StartupActivity"
android:label="@string/app_name"
Expand All @@ -22,34 +21,46 @@
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

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

<activity
android:name=".categories.SelectCategoriesActivity"
android:label="@string/categories"
android:parentActivityName=".articles.ArticlesActivity"
android:theme="@style/AppTheme.NoActionBar"
android:label="@string/categories"/>
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".articles.ArticlesActivity"
android:label="@string/title_activity_articles"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.app.default_searchable"
android:value=".search.SearchActivity"/>
</activity>

<activity
android:name=".articledetails.ArticleDetailsActivity"
android:parentActivityName=".articles.ArticlesActivity"
android:theme="@style/AppTheme.NoActionBar">
</activity>

<activity
android:name=".settings.SettingsActivity"
android:label="@string/title_activity_settings"
android:parentActivityName=".articles.ArticlesActivity"
android:theme="@style/AppTheme.Translucent"
android:label="@string/title_activity_settings">
android:theme="@style/AppTheme.Translucent">
</activity>
<activity
android:name=".search.SearchActivity"
android:parentActivityName=".articles.ArticlesActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable"/>
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
</activity>

</application>

</manifest>
4 changes: 4 additions & 0 deletions app/src/main/kotlin/com/markodevcic/newsreader/api/NewsApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ interface NewsApi {
fun getSources(@Query("category") category: String,
@Query("apiKey") apiKey: String = BuildConfig.NEWS_READER_API_KEY,
@Query("language") lang: String = "en"): Call<SourceResponse>

@GET("everything")
fun search(@Query("q") query: String,
@Query("apiKey") apiKey: String = BuildConfig.NEWS_READER_API_KEY) : Call<ArticleResponse>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.markodevcic.newsreader.articles

import android.animation.ObjectAnimator
import android.app.SearchManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
Expand All @@ -24,6 +27,7 @@ import com.markodevcic.newsreader.categories.SelectCategoriesActivity
import com.markodevcic.newsreader.data.Article
import com.markodevcic.newsreader.extensions.startActivity
import com.markodevcic.newsreader.injection.Injector
import com.markodevcic.newsreader.search.SearchActivity
import com.markodevcic.newsreader.settings.SettingsActivity
import com.markodevcic.newsreader.util.CATEGORIES_TO_RES_MAP
import com.markodevcic.newsreader.util.KEY_CATEGORIES
Expand Down Expand Up @@ -66,6 +70,7 @@ class ArticlesActivity : AppCompatActivity(), NavigationView.OnNavigationItemSel
setSupportActionBar(toolbar)

setupArticlesView()
setupSearchView()

btnMarkAllRead.setOnClickListener {
val articles = adapter?.articles ?: return@setOnClickListener
Expand Down Expand Up @@ -113,6 +118,12 @@ class ArticlesActivity : AppCompatActivity(), NavigationView.OnNavigationItemSel
})
}

private fun setupSearchView() {
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
searchView.setIconifiedByDefault(true)
searchView.setSearchableInfo(searchManager.getSearchableInfo(ComponentName(this, SearchActivity::class.java)))
}

private fun setupMenuItems(): Menu {
val menu = navigationView.menu
val selectedCategories = sharedPrefs.getStringSet(KEY_CATEGORIES, null)
Expand Down Expand Up @@ -291,7 +302,7 @@ class ArticlesActivity : AppCompatActivity(), NavigationView.OnNavigationItemSel

override fun onArticlesDownloaded(count: Int) {
val message: String = if (count > 0) {
"Downloaded $count ${if (count == 1) "article" else "articles" }"
"Downloaded $count ${if (count == 1) "article" else "articles"}"
} else {
"No new articles"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ class ArticlesViewHolder(private val view: View) : RecyclerView.ViewHolder(view)
date.setTextColor(ContextCompat.getColor(view.context, android.R.color.tertiary_text_light))
}

category.text = view.context.getText(CATEGORIES_TO_RES_MAP[article.category]!!)
if (article.category == null) {
category.visibility = View.GONE
} else {
category.text = view.context.getText(CATEGORIES_TO_RES_MAP[article.category!!]!!)
}
date.text = formatDate(article.publishedAt)
val imageUrl = article.urlToImage
if (imageUrl != null && imageUrl.isNotBlank()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ open class Article : RealmObject() {
var author: String? = null
var description: String? = null
var urlToImage: String? = null
lateinit var category: String
var category: String? = null
@JsonDeserialize(using = DateToLongDeserializer::class)
var publishedAt: Long? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.markodevcic.newsreader.injection

import com.markodevcic.newsreader.articles.ArticlesActivity
import com.markodevcic.newsreader.categories.SelectCategoriesActivity
import com.markodevcic.newsreader.search.SearchActivity
import com.markodevcic.newsreader.settings.SettingsActivity
import com.markodevcic.newsreader.startup.StartupActivity
import dagger.Component
Expand All @@ -13,4 +14,5 @@ interface AppComponent {
fun inject(articlesActivity: ArticlesActivity)
fun inject(selectCategoriesActivity: SelectCategoriesActivity)
fun inject(settingsActivity: SettingsActivity)
fun inject(searchActivity: SearchActivity)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.markodevcic.newsreader.search

import android.app.SearchManager
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.View
import com.markodevcic.newsreader.R
import com.markodevcic.newsreader.articles.ArticlesViewHolder
import com.markodevcic.newsreader.injection.Injector
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_search.*
import kotlinx.android.synthetic.main.content_main.*
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import javax.inject.Inject

class SearchActivity : AppCompatActivity() {

private val job = Job()

@Inject
lateinit var presenter: SearchPresenter
private var adapter: SearchAdapter? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)

Injector.appComponent.inject(this)

setSupportActionBar(toolbar)
supportActionBar?.setHomeButtonEnabled(true)

setupArticlesView()

if (Intent.ACTION_SEARCH == intent.action) {
val query = intent.getStringExtra(SearchManager.QUERY)
supportActionBar?.title = query.capitalize()
launch(UI + job) {
progressBar.visibility = View.VISIBLE
val articles = presenter.search(query)
adapter = SearchAdapter(articles)
articlesView.adapter = adapter
progressBar.visibility = View.GONE
}
} else {
throw IllegalStateException("this activity should be called from search view")
}
}

private fun setupArticlesView() {
articlesView.setHasFixedSize(true)
articlesView.isDrawingCacheEnabled = true
articlesView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
articlesView.layoutManager = LinearLayoutManager(this@SearchActivity)
articlesView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView?, scrollState: Int) {
super.onScrollStateChanged(recyclerView, scrollState)
val picasso = Picasso.with(this@SearchActivity.applicationContext)
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
picasso.resumeTag(ArticlesViewHolder)
} else {
picasso.pauseTag(ArticlesViewHolder)
}
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.markodevcic.newsreader.search

import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.markodevcic.newsreader.R
import com.markodevcic.newsreader.articles.ArticlesViewHolder
import com.markodevcic.newsreader.data.Article

class SearchAdapter(private val articles: List<Article>) : RecyclerView.Adapter<ArticlesViewHolder>() {

init {
setHasStableIds(true)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticlesViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.item_article, parent, false)
return ArticlesViewHolder(view)
}

override fun onBindViewHolder(holder: ArticlesViewHolder, position: Int) {
holder.bind(articles[position])
}

override fun getItemCount(): Int = articles.size
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.markodevcic.newsreader.search

import com.markodevcic.newsreader.data.Article
import com.markodevcic.newsreader.sync.SyncUseCase
import javax.inject.Inject

class SearchPresenter @Inject constructor(private val syncUseCase: SyncUseCase) {

suspend fun search(query: String): List<Article> = syncUseCase.search(query)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.markodevcic.newsreader.sync

import com.markodevcic.newsreader.data.Article
import com.markodevcic.newsreader.data.Source

interface SyncUseCase {
suspend fun downloadSourcesAsync(categories: Collection<String>)
suspend fun downloadArticlesAsync(source: Source): Int
suspend fun search(query: String): List<Article>
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import javax.inject.Provider
class SyncUseCaseImpl(private val newsApi: NewsApi,
private val sourcesRepository: Provider<Repository<Source>>,
private val articlesRepository: Provider<Repository<Article>>) : SyncUseCase {

override suspend fun downloadSourcesAsync(categories: Collection<String>) {
sourcesRepository.get().use { repo ->
val downloadJobs = categories.map { cat -> newsApi.getSources(cat).launchAsync() }
Expand Down Expand Up @@ -44,4 +43,10 @@ class SyncUseCaseImpl(private val newsApi: NewsApi,
}.await()
return downloadCount
}
}

suspend override fun search(query: String): List<Article> {
return newsApi.search(query)
.executeAsync()
.articles
}
}
34 changes: 34 additions & 0 deletions app/src/main/res/layout/activity_search.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.markodevcic.newsreader.search.SearchActivity">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay">

</android.support.v7.widget.Toolbar>

</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_main"/>

<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_gravity="center"/>
</android.support.design.widget.CoordinatorLayout>

11 changes: 10 additions & 1 deletion app/src/main/res/layout/app_bar_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
app:popupTheme="@style/AppTheme.PopupOverlay">

<SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
/>
</android.support.v7.widget.Toolbar>

</android.support.design.widget.AppBarLayout>

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@
<string name="_5_days">5 days</string>
<string name="_7_days">7 days</string>
<string name="settings">Settings</string>
<string name="search_label">Search</string>
</resources>
10 changes: 10 additions & 0 deletions app/src/main/res/xml/searchable.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_label"
android:searchSuggestSelection=" ?"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:voiceLanguage="en"

>
</searchable>

0 comments on commit dcd1a32

Please sign in to comment.