Skip to content

Commit e7e97d8

Browse files
committed
initial commit
0 parents  commit e7e97d8

72 files changed

Lines changed: 2637 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#built application files
2+
*.apk
3+
*.ap_
4+
5+
# files for the dex VM
6+
*.dex
7+
8+
# Java class files
9+
*.class
10+
11+
# generated files
12+
bin/
13+
gen/
14+
15+
# Local configuration file (sdk path, etc)
16+
local.properties
17+
18+
# Windows thumbnail db
19+
Thumbs.db
20+
21+
# OSX files
22+
.DS_Store
23+
24+
# Android Studio
25+
*.iml
26+
.idea
27+
.gradle
28+
/build
29+
/captures
30+
.externalNativeBuild
31+
.cxx
32+
output.json
33+
34+
# Deprecated
35+
/*/out
36+
/*/*/build
37+
/*/*/production
38+
*.iws
39+
*.ipr
40+
*~
41+
*.swp

app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

app/build.gradle

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
apply plugin: 'com.android.application'
2+
apply plugin: 'kotlin-android'
3+
apply plugin: 'kotlin-android-extensions'
4+
apply plugin: 'kotlin-kapt'
5+
apply plugin: "androidx.navigation.safeargs.kotlin"
6+
7+
android {
8+
compileSdkVersion 30
9+
buildToolsVersion "30.0.0"
10+
11+
defaultConfig {
12+
applicationId "com.koshsu.githubsearch"
13+
minSdkVersion 21
14+
targetSdkVersion 30
15+
versionCode 1
16+
versionName "1.0"
17+
18+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19+
}
20+
21+
buildTypes {
22+
release {
23+
minifyEnabled false
24+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25+
}
26+
}
27+
28+
compileOptions {
29+
sourceCompatibility JavaVersion.VERSION_1_8
30+
targetCompatibility JavaVersion.VERSION_1_8
31+
}
32+
33+
kotlinOptions {
34+
jvmTarget = "1.8"
35+
}
36+
37+
buildFeatures {
38+
dataBinding true
39+
}
40+
41+
}
42+
43+
dependencies {
44+
implementation fileTree(dir: "libs", include: ["*.jar"])
45+
testImplementation 'junit:junit:4.12'
46+
47+
// Android
48+
implementation "androidx.core:core-ktx:1.3.2"
49+
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
50+
51+
// Kotlin
52+
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10"
53+
54+
// Coroutines
55+
def coroutines_version = '1.3.6'
56+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
57+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
58+
59+
// ViewModel & LiveData
60+
def lifecycle_version = '2.2.0'
61+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
62+
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
63+
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
64+
65+
// Material Design & Widgets
66+
implementation 'com.google.android.material:material:1.2.1'
67+
implementation 'androidx.appcompat:appcompat:1.2.0'
68+
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
69+
70+
// Kodein (DI)
71+
def kodein_version = '6.2.1'
72+
implementation "org.kodein.di:kodein-di-generic-jvm:$kodein_version"
73+
implementation "org.kodein.di:kodein-di-framework-android-x:$kodein_version"
74+
75+
// Retrofit and GSON
76+
def retrofit_version = '2.9.0'
77+
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
78+
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
79+
80+
// Room (DB)
81+
def room_version = '2.2.5'
82+
implementation "androidx.room:room-runtime:$room_version"
83+
implementation "androidx.room:room-ktx:$room_version"
84+
kapt "androidx.room:room-compiler:$room_version"
85+
86+
// Navigation Architecture
87+
def navigation_version = '2.3.1'
88+
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
89+
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
90+
91+
// Glide
92+
def glide_version = '4.11.0'
93+
implementation "com.github.bumptech.glide:glide:$glide_version"
94+
kapt "com.github.bumptech.glide:compiler:$glide_version"
95+
96+
// Preference
97+
implementation 'androidx.preference:preference-ktx:1.1.1'
98+
}

app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile

app/src/main/AndroidManifest.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.koshsu.githubsearch">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7+
8+
<application
9+
android:name=".application.App"
10+
android:allowBackup="true"
11+
android:icon="@mipmap/logo_github_search"
12+
android:label="@string/app_name"
13+
android:roundIcon="@mipmap/logo_github_search_round"
14+
android:supportsRtl="true"
15+
android:theme="@style/AppTheme">
16+
<activity android:name=".ui.MainActivity">
17+
<intent-filter>
18+
<action android:name="android.intent.action.MAIN" />
19+
20+
<category android:name="android.intent.category.LAUNCHER" />
21+
</intent-filter>
22+
</activity>
23+
</application>
24+
25+
</manifest>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.koshsu.githubsearch.application
2+
3+
import android.app.Application
4+
import com.koshsu.githubsearch.data.database.AppDatabase
5+
import com.koshsu.githubsearch.data.network.GithubApi
6+
import com.koshsu.githubsearch.data.preference.PreferenceProvider
7+
import com.koshsu.githubsearch.data.repository.DetailsRepository
8+
import com.koshsu.githubsearch.data.repository.SearchRepository
9+
import com.koshsu.githubsearch.ui.details.DetailsViewModelFactory
10+
import com.koshsu.githubsearch.ui.search.SearchViewModelFactory
11+
import org.kodein.di.Kodein
12+
import org.kodein.di.KodeinAware
13+
import org.kodein.di.android.x.androidXModule
14+
import org.kodein.di.generic.bind
15+
import org.kodein.di.generic.instance
16+
import org.kodein.di.generic.provider
17+
import org.kodein.di.generic.singleton
18+
19+
class App : Application(), KodeinAware {
20+
21+
override val kodein = Kodein.lazy {
22+
import(androidXModule(this@App))
23+
24+
bind() from singleton { GithubApi() }
25+
bind() from singleton { AppDatabase(instance()) }
26+
bind() from singleton { PreferenceProvider(instance()) }
27+
bind() from singleton { SearchRepository(instance(), instance(), instance(), instance()) }
28+
bind() from singleton { DetailsRepository(instance(), instance(), instance()) }
29+
bind() from provider { SearchViewModelFactory(instance()) }
30+
bind() from provider { DetailsViewModelFactory(instance()) }
31+
}
32+
33+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.koshsu.githubsearch.data
2+
3+
// Status of Data in Repository
4+
sealed class Status <M> {
5+
6+
// Status - Loading
7+
class Loading<M> : Status<M>()
8+
9+
// Status - Success
10+
data class Success<M>(val data: M) : Status<M>()
11+
12+
// Status - Error
13+
data class Error<M>(val errorMessage: String) : Status<M>()
14+
15+
companion object {
16+
17+
// Returns Loading instance
18+
fun <M> loading() = Loading<M>()
19+
20+
// Returns Success instance with data to emit
21+
fun <M> success(data: M) =
22+
Success(data)
23+
24+
// Returns Error instance with error message
25+
fun <M> error(errorMessage: String) =
26+
Error<M>(errorMessage)
27+
28+
}
29+
30+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.koshsu.githubsearch.data.database
2+
3+
import android.content.Context
4+
import androidx.room.Database
5+
import androidx.room.Room
6+
import androidx.room.RoomDatabase
7+
import com.koshsu.githubsearch.data.database.dao.RepoAndOwnerDao
8+
import com.koshsu.githubsearch.data.database.model.Owner
9+
import com.koshsu.githubsearch.data.database.model.Repo
10+
11+
@Database(
12+
entities = [Repo::class, Owner::class],
13+
version = 1,
14+
exportSchema = false
15+
)
16+
abstract class AppDatabase : RoomDatabase() {
17+
18+
abstract fun getRepoAndOwnerDao() : RepoAndOwnerDao
19+
20+
companion object {
21+
22+
// Singleton prevents multiple instances of database opening at the same time.
23+
@Volatile
24+
private var instance: AppDatabase? = null
25+
26+
// If the instance is not null, then return it, if it is, then build the database
27+
operator fun invoke(context: Context) = instance ?: synchronized(this) {
28+
instance ?: buildDatabase(context).also {
29+
instance = it
30+
}
31+
}
32+
33+
// Function that builds the database
34+
private fun buildDatabase(context: Context) = Room.databaseBuilder(
35+
context.applicationContext,
36+
AppDatabase::class.java,
37+
"database"
38+
).build()
39+
40+
}
41+
42+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.koshsu.githubsearch.data.database.dao
2+
3+
import androidx.room.*
4+
import com.koshsu.githubsearch.data.database.model.Owner
5+
import com.koshsu.githubsearch.data.database.model.Repo
6+
import com.koshsu.githubsearch.data.database.model.RepoWithOwner
7+
import kotlinx.coroutines.flow.Flow
8+
import kotlinx.coroutines.flow.flow
9+
10+
/**
11+
* This class provides general DAO for both entities [Repo], [Owner]
12+
* "Abstract class" is used instead of "interface" to provide ability
13+
* to use my own functions for insertion and reading data
14+
* */
15+
@Dao
16+
abstract class RepoAndOwnerDao {
17+
18+
/** Public functions for database manipulations */
19+
20+
fun insertReposWithOwner(repos: List<Repo>) {
21+
// delete previous data
22+
deleteAllRepos()
23+
deleteAllOwners()
24+
25+
// save new data
26+
for (r in repos) {
27+
r.owner.repoId = r.id
28+
upsertOwner(r.owner)
29+
}
30+
insertRepos(repos)
31+
}
32+
33+
fun getReposWithOwner() : Flow<List<Repo>> {
34+
val reposWithOwner = _getAllWithOwnerSync()
35+
val repos: MutableList<Repo> = mutableListOf()
36+
for (i in reposWithOwner) {
37+
i.repo.owner = i.owner
38+
repos.add(i.repo)
39+
}
40+
return flow { emit(repos) }
41+
}
42+
43+
fun getRepoWithOwnerById(repoId: Long) : Flow<Repo> {
44+
val repoWithOwner = _getRepoWithOwnerById(repoId)
45+
val repo = repoWithOwner.repo
46+
repo.owner = repoWithOwner.owner
47+
return flow { emit(repo) }
48+
}
49+
50+
51+
/**
52+
* Generated functions by Room
53+
* "_"prefix means that these functions are private for this class
54+
* (Room doesn't allow private functions)
55+
*/
56+
57+
// insert or update if exists
58+
@Insert(onConflict = OnConflictStrategy.REPLACE)
59+
abstract fun upsertRepos(repos: List<Repo>)
60+
61+
@Insert(onConflict = OnConflictStrategy.IGNORE)
62+
abstract fun insertRepos(repos: List<Repo>)
63+
64+
@Insert(onConflict = OnConflictStrategy.REPLACE)
65+
abstract fun upsertRepo(vararg repo: Repo)
66+
67+
@Insert(onConflict = OnConflictStrategy.REPLACE)
68+
abstract fun upsertOwner(owner: Owner)
69+
70+
@Transaction
71+
@Query("SELECT * FROM Repo, Owner WHERE Repo.id = Owner.repoId ORDER BY Repo.stars DESC")
72+
abstract fun _getAllWithOwnerSync() : List<RepoWithOwner>
73+
74+
@Transaction
75+
@Query("SELECT * FROM Repo INNER JOIN Owner ON Owner.repoId = Repo.id WHERE Repo.id = :repoId")
76+
abstract fun _getRepoWithOwnerById(repoId: Long) : RepoWithOwner
77+
78+
@Query("DELETE FROM Repo")
79+
abstract fun deleteAllRepos()
80+
81+
@Query("DELETE FROM Owner")
82+
abstract fun deleteAllOwners()
83+
84+
}

0 commit comments

Comments
 (0)