Skip to content

Commit

Permalink
Android trending repositories project
Browse files Browse the repository at this point in the history
  • Loading branch information
sla8c committed Aug 19, 2018
1 parent fb086cd commit 16bdd9f
Show file tree
Hide file tree
Showing 69 changed files with 2,494 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild

1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
95 changes: 95 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'


android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "lum.steve.githubtrendsapp"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests.returnDefaultValues = true
}

flavorDimensions "default"

// If you need to add more flavors, consider using flavor dimensions.
productFlavors {
prod {
dimension "default"
}
mock {
applicationIdSuffix = ".mock"
dimension "default"
}
}
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:2.0.1'
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

implementation "com.android.support:design:$rootProject.supportLibraryVersion"
implementation "com.android.support:cardview-v7:$rootProject.supportLibraryVersion"
implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
implementation "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"
implementation "com.android.support:support-v4:$rootProject.supportLibraryVersion"

implementation "com.android.support.constraint:constraint-layout:$rootProject.constraintVersion"

implementation "com.google.guava:guava:$rootProject.guavaVersion"

implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion"
implementation "com.squareup.okhttp3:okhttp:$rootProject.okhttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okhttpVersion"
implementation "com.squareup.retrofit2:adapter-rxjava:$rootProject.retrofitAdapterRxVersion"
implementation "com.google.code.gson:gson:$rootProject.gsonVersion"
implementation "com.squareup.picasso:picasso:$rootProject.picassoVersion"

//rxjava
implementation "io.reactivex:rxandroid:$rootProject.rxandroidVersion"
implementation "io.reactivex:rxjava:$rootProject.rxjavaVersion"

// Dependencies for local unit tests
testImplementation "junit:junit:$rootProject.ext.junitVersion"
testImplementation "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestVersion"
testImplementation "org.mockito:mockito-core:$rootProject.ext.mockitoVersion"

// Android Testing Support Library's runner and rules
androidTestImplementation "com.android.support.test:runner:$rootProject.ext.runnerVersion"
androidTestImplementation "com.android.support.test:rules:$rootProject.ext.runnerVersion"

// Dependencies for Android unit tests
androidTestImplementation "junit:junit:$rootProject.ext.junitVersion"
androidTestImplementation "org.mockito:mockito-android:$rootProject.ext.mockitoVersion"

// Espresso UI Testing
androidTestImplementation("com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion") {
exclude module: 'jsr305'
}
androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$rootProject.espressoVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-intents:$rootProject.espressoVersion"

}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lum.steve.githubtrendsapp.repos.trend

import android.support.test.espresso.Espresso.onView
import android.support.test.espresso.assertion.ViewAssertions
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
import android.support.test.espresso.matcher.ViewMatchers.withText
import android.support.test.filters.LargeTest
import android.support.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@LargeTest
class TrendingReposActivityTest {

@Rule
@JvmField var rule = ActivityTestRule(TrendingReposActivity::class.java)

@Test
fun repos_DisplayedInUi() {
onView(withText("repo1 desc")).check(ViewAssertions.matches(isDisplayed()))
onView(withText("repo2 desc")).check(ViewAssertions.matches(isDisplayed()))
}

}
27 changes: 27 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="lum.steve.githubtrendsapp">

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

<application
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">

<activity android:name=".repos.trend.TrendingReposActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".repos.detail.ReposDetailActivity"
android:parentActivityName=".repos.trend.TrendingReposActivity" />
</application>

</manifest>
26 changes: 26 additions & 0 deletions app/src/main/java/lum/steve/githubtrendsapp/base/BaseActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package lum.steve.githubtrendsapp.base

import android.content.Context
import android.os.Bundle
import android.support.v7.app.AppCompatActivity

abstract class BaseActivity<in V : BaseView, T : BasePresenter<V>> : AppCompatActivity(), BaseView {

protected abstract var presenter: T

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

presenter.attachView(this as V)
}

override fun getContext(): Context {
return this
}

override fun onDestroy() {
super.onDestroy()
presenter.detachView()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package lum.steve.githubtrendsapp.base

interface BasePresenter<in V : BaseView> {

fun attachView(view: V)

fun detachView()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package lum.steve.githubtrendsapp.base

import rx.Scheduler
import rx.Subscription
import rx.subscriptions.CompositeSubscription

/**
* Created by andrewkhristyan on 10/2/16.
*/
open class BasePresenterImpl<V : BaseView>(protected var ioScheduler: Scheduler?, protected var mainScheduler: Scheduler?) : BasePresenter<V> {

protected var mView: V? = null

private val compositeSubscription = CompositeSubscription()

override fun attachView(view: V) {
mView = view
}

override fun detachView() {
compositeSubscription.clear()
mView = null
}

protected fun addSubscription(subscription: Subscription) {
this.compositeSubscription.add(subscription)
}

}
8 changes: 8 additions & 0 deletions app/src/main/java/lum/steve/githubtrendsapp/base/BaseView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package lum.steve.githubtrendsapp.base

import android.content.Context

interface BaseView {

fun getContext(): Context
}
44 changes: 44 additions & 0 deletions app/src/main/java/lum/steve/githubtrendsapp/repos/ReposAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package lum.steve.githubtrendsapp.repos

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.squareup.picasso.Picasso
import lum.steve.githubtrendsapp.R
import lum.steve.githubtrendsapp.repos.data.remote.model.GitHubSearchResults
import lum.steve.githubtrendsapp.repos.data.remote.model.Item
import lum.steve.githubtrendsapp.repos.trend.TrendingReposActivity


internal class ReposAdapter(private var searchResults: GitHubSearchResults?, private val context: Context,
private val onItemClickListener: TrendingReposActivity.OnItemClickListener) : RecyclerView.Adapter<ReposViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReposViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item_repo, parent, false)
return ReposViewHolder(v)
}

override fun onBindViewHolder(holder: ReposViewHolder, position: Int) {
val item : Item = searchResults!!.items!![position]

holder.textViewBio.text = item.description
holder.textViewName.text = item.fullName
Picasso.with(context).load(item.owner!!.avatarUrl).into(holder.imageViewAvatar)

holder.itemView.setOnClickListener {
onItemClickListener.onItemClick(item)
}
}

override fun getItemCount(): Int {
return if (searchResults == null) {
0
} else searchResults!!.items!!.size
}

fun setItems(reposList: GitHubSearchResults) {
this.searchResults = reposList
notifyDataSetChanged()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package lum.steve.githubtrendsapp.repos

import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.ImageView
import android.widget.TextView

import lum.steve.githubtrendsapp.R

internal class ReposViewHolder(v: View) : RecyclerView.ViewHolder(v) {

val textViewBio: TextView
val textViewName: TextView
val imageViewAvatar: ImageView

init {
imageViewAvatar = v.findViewById<View>(R.id.imageview_avatar) as ImageView
textViewName = v.findViewById<View>(R.id.textview_username) as TextView
textViewBio = v.findViewById<View>(R.id.textview_desc) as TextView
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package lum.steve.githubtrendsapp.repos.data

import lum.steve.githubtrendsapp.repos.data.remote.model.GitHubRepoDetail
import lum.steve.githubtrendsapp.repos.data.remote.model.GitHubSearchResults
import rx.Observable

interface ReposRepository {
fun searchRepos(q: String): Observable<GitHubSearchResults>
fun fetchRepoInfo(owner: String, repo: String): Observable<GitHubRepoDetail>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package lum.steve.githubtrendsapp.repos.data

import lum.steve.githubtrendsapp.repos.data.remote.GithubService
import lum.steve.githubtrendsapp.repos.data.remote.model.GitHubRepoDetail
import lum.steve.githubtrendsapp.repos.data.remote.model.GitHubSearchResults
import rx.Observable

class ReposRepositoryImpl(val githubService: GithubService) : ReposRepository {

override fun searchRepos(q: String): Observable<GitHubSearchResults> {
return Observable.defer { githubService.loadTrendingRepos("stars", "desc", q) }
}

override fun fetchRepoInfo(owner: String, repo:String): Observable<GitHubRepoDetail> {
return Observable.defer { githubService.fetchRepoInfo(owner, repo) }
}

companion object {

private var INSTANCE: ReposRepository? = null

/**
* Singleton instantiaton
*/
@JvmStatic
fun getInstance(githubService: GithubService): ReposRepository {
return INSTANCE ?: ReposRepositoryImpl(githubService = githubService)
.apply { INSTANCE = this }
}
}

}
Loading

0 comments on commit 16bdd9f

Please sign in to comment.