From ab6cd0090f8a16152deb129f3aed8120c491608e Mon Sep 17 00:00:00 2001 From: kimdahyee Date: Wed, 3 Nov 2021 02:53:24 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=EA=B8=B0=EB=B3=B8=ED=8B=80=20=EC=99=84?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/codeStyles/Project.xml | 7 ++- .idea/codeStyles/codeStyleConfig.xml | 1 + app/.idea/.gitignore | 3 + app/.idea/gradle.xml | 17 +++++ app/.idea/misc.xml | 9 +++ app/.idea/modules.xml | 8 +++ app/build.gradle | 27 +++++++- app/src/main/AndroidManifest.xml | 8 ++- .../plus/adapter/SearchBindingAdapter.kt | 20 ++++++ .../pic/android/plus/adapter/UserAdapter.kt | 45 +++++++++++++ .../java/place/pic/android/plus/model/User.kt | 12 ++++ .../android/plus/remote/GithubApiService.kt | 19 ++++++ .../plus/remote/GithubApiServiceImpl.kt | 19 ++++++ .../remote/response/UserSearchResponse.kt | 33 ++++++++++ .../android/plus/{ => view}/MainActivity.kt | 23 +++---- .../pic/android/plus/view/SearchActivity.kt | 63 +++++++++++++++++++ .../android/plus/viewmodel/SearchViewModel.kt | 63 +++++++++++++++++++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/activity_search.xml | 57 +++++++++++++++++ app/src/main/res/layout/item_user_search.xml | 55 ++++++++++++++++ app/src/main/res/values-night/themes.xml | 2 +- app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/themes.xml | 2 +- build.gradle | 2 +- 24 files changed, 481 insertions(+), 20 deletions(-) create mode 100644 app/.idea/.gitignore create mode 100644 app/.idea/gradle.xml create mode 100644 app/.idea/misc.xml create mode 100644 app/.idea/modules.xml create mode 100644 app/src/main/java/place/pic/android/plus/adapter/SearchBindingAdapter.kt create mode 100644 app/src/main/java/place/pic/android/plus/adapter/UserAdapter.kt create mode 100644 app/src/main/java/place/pic/android/plus/model/User.kt create mode 100644 app/src/main/java/place/pic/android/plus/remote/GithubApiService.kt create mode 100644 app/src/main/java/place/pic/android/plus/remote/GithubApiServiceImpl.kt create mode 100644 app/src/main/java/place/pic/android/plus/remote/response/UserSearchResponse.kt rename app/src/main/java/place/pic/android/plus/{ => view}/MainActivity.kt (80%) create mode 100644 app/src/main/java/place/pic/android/plus/view/SearchActivity.kt create mode 100644 app/src/main/java/place/pic/android/plus/viewmodel/SearchViewModel.kt create mode 100644 app/src/main/res/layout/activity_search.xml create mode 100644 app/src/main/res/layout/item_user_search.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ea4e5d0..a8fce2b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,7 +1,9 @@ + + - + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index df5f35d..a01a3e5 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/app/.idea/.gitignore b/app/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/app/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/app/.idea/gradle.xml b/app/.idea/gradle.xml new file mode 100644 index 0000000..91f278a --- /dev/null +++ b/app/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/.idea/misc.xml b/app/.idea/misc.xml new file mode 100644 index 0000000..f845854 --- /dev/null +++ b/app/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/.idea/modules.xml b/app/.idea/modules.xml new file mode 100644 index 0000000..e1f9e1c --- /dev/null +++ b/app/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 71490b5..f4fbb5a 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 { @@ -29,10 +30,13 @@ android { kotlinOptions { jvmTarget = '1.8' } + + dataBinding { + enabled = true + } } dependencies { - implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' @@ -40,4 +44,25 @@ dependencies { testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + + //view + implementation 'androidx.recyclerview:recyclerview:1.1.0' + + //livedata + def lifecycle_version = "2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + + //glide + implementation "com.github.bumptech.glide:glide:4.11.0" + compileOnly 'com.google.android.wearable:wearable:2.8.1' + kapt "com.github.bumptech.glide:compiler:4.11.0" + + //retrofit + implementation 'com.squareup.retrofit2:retrofit:2.7.2' + implementation 'com.squareup.retrofit2:converter-gson:2.7.1' + implementation 'com.squareup.okhttp3:logging-interceptor:4.2.1' + + // Gson + implementation 'com.google.code.gson:gson:2.8.6' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8508fca..5adcf55 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ + + + + diff --git a/app/src/main/java/place/pic/android/plus/adapter/SearchBindingAdapter.kt b/app/src/main/java/place/pic/android/plus/adapter/SearchBindingAdapter.kt new file mode 100644 index 0000000..e30eba7 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/adapter/SearchBindingAdapter.kt @@ -0,0 +1,20 @@ +package place.pic.android.plus.adapter + +import android.widget.ImageView +import androidx.databinding.BindingAdapter +import com.bumptech.glide.Glide + +/** + * Created By kimdahyee + * on 11월 03일, 2020 + */ + +class SearchBindingAdapter { + companion object { + @JvmStatic + @BindingAdapter("imageUrl") + fun loadImage(imageView: ImageView, url: String) { + Glide.with(imageView.context).load(url).into(imageView) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/adapter/UserAdapter.kt b/app/src/main/java/place/pic/android/plus/adapter/UserAdapter.kt new file mode 100644 index 0000000..9cfc170 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/adapter/UserAdapter.kt @@ -0,0 +1,45 @@ +package place.pic.android.plus.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import place.pic.android.plus.databinding.ItemUserSearchBinding +import place.pic.android.plus.model.User + +/** + * Created By kimdahyee + * on 11월 02일, 2020 + */ + +class UserAdapter : RecyclerView.Adapter() { + + var data : MutableList = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val binding = ItemUserSearchBinding.inflate(layoutInflater, parent, false) + return UserViewHolder(binding) + } + + override fun onBindViewHolder(holder: UserViewHolder, position: Int) { + holder.bind(data[position]) + } + + override fun getItemCount(): Int { + return data.size + } + + inner class UserViewHolder( + private val binding: ItemUserSearchBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(user: User) { + binding.user = user + } + } + + fun setItem(users: List) { + data = users as MutableList + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/model/User.kt b/app/src/main/java/place/pic/android/plus/model/User.kt new file mode 100644 index 0000000..e37bf31 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/model/User.kt @@ -0,0 +1,12 @@ +package place.pic.android.plus.model + +/** + * Created By kimdahyee + * on 11월 02일, 2020 + */ + +data class User( + val imageUrl: String, + val name: String, + val id: Long +) \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/remote/GithubApiService.kt b/app/src/main/java/place/pic/android/plus/remote/GithubApiService.kt new file mode 100644 index 0000000..1151294 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/remote/GithubApiService.kt @@ -0,0 +1,19 @@ +package place.pic.android.plus.remote + +import place.pic.android.plus.remote.response.UserSearchResponse +import retrofit2.Call +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.Retrofit +import retrofit2.http.* + +/** + * Created By kimdahyee + * on 11월 02일, 2020 + */ + +interface GithubApiService { + @GET("users") + fun requestUserSearch( + @Query("q") param: String? + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/remote/GithubApiServiceImpl.kt b/app/src/main/java/place/pic/android/plus/remote/GithubApiServiceImpl.kt new file mode 100644 index 0000000..e236c71 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/remote/GithubApiServiceImpl.kt @@ -0,0 +1,19 @@ +package place.pic.android.plus.remote + +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +/** + * Created By kimdahyee + * on 11월 02일, 2020 + */ + +object GithubApiServiceImpl { + private val retrofit: Retrofit + get() = Retrofit.Builder() + .baseUrl("https://api.github.com/search/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + + val service : GithubApiService = retrofit.create(GithubApiService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/remote/response/UserSearchResponse.kt b/app/src/main/java/place/pic/android/plus/remote/response/UserSearchResponse.kt new file mode 100644 index 0000000..dbb11db --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/remote/response/UserSearchResponse.kt @@ -0,0 +1,33 @@ +package place.pic.android.plus.remote.response + +/** + * Created By kimdahyee + * on 11월 02일, 2020 + */ + +data class UserSearchResponse ( + val total_count: Int, + val incomplete_results: Boolean, + val items: List +) { + data class Data ( + val login: String, + val id: Long, + val node_id: String, + val avatar_url: String, + val url: String, + val html_url: String, + val followers_url: String, + val following_url: String, + val gists_url: String, + val starred_url: String, + val subscriptions_url: String, + val organizations_url: String, + val repos_url: String, + val events_url: String, + val received_events_url: String, + val type: String, + val site_admin: Boolean, + val score: Double + ) +} \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/MainActivity.kt b/app/src/main/java/place/pic/android/plus/view/MainActivity.kt similarity index 80% rename from app/src/main/java/place/pic/android/plus/MainActivity.kt rename to app/src/main/java/place/pic/android/plus/view/MainActivity.kt index b8670ea..79787da 100644 --- a/app/src/main/java/place/pic/android/plus/MainActivity.kt +++ b/app/src/main/java/place/pic/android/plus/view/MainActivity.kt @@ -1,11 +1,12 @@ -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) - } -} +package place.pic.android.plus.view + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import place.pic.android.plus.R + +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/view/SearchActivity.kt b/app/src/main/java/place/pic/android/plus/view/SearchActivity.kt new file mode 100644 index 0000000..517c0d8 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/view/SearchActivity.kt @@ -0,0 +1,63 @@ +package place.pic.android.plus.view + +import android.content.Context +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.util.Log +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import android.widget.Toast +import androidx.lifecycle.ViewModelProvider +import place.pic.android.plus.R +import place.pic.android.plus.adapter.UserAdapter +import place.pic.android.plus.databinding.ActivitySearchBinding +import place.pic.android.plus.viewmodel.SearchViewModel + +class SearchActivity : AppCompatActivity() { + + private lateinit var searchViewModel: SearchViewModel + private val userAdapter by lazy { UserAdapter() } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_search) + init() + } + + private fun init() { + val binding = ActivitySearchBinding.inflate(layoutInflater) + binding.lifecycleOwner = this + searchViewModel = ViewModelProvider( + this, + ViewModelProvider.NewInstanceFactory() + ).get(SearchViewModel::class.java) + searchViewModel.users.observe(this) { + userAdapter.setItem(it) + } + binding.viewModel = searchViewModel + binding.rcvUserSearch.adapter = userAdapter + binding.btUserSearch.setOnClickListener { onSearchClick() } + setContentView(binding.root) + } + + private fun onSearchClick() { + var input = findViewById(R.id.et_user_search) + Log.d("dahye input test", input.text.toString()) + + if (input == null) { + Toast.makeText(this, "검색어를 입력하세요.", Toast.LENGTH_SHORT).show() + } else { + input.run { + searchViewModel.requestUserSearch(input.text.toString()) + hideKeyboard() + } + } + } + + private fun hideKeyboard() { + currentFocus?.run { + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager + imm?.hideSoftInputFromWindow(windowToken, 0) } + } + +} \ No newline at end of file diff --git a/app/src/main/java/place/pic/android/plus/viewmodel/SearchViewModel.kt b/app/src/main/java/place/pic/android/plus/viewmodel/SearchViewModel.kt new file mode 100644 index 0000000..bacc597 --- /dev/null +++ b/app/src/main/java/place/pic/android/plus/viewmodel/SearchViewModel.kt @@ -0,0 +1,63 @@ +package place.pic.android.plus.viewmodel + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import place.pic.android.plus.model.User +import place.pic.android.plus.remote.GithubApiServiceImpl +import place.pic.android.plus.remote.response.UserSearchResponse +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +/** + * Created By kimdahyee + * on 11월 02일, 2020 + */ + +class SearchViewModel : ViewModel() { + + private val list = mutableListOf() + + private val _users = MutableLiveData>() + val users : LiveData> + get() = _users + + fun addUser(user: User) { + list.add(user) + _users.value = list + } + + fun requestUserSearch(query: String) { + GithubApiServiceImpl.service.requestUserSearch( + param = query + ).enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + if (response.isSuccessful) { + Log.d("server check", "통신 성공") + list.clear() + for (i in response.body()!!.items.indices) { + addUser( + User( + imageUrl = response.body()!!.items[i].avatar_url, + name = response.body()!!.items[i].login, + id = response.body()!!.items[i].id + ) + ) + Log.d("dahye server response check", response.body()!!.items[i].login) + } + } + Log.d("dahye final data check", _users.value.toString()) + } + + + override fun onFailure(call: Call, t: Throwable) { + t.message?.let { Log.d("fail", it) } + } + }) + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ca0c0de..6d57aac 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".view.MainActivity"> + + + + + + + + + + + + +