Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 9

Catch up

ANDROID MANIFEST
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="pe.edu.upc.catchup">

<dist:module dist:instant="true"/>

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

<application
android:name=".CatchUpApp"
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/AppTheme">
<meta-data android:name="DATABASE" android:value="catch_up.db" />
<meta-data android:name="VERSION" android:value="1" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="pe.edu.upc.catchup.models" />
<activity
android:name=".controllers.activities.SourceActivity"
android:label="@string/title_activity_source"
android:parentActivityName=".controllers.activities.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="pe.edu.upc.catchup.controllers.activities.MainActivity"/>
</activity>
<activity
android:name=".controllers.activities.MainActivity"
android:launchMode="singleInstance"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

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

</manifest>

ARTICLESADAPTER

package pe.edu.upc.catchup.adapters

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_article.view.*
import pe.edu.upc.catchup.R
import pe.edu.upc.catchup.models.Article

class ArticlesAdapter(var articles: List<Article>) :


RecyclerView.Adapter<ArticlesAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val pictureImageView = itemView.pictureImageView
val titleTextView = itemView.titleTextView
val moreButton = itemView.moreButton

fun bindTo(article: Article) {


pictureImageView.apply {
setDefaultImageResId(R.mipmap.ic_launcher)
setErrorImageResId(R.mipmap.ic_launcher)
setImageUrl(article.urlToImage)
}
titleTextView.text = article.title
moreButton.setOnClickListener {

}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):


ArticlesAdapter.ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.item_article, parent, false))
}

override fun getItemCount(): Int {


return articles.size
}

override fun onBindViewHolder(holder: ArticlesAdapter.ViewHolder, position: Int) {


holder.bindTo(articles[position])
}

SOURCE ADAPTER

package pe.edu.upc.catchup.adapters

import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.view.*
import kotlinx.android.synthetic.main.item_source.view.*
import pe.edu.upc.catchup.R
import pe.edu.upc.catchup.controllers.activities.SourceActivity
import pe.edu.upc.catchup.models.Favorite
import pe.edu.upc.catchup.models.Source
import java.io.Serializable

class SourcesAdapter(var sources: List<Source>) :


RecyclerView.Adapter<SourcesAdapter.ViewHolder>() {
var selectedIndex = 0
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val context = itemView.context
val itemSource = itemView.itemSource
val logoImageView = itemView.logoImageView
val favoriteImageView = itemView.favoriteImageView
val nameTextView = itemView.nameTextView

fun bindTo(source: Source) {


logoImageView.apply {
setDefaultImageResId(R.mipmap.ic_launcher)
setErrorImageResId(R.mipmap.ic_launcher)
Log.d("NewsApi", source.urlToLogo)
setImageUrl(source.urlToLogo)
}
favoriteImageView.setImageResource(Favorite.resourceForState(source.isFavorite))
nameTextView.text = source.name
itemSource.setOnClickListener {
this@SourcesAdapter.selectedIndex = adapterPosition
val intent = Intent(it.context, SourceActivity::class.java)
intent.putExtra("source", source as Serializable)
it.context.startActivity(intent)
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SourcesAdapter.ViewHolder


{
return ViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.item_source, parent, false))
}

override fun getItemCount(): Int {


return sources.size
}

override fun onBindViewHolder(holder: SourcesAdapter.ViewHolder, position: Int) {


holder.bindTo(sources.get(position))
}

fun verifyItemChanged() {
notifyItemChanged(selectedIndex)

}
}

CONTROLLERS -> ACTIVITIES ->MAIN ACTIVITY


package pe.edu.upc.catchup.controllers.activities

import android.os.Bundle
import android.view.MenuItem
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import android.widget.TextView
import androidx.fragment.app.Fragment
import pe.edu.upc.catchup.R
import pe.edu.upc.catchup.controllers.fragments.FavoritesFragment
import pe.edu.upc.catchup.controllers.fragments.HomeFragment
import pe.edu.upc.catchup.controllers.fragments.SettingsFragment
import pe.edu.upc.catchup.controllers.fragments.SourcesFragment
import pe.edu.upc.catchup.network.NewsApi

class MainActivity : AppCompatActivity() {


private val onNavigationItemSelectedListener =
BottomNavigationView.OnNavigationItemSelectedListener { item ->
navigateTo(item)
}

override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)

navView.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener)
navigateTo(navView.menu.findItem(R.id.navigation_home))
}

private fun getFragmentFor(item: MenuItem): Fragment {


return when(item.itemId) {
R.id.navigation_home -> HomeFragment()
R.id.navigation_sources -> SourcesFragment()
R.id.navigation_favorites -> FavoritesFragment()
R.id.navigation_settings -> SettingsFragment()
else -> HomeFragment()
}
}

private fun navigateTo(item: MenuItem): Boolean {


item.isChecked = true

return supportFragmentManager
.beginTransaction()
.replace(R.id.content, getFragmentFor(item))
.commit() > 0
}
}

CONTROLLERS -> ATIVITIES -> SOURCACTIVITY

package pe.edu.upc.catchup.controllers.activities

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity;
import pe.edu.upc.catchup.R

import kotlinx.android.synthetic.main.activity_source.*
import kotlinx.android.synthetic.main.content_source.*
import pe.edu.upc.catchup.models.Favorite
import pe.edu.upc.catchup.models.Source

class SourceActivity : AppCompatActivity() {


var isFavorite: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_source)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
intent?.extras?.apply {
val source = getSerializable("source") as Source
logoImageView.apply {
setDefaultImageResId(R.mipmap.ic_launcher)
setErrorImageResId(R.mipmap.ic_launcher)
setImageUrl(source.urlToLogo)
}
nameTextView.text = source.name
descriptionTextView.text = source.description
isFavorite = source.isFavorite
favoriteButton.setImageResource(Favorite.resourceForState(isFavorite) )
favoriteButton.setOnClickListener {
isFavorite = !isFavorite
favoriteButton.setImageResource(Favorite.resourceForState(isFavorite) )
source.isFavorite = isFavorite

}
}
}

CONTROLLER -> FRAGMENTS -> SOURCESFRAGMENT

package pe.edu.upc.catchup.controllers.fragments

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_sources.view.*

import pe.edu.upc.catchup.R
import pe.edu.upc.catchup.adapters.SourcesAdapter
import pe.edu.upc.catchup.models.Source
import pe.edu.upc.catchup.network.NewsApi

/**
* A simple [Fragment] subclass.
*
*/
class SourcesFragment : Fragment() {
private lateinit var sourcesRecyclerView: RecyclerView

private var sources = ArrayList<Source>()


private var sourcesAdapter = SourcesAdapter(sources)

override fun onCreateView(


inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_sources, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {


super.onViewCreated(view, savedInstanceState)
sourcesRecyclerView = view.sourcesRecyclerView
sourcesRecyclerView.apply {
adapter = sourcesAdapter
this.layoutManager = GridLayoutManager(view.context, 2)
}
NewsApi.apiKey = view.context.getString(R.string.new_api_key)
NewsApi.requestSources({
it?.sources?.apply {
Log.d("NewsApi", "Sources count: $size")
sourcesAdapter.sources = this
sourcesAdapter.notifyDataSetChanged()
}
}, {
Log.d("NewsApi", "${it.errorBody} ${it.localizedMessage}")
sourcesAdapter.sources = ArrayList()
sourcesAdapter.notifyDataSetChanged()
})
}

override fun onResume() {


super.onResume()
sourcesAdapter.verifyItemChanged()
}
}

JAVA -> MODELS-> ARTICLE


package pe.edu.upc.catchup.models

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import java.io.Serializable

data class Article(


val source: Source,
val author: String?,
val title: String?,
val description: String?,
val url: String?,
val urlToImage: String?,
val publishedAt: String?,
val content: String?
) : Serializable {
constructor() : this(
Source(),
"",
"",
"",
"",
"",
"",
""
)
}

JAVA -> MDELS->FAVORITE


package pe.edu.upc.catchup.models

import com.orm.SugarRecord
import pe.edu.upc.catchup.R

data class Favorite (


val sourceId: String,
val sourceName: String
) : SugarRecord() {
constructor() : this("", "")
companion object {
fun findFor(source: Source): Favorite? {
val favorites = SugarRecord.find(Favorite::class.java, "source_id = ?", source.id)
return if(favorites.isEmpty()) null else favorites.first()
}

fun resourceForState(state: Boolean): Int {


return if (state) R.drawable.ic_favorite_black_24dp else
R.drawable.ic_favorite_border_black_24dp
}

fun allSourceIds(): String {


val favorites = SugarRecord.findAll(Favorite::class.java)
return favorites.asSequence().filterNot { it.sourceId.isEmpty() }.joinToString
{ it.sourceId }
}
}
}

JAVA->MODELS -> SOURCE


package pe.edu.upc.catchup.models

import android.os.Parcel
import android.os.Parcelable
import com.orm.SugarRecord
import kotlinx.android.parcel.Parceler
import kotlinx.android.parcel.Parcelize
import pe.edu.upc.catchup.network.LogoApi
import java.io.Serializable

data class Source (


val id: String?,
val name: String,
val description: String?,
val url: String?,
val category: String?,
val language: String?,
val country: String?
) : Serializable {
constructor() : this("", "", "", "", "", "", "")
val urlToLogo: String
get() = LogoApi.urlToLogo(url)

var isFavorite: Boolean


get() = SugarRecord.find(Favorite::class.java, "source_id = ?", this.id).size > 0
set(value) {
if((value and (Favorite.findFor(this) is Favorite)) or
(!value and (Favorite.findFor(this) == null))) return
if(value) this.id?.let { Favorite(it, name).save() } else {
Favorite.findFor(this)?.apply { delete() }
}
}
}

JAVA -> NETWORK->CATCHUPCAPP


package pe.edu.upc.catchup

import android.app.Application
import com.androidnetworking.AndroidNetworking
import com.jacksonandroidnetworking.JacksonParserFactory
import com.orm.SugarApp

class CatchUpApp : SugarApp() {


override fun onCreate() {
super.onCreate()
AndroidNetworking.initialize(applicationContext)
AndroidNetworking.setParserFactory(JacksonParserFactory())
}
}

GRADLE SCRIPTS

BUILDGRADLE MODULE
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 28
defaultConfig {
applicationId "pe.edu.upc.catchup"
minSdkVersion 26
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-
rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.1'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.amitshekhar.android:android-networking:1.0.2'
implementation 'com.amitshekhar.android:jackson-android-networking:1.0.2'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.github.satyan:sugar:1.5'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

BUILD GRADLE PEOJECT

// Top-level build file where you can add configuration options common to all sub-
projects/modules.

buildscript {
ext.kotlin_version = '1.3.30'
repositories {
google()
jcenter()

}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()

}
}

task clean(type: Delete) {


delete rootProject.buildDir
}

You might also like