Skip to content
This repository has been archived by the owner on Jan 26, 2023. It is now read-only.

S song develop #12

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Search My Profile in Github

## branch

### hunki - like develop

### feature_hunki : using to make feat
35 changes: 34 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}

android {
Expand Down Expand Up @@ -30,16 +31,48 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
dataBinding{
enabled = true
}
kotlinOptions{
jvmTarget = "1.8"
}
}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

// retrofit
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'

// coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'

// liveData
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'

// viewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$version_lifecycle"

// ktx
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.activity:activity-ktx:1.1.0"

// Room database
implementation "androidx.room:room-runtime:$version_room"
kapt "androidx.room:room-compiler:$version_room"

// Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$version_room"
}
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.siba.searchmvvmpractice">

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

<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/Theme.SearchMVVMPractice">
<activity android:name=".MainActivity">
<activity android:name="com.siba.searchmvvmpractice.ui.presentation.activity.SearchActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
11 changes: 0 additions & 11 deletions app/src/main/java/com/siba/searchmvvmpractice/MainActivity.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.siba.searchmvvmpractice.injection

import android.content.Context
import androidx.lifecycle.ViewModelProvider
import com.siba.searchmvvmpractice.local.database.SearchTermDatabase
import com.siba.searchmvvmpractice.remote.RetrofitService
import com.siba.searchmvvmpractice.remote.api.RetrofitBuilder
import com.siba.searchmvvmpractice.repository.SearchRepository
import com.siba.searchmvvmpractice.ui.base.SearchViewModelFactory

object Injection {

fun provideRetrofitService() : RetrofitService{
return RetrofitBuilder.retrofitService
}

fun provideMainRepository(context : Context) : SearchRepository{
val database = SearchTermDatabase.getInstance(context)
return SearchRepository(provideRetrofitService(),database.searchTermDao)
}

fun provideSearchViewModelFactory(context : Context) : ViewModelProvider.Factory{
return SearchViewModelFactory(provideMainRepository(context))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.siba.searchmvvmpractice.local.dao

import androidx.lifecycle.LiveData
import androidx.room.*
import com.siba.searchmvvmpractice.local.entity.RecentSearchTerm

@Dao
interface SearchTermDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertKeyword(recentSearchTerm: RecentSearchTerm)

@Query("DELETE FROM recent_search_term_table")
fun clear()

@Query("SELECT * FROM recent_search_term_table")
fun getAllKeyword() : LiveData<List<RecentSearchTerm>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.siba.searchmvvmpractice.local.database

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.siba.searchmvvmpractice.local.dao.SearchTermDao
import com.siba.searchmvvmpractice.local.entity.RecentSearchTerm

@Database(entities = [RecentSearchTerm::class],version = 1)
abstract class SearchTermDatabase : RoomDatabase(){
abstract val searchTermDao : SearchTermDao

companion object{
@Volatile
private var INSTANCE : SearchTermDatabase? = null

fun getInstance(context : Context) : SearchTermDatabase{
synchronized(this){
var instance = INSTANCE

if(instance == null){
instance = Room.databaseBuilder(
context.applicationContext,
SearchTermDatabase::class.java,
"search_keyword_history_database2"
).build()
INSTANCE = instance
}
return instance
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.siba.searchmvvmpractice.local.entity

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "recent_search_term_table")

data class RecentSearchTerm(
@PrimaryKey(autoGenerate = true)
val searchTermId : Int = 0,
val keyword : String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.siba.searchmvvmpractice.remote

import com.siba.searchmvvmpractice.remote.model.UserCatalog
import com.siba.searchmvvmpractice.remote.model.UserRepositoryCatalog
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface RetrofitService {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리트로핏 서비스 리트로핏에 대한 서비스? 뭔가 이름이 이상하다고 생각해
나였으면 GitHubSearchService 혹은 SearchService 로 할 것 같아

@GET("search/users")
suspend fun getUsers(
@Query("q") user : String
) : UserCatalog

@GET("search/repositories")
suspend fun getRepositories(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 프로젝트 안에서도 레포지터리가 있고 우리가 원하는 깃헙의 온라인 레포지터리도 있어서 그 두가지의 구분으로 나라면 getOnlineRepositories 뭐 이런 느낌으로 했을 것 같음!

@Query("q") repositoryName : String
) : UserRepositoryCatalog
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.siba.searchmvvmpractice.remote.api

import com.siba.searchmvvmpractice.remote.RetrofitService
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitBuilder {
private const val URL = "https://api.github.com"

private fun getRetrofit() : Retrofit {
return Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}

val retrofitService : RetrofitService = getRetrofit().create(RetrofitService::class.java)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.siba.searchmvvmpractice.remote.model

import com.google.gson.annotations.SerializedName

data class UserCatalog(
@SerializedName("total_count")
val total_count : Int,
@SerializedName("incomplete_results")
val incomplete_results : Boolean,
@SerializedName("items")
val users : List<Users>
)
data class Users (
@SerializedName("login")
val login : String,
@SerializedName("id")
val id : Int,
@SerializedName("node_id")
val node_id : String,
@SerializedName("avatar_url")
val avatar_url : String,
@SerializedName("gravatar_id")
val gravatar_id : String,
@SerializedName("url")
val url : String,
@SerializedName("html_url")
val html_url : String,
@SerializedName("followers_url")
val followers_url : String,
@SerializedName("following_url")
val following_url : String,
@SerializedName("gists_url")
val gists_url : String,
@SerializedName("starred_url")
val starred_url : String,
@SerializedName("subscriptions_url")
val subscriptions_url : String,
@SerializedName("organizations_url")
val organizations_url : String,
@SerializedName("repos_url")
val repos_url : String,
@SerializedName("events_url")
val events_url : String,
@SerializedName("received_events_url")
val received_events_url : String,
@SerializedName("type")
val type : String,
@SerializedName("site_admin")
val site_admin : Boolean,
@SerializedName("score")
val score : Double
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.siba.searchmvvmpractice.remote.model

import com.google.gson.annotations.SerializedName

data class UserRepositoryCatalog (
@SerializedName("total_count")
val total_count : Int,
@SerializedName("incomplete_results")
val incomplete_results : Boolean,
@SerializedName("items")
val userRepository : List<UserRepository>
)
data class UserRepository (
@SerializedName("full_name")
val full_name : String,
@SerializedName("html_url")
val html_url : String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.siba.searchmvvmpractice.repository

import com.siba.searchmvvmpractice.local.dao.SearchTermDao
import com.siba.searchmvvmpractice.local.entity.RecentSearchTerm
import com.siba.searchmvvmpractice.remote.RetrofitService
import com.siba.searchmvvmpractice.remote.model.UserCatalog
import com.siba.searchmvvmpractice.remote.model.UserRepositoryCatalog
import com.siba.searchmvvmpractice.remote.model.Users

class SearchRepository(
private val retrofitService: RetrofitService,
private val searchTermDao: SearchTermDao
) {
suspend fun fetchUser(userName : String) : UserCatalog = retrofitService.getUsers(userName)

suspend fun fetchRepo(repositoryName : String) : UserRepositoryCatalog = retrofitService.getRepositories(repositoryName)

suspend fun insert(recentSearchTerm: RecentSearchTerm){
searchTermDao.insertKeyword(recentSearchTerm)
}

fun getAll() = searchTermDao.getAllKeyword()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.siba.searchmvvmpractice.ui.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.siba.searchmvvmpractice.BR
import com.siba.searchmvvmpractice.R
import com.siba.searchmvvmpractice.databinding.RepoItemBinding
import com.siba.searchmvvmpractice.remote.model.UserRepository
import com.siba.searchmvvmpractice.remote.model.UserRepositoryCatalog

class RepoAdapter<B : RepoItemBinding> : RecyclerView.Adapter<RepoAdapter<B>.RepoViewHolder<B>>() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제네릭을 써서 컴파일시 강한 타입 검사와 손쉬운 타입 변화가 될 수 있는 건 좋은데 어짜피 해당 뷰 홀더에는 하나의 뷰 홀더를 사용하고 있고
굳이 Fragment에서 뷰홀더가 써야하는 RepoItemBinding을 엑티비티에서 이런 타입이다 선언해줄 필요가 있을까..? 이미 레포지터리를 위한 어뎁터인데 당연하게 안에 들어가는 바인딩은 레포지터리 아이템에 대한 바인딩이겠지..?

그래서 뭔가 RepoItemBinding 라는 클래스 타입을 B라는 제네릭으로 축약해버린거 같아서 아쉽다...

var data = mutableListOf<UserRepository>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepoViewHolder<B> =
RepoViewHolder<B>(LayoutInflater.from(parent.context).inflate(R.layout.repo_item,parent,false))

override fun onBindViewHolder(holder: RepoViewHolder<B>, position: Int) {
holder.bind(data[position])
}

override fun getItemCount(): Int = data.size

inner class RepoViewHolder<B : RepoItemBinding>(itemView : View) : RecyclerView.ViewHolder(itemView) {
val binding : B = DataBindingUtil.bind(itemView)!!
fun bind(userRepository : UserRepository){
binding.setVariable(BR.userRepository,userRepository)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inner class RepoViewHolder<B : RepoItemBinding>(itemView : View) : RecyclerView.ViewHolder(itemView) {
val binding : B = DataBindingUtil.bind(itemView)!!
fun bind(userRepository : UserRepository){
binding.setVariable(BR.userRepository,userRepository)
}
}
inner class RepoViewHolde(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(userRepository : UserRepository){
binding.setVariable(BR.userRepository,userRepository)
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

역시천재


}
Loading