diff --git a/app/build.gradle b/app/build.gradle
index 71490b5..1c710cd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
+ id 'kotlin-kapt'
}
android {
@@ -16,6 +17,11 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
+ buildFeatures {
+ dataBinding true
+ viewBinding true
+ }
+
buildTypes {
release {
minifyEnabled false
@@ -40,4 +46,20 @@ dependencies {
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'androidx.activity:activity-ktx:1.1.0'
+ implementation 'androidx.fragment:fragment-ktx:1.2.5'
+
+ // Lifecycle
+ implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
+
+ // Retrofit
+ implementation 'com.google.code.gson:gson:2.8.6'
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+ implementation "com.squareup.retrofit2:adapter-rxjava2:2.5.0"
+
+ //Glide
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8508fca..9b74e56 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
+
+
+ android:name=".detail.DetailUserActivity"
+ android:exported="false" />
+
@@ -19,5 +24,4 @@
-
\ No newline at end of file
diff --git a/app/src/main/java/place/pic/android/plus/BindingAdapter.kt b/app/src/main/java/place/pic/android/plus/BindingAdapter.kt
new file mode 100644
index 0000000..f1acada
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/BindingAdapter.kt
@@ -0,0 +1,49 @@
+package place.pic.android.plus
+
+import android.widget.ImageView
+import androidx.databinding.BindingAdapter
+import com.bumptech.glide.Glide
+
+object BindingAdapter {
+ @BindingAdapter("setImageUrl")
+ @JvmStatic
+ fun loadImage(imageView: ImageView, url: String) {
+ Glide.with(imageView.context)
+ .load(url)
+ .circleCrop()
+ .into(imageView)
+ }
+
+ @BindingAdapter("setButtonChange")
+ @JvmStatic
+ fun setButtonChange(button: ImageView, compass: Boolean) {
+ when (compass) {
+ true -> button.setBackgroundResource(R.drawable.ic_baseline_clear_24)
+ else -> button.setBackgroundResource(R.drawable.ic_baseline_search_24)
+ }
+ }
+
+ /*
+ @BindingAdapter("TextChangeWatcher")
+ @JvmStatic
+ fun onEditTextWatcher(): TextWatcher {
+ return object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+ override fun afterTextChanged(s: Editable) {
+ }
+ }
+ }
+
+ @BindingAdapter("bindRecyclerView")
+ @JvmStatic
+ fun bindRecyclerView(recyclerView: RecyclerView, searchUserData: MutableList?)
+ {
+ if (recyclerView.adapter != null) {
+ with(recyclerView.adapter as SearchUserAdapter) {
+ submitList(searchUserData)
+ }
+ }
+ }
+ */
+}
diff --git a/app/src/main/java/place/pic/android/plus/MainActivity.kt b/app/src/main/java/place/pic/android/plus/MainActivity.kt
deleted file mode 100644
index b8670ea..0000000
--- a/app/src/main/java/place/pic/android/plus/MainActivity.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package place.pic.android.plus
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-
-class MainActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- }
-}
diff --git a/app/src/main/java/place/pic/android/plus/SingleLiveEvent.kt b/app/src/main/java/place/pic/android/plus/SingleLiveEvent.kt
new file mode 100644
index 0000000..15ee031
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/SingleLiveEvent.kt
@@ -0,0 +1,43 @@
+package place.pic.android.plus
+
+import android.util.Log
+import androidx.annotation.MainThread
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Observer
+import java.util.concurrent.atomic.AtomicBoolean
+
+open class SingleLiveEvent : MutableLiveData() {
+
+ private val mPending = AtomicBoolean(false)
+
+ override fun observe(owner: LifecycleOwner, observer: Observer) {
+ if (hasActiveObservers()) {
+ Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
+ }
+
+ super.observe(
+ owner,
+ Observer { t ->
+ if (mPending.compareAndSet(true, false)) {
+ observer.onChanged(t)
+ }
+ }
+ )
+ }
+
+ @MainThread
+ override fun setValue(t: T?) {
+ mPending.set(true)
+ super.setValue(t)
+ }
+
+ @MainThread
+ fun call() {
+ value = null
+ }
+
+ companion object {
+ private val TAG = "SingleLiveEvent"
+ }
+}
diff --git a/app/src/main/java/place/pic/android/plus/data/remote/GithubService.kt b/app/src/main/java/place/pic/android/plus/data/remote/GithubService.kt
new file mode 100644
index 0000000..9011575
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/data/remote/GithubService.kt
@@ -0,0 +1,12 @@
+package place.pic.android.plus.data.remote
+
+import place.pic.android.plus.data.remote.response.ResponseUserSearch
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface GithubService {
+ @GET("/search/users")
+ suspend fun userList(
+ @Query("q") param: String?
+ ): ResponseUserSearch
+}
diff --git a/app/src/main/java/place/pic/android/plus/data/remote/RetrofitBuilder.kt b/app/src/main/java/place/pic/android/plus/data/remote/RetrofitBuilder.kt
new file mode 100644
index 0000000..12bf031
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/data/remote/RetrofitBuilder.kt
@@ -0,0 +1,14 @@
+package place.pic.android.plus.data.remote
+
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+object RetrofitBuilder {
+ private const val baseUrl = "https://api.github.com"
+
+ private var retrofit = Retrofit.Builder().baseUrl(baseUrl)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+
+ var service: GithubService = retrofit.create(GithubService::class.java)
+}
diff --git a/app/src/main/java/place/pic/android/plus/data/remote/response/ResponseUserSearch.kt b/app/src/main/java/place/pic/android/plus/data/remote/response/ResponseUserSearch.kt
new file mode 100644
index 0000000..a113e5b
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/data/remote/response/ResponseUserSearch.kt
@@ -0,0 +1,15 @@
+package place.pic.android.plus.data.remote.response
+
+import java.io.Serializable
+
+data class ResponseUserSearch(
+ val total_count: Int,
+ val incomplete_results: Boolean,
+ val items: List
+) : Serializable
+
+data class SearchUserData(
+ val login: String,
+ val avatar_url: String,
+ val html_url: String
+) : Serializable
diff --git a/app/src/main/java/place/pic/android/plus/detail/DetailUserActivity.kt b/app/src/main/java/place/pic/android/plus/detail/DetailUserActivity.kt
new file mode 100644
index 0000000..39ebf92
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/detail/DetailUserActivity.kt
@@ -0,0 +1,39 @@
+package place.pic.android.plus.detail
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.bumptech.glide.Glide
+import place.pic.android.plus.data.remote.response.SearchUserData
+import place.pic.android.plus.databinding.ActivityDetailUserBinding
+
+class DetailUserActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityDetailUserBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityDetailUserBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ setBindingUserData()
+ }
+
+ private fun setBindingUserData() {
+ val user = intent.getSerializableExtra("user") as SearchUserData
+
+ with(binding) {
+ tvUser.text = user.login
+
+ Glide.with(this@DetailUserActivity)
+ .load(user.avatar_url)
+ .circleCrop()
+ .into(imgUser)
+
+ btnUser.setOnClickListener {
+ val webIntent =
+ Intent(Intent.ACTION_VIEW, Uri.parse(user.html_url))
+ startActivity(webIntent)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/place/pic/android/plus/search/SearchUserActivity.kt b/app/src/main/java/place/pic/android/plus/search/SearchUserActivity.kt
new file mode 100644
index 0000000..b0aaed5
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/search/SearchUserActivity.kt
@@ -0,0 +1,97 @@
+package place.pic.android.plus.search
+
+import android.content.Intent
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.KeyEvent
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.TextView
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.launch
+import place.pic.android.plus.R
+import place.pic.android.plus.data.remote.response.SearchUserData
+import place.pic.android.plus.databinding.ActivitySearchUserBinding
+import place.pic.android.plus.detail.DetailUserActivity
+import place.pic.android.plus.search.adapter.SearchUserAdapter
+import place.pic.android.plus.search.viewmodel.SearchUserViewModel
+
+class SearchUserActivity : AppCompatActivity() {
+ private lateinit var binding: ActivitySearchUserBinding
+ private val searchUserViewModel: SearchUserViewModel by viewModels()
+ private lateinit var searchUserAdapter: SearchUserAdapter
+ private val userList = mutableListOf()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_search_user)
+ searchUserAdapter = SearchUserAdapter()
+ binding.searchUserActivity = searchUserViewModel
+ binding.lifecycleOwner = this
+ binding.rvUserSearch.adapter = searchUserAdapter
+
+ searchUser()
+ changeButton()
+ deleteText()
+ gotoDetail()
+ }
+
+ private fun searchUser() {
+ searchUserViewModel.recyclerListData.observe(this) {
+ searchUserAdapter.submitList(it)
+ }
+
+ binding.etUserSearch.setOnEditorActionListener(object : TextView.OnEditorActionListener {
+ override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ lifecycleScope.launch {
+ searchUserViewModel.requestUserData(binding.etUserSearch.text.toString())
+ }
+ return true
+ }
+ return false
+ }
+ })
+ }
+
+ // livedata 쓰기
+ private fun changeButton() {
+ binding.etUserSearch.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
+ binding.btnUserSearch.visibility = View.VISIBLE
+ binding.btnUserSearchDelete.visibility = View.INVISIBLE
+ }
+
+ override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
+ }
+
+ override fun afterTextChanged(p0: Editable?) {
+ binding.btnUserSearch.visibility = View.INVISIBLE
+ binding.btnUserSearchDelete.visibility = View.VISIBLE
+ }
+ })
+ }
+
+ // Btn 바인딩으로빼기
+ private fun deleteText() {
+ binding.btnUserSearchDelete.setOnClickListener {
+ binding.etUserSearch.text.clear()
+ }
+ }
+
+ // 여기 바꿔야함요 ㅎㅎ
+ private fun gotoDetail() {
+ searchUserAdapter.itemClick = object : SearchUserAdapter.ItemClick {
+ override fun onClick(view: View, position: Int) {
+ val intent = Intent(this@SearchUserActivity, DetailUserActivity::class.java)
+
+ intent.putExtra("user", userList[position])
+ startActivity(intent)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/place/pic/android/plus/search/adapter/SearchUserAdapter.kt b/app/src/main/java/place/pic/android/plus/search/adapter/SearchUserAdapter.kt
new file mode 100644
index 0000000..e209986
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/search/adapter/SearchUserAdapter.kt
@@ -0,0 +1,51 @@
+package place.pic.android.plus.search.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import place.pic.android.plus.data.remote.response.SearchUserData
+import place.pic.android.plus.databinding.ItemUserSearchBinding
+
+class SearchUserAdapter : ListAdapter(SearchUserDiffUtil()) {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchUserViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val binding = ItemUserSearchBinding.inflate(layoutInflater, parent, false)
+ return SearchUserViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: SearchUserViewHolder, position: Int) {
+ val user = getItem(position)
+ holder.bind(user)
+ if (itemClick != null) {
+ holder.itemView.setOnClickListener { v ->
+ itemClick?.onClick(v, position)
+ }
+ }
+ }
+
+ class SearchUserViewHolder(private val binding: ItemUserSearchBinding) : RecyclerView.ViewHolder(binding.root) {
+ fun bind(searchUserData: SearchUserData) {
+ binding.user = searchUserData
+ binding.executePendingBindings()
+ }
+ }
+
+ private class SearchUserDiffUtil : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: SearchUserData, newItem: SearchUserData): Boolean {
+ return oldItem.login == newItem.login
+ }
+
+ override fun areContentsTheSame(oldItem: SearchUserData, newItem: SearchUserData): Boolean {
+ return oldItem == newItem
+ }
+ }
+
+ interface ItemClick {
+ fun onClick(view: View, position: Int)
+ }
+
+ var itemClick: ItemClick? = null
+}
diff --git a/app/src/main/java/place/pic/android/plus/search/viewmodel/SearchUserViewModel.kt b/app/src/main/java/place/pic/android/plus/search/viewmodel/SearchUserViewModel.kt
new file mode 100644
index 0000000..e698bf9
--- /dev/null
+++ b/app/src/main/java/place/pic/android/plus/search/viewmodel/SearchUserViewModel.kt
@@ -0,0 +1,53 @@
+package place.pic.android.plus.search.viewmodel
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+import place.pic.android.plus.SingleLiveEvent
+import place.pic.android.plus.data.remote.RetrofitBuilder
+import place.pic.android.plus.data.remote.response.SearchUserData
+import retrofit2.HttpException
+import java.io.IOException
+
+class SearchUserViewModel : ViewModel() {
+ private val _recyclerListData = SingleLiveEvent>()
+ val recyclerListData: LiveData>
+ get() = _recyclerListData
+
+ /*
+ private val _compass = MutableLiveData(false)
+ val compass: LiveData = _compass
+
+ private val _searchText = MutableLiveData()
+ val searchText: LiveData = _searchText
+
+ fun compassIcon() {
+ _compass.value = !_compass.value!!
+ }
+
+ fun searchTextWacher(){
+ }
+ */
+
+ fun requestUserData(toString: String) {
+ viewModelScope.launch(Dispatchers.IO) {
+ try {
+ val userData = RetrofitBuilder.service.userList(toString)
+ _recyclerListData.postValue(userData.items)
+ } catch (e: HttpException) {
+ Log.d("request", e.toString())
+ } catch (e: IOException) {
+ this.cancel()
+ }
+ }
+ userClick()
+ }
+
+ private fun userClick() {
+ _recyclerListData.call()
+ }
+}
diff --git a/app/src/main/res/drawable/border_white_fill_round_20.xml b/app/src/main/res/drawable/border_white_fill_round_20.xml
new file mode 100644
index 0000000..c6bfcbb
--- /dev/null
+++ b/app/src/main/res/drawable/border_white_fill_round_20.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
new file mode 100644
index 0000000..bab545a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_clear_24.xml b/app/src/main/res/drawable/ic_baseline_clear_24.xml
new file mode 100644
index 0000000..16d6d37
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_clear_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_not_interested_24.xml b/app/src/main/res/drawable/ic_baseline_not_interested_24.xml
new file mode 100644
index 0000000..22864ca
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_not_interested_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_people_outline_24.xml b/app/src/main/res/drawable/ic_baseline_people_outline_24.xml
new file mode 100644
index 0000000..30d1c94
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_people_outline_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_search_24.xml b/app/src/main/res/drawable/ic_baseline_search_24.xml
new file mode 100644
index 0000000..07b76d6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_search_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_detail_user.xml b/app/src/main/res/layout/activity_detail_user.xml
new file mode 100644
index 0000000..b0454f4
--- /dev/null
+++ b/app/src/main/res/layout/activity_detail_user.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index ca0c0de..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_search_user.xml b/app/src/main/res/layout/activity_search_user.xml
new file mode 100644
index 0000000..bfa38e1
--- /dev/null
+++ b/app/src/main/res/layout/activity_search_user.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_user_search.xml b/app/src/main/res/layout/item_user_search.xml
new file mode 100644
index 0000000..79a4f48
--- /dev/null
+++ b/app/src/main/res/layout/item_user_search.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index dfc5d99..2b1d1cd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
-
- AssignmentMVVM
+
+ AssignmentMVVM
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 480eeb8..64fb069 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,13 +1,13 @@
-