From 271d471f7fb7e78170ce81bef1d8871e7c62c963 Mon Sep 17 00:00:00 2001 From: aurelioklv Date: Tue, 12 Mar 2024 17:36:55 +0700 Subject: [PATCH] feat: add followers and following list --- README.md | 11 +++ app/build.gradle.kts | 2 + .../githubuser/data/api/ApiService.kt | 11 ++- .../response/FollowersFollowingResponse.kt | 12 --- .../data/response/UserSearchResponse.kt | 88 +++++++++---------- .../githubuser/ui/details/DetailsActivity.kt | 32 +++++-- .../githubuser/ui/details/DetailsViewModel.kt | 20 ++++- .../ui/details/SectionsPagerAdapter.kt | 23 +++++ .../ui/details/follow/FollowAdapter.kt | 60 +++++++++++++ .../ui/details/follow/FollowFragment.kt | 68 ++++++++++++++ .../ui/details/follow/FollowViewModel.kt | 65 ++++++++++++++ .../githubuser/ui/main/MainActivity.kt | 30 ++++--- .../githubuser/ui/main/MainViewModel.kt | 4 +- app/src/main/res/layout/activity_details.xml | 27 +++++- app/src/main/res/layout/fragment_follow.xml | 21 +++++ app/src/main/res/values/strings.xml | 3 + gradle/libs.versions.toml | 2 + 17 files changed, 389 insertions(+), 90 deletions(-) create mode 100644 README.md create mode 100644 app/src/main/java/com/aurelioklv/githubuser/ui/details/SectionsPagerAdapter.kt create mode 100644 app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowAdapter.kt create mode 100644 app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowFragment.kt create mode 100644 app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowViewModel.kt create mode 100644 app/src/main/res/layout/fragment_follow.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..55a3c85 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +## Setting Up API Key and Base URL +To ensure seamless integration with the APIs, please follow these steps: + +1. Open the project in Android Studio. +2. Locate the `local.properties` file in the root directory of the project. +3. Add the following lines to the `local.properties` file: + +```properties +API_KEY=your_api_key_here +BASE_URL=your_base_url_here +``` \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 24155fc..0dbdc88 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -63,6 +63,8 @@ dependencies { implementation(libs.androidx.lifecycle.viewmodel.ktx) implementation(libs.activity.ktx) + implementation(libs.androidx.fragment.ktx) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/app/src/main/java/com/aurelioklv/githubuser/data/api/ApiService.kt b/app/src/main/java/com/aurelioklv/githubuser/data/api/ApiService.kt index b4ddc17..d66b093 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/data/api/ApiService.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/data/api/ApiService.kt @@ -1,7 +1,6 @@ package com.aurelioklv.githubuser.data.api -import com.aurelioklv.githubuser.data.response.FollowersResponse -import com.aurelioklv.githubuser.data.response.FollowingResponse +import com.aurelioklv.githubuser.data.response.FollowerFollowingItem import com.aurelioklv.githubuser.data.response.UserResponse import com.aurelioklv.githubuser.data.response.UserSearchResponse import retrofit2.Call @@ -16,9 +15,9 @@ interface ApiService { @GET("users/{username}") fun getUserDetails(@Path("username") username: String): Call - @GET("users/{username}/followers") - fun getFollowers(@Path("username") username: String): Call + @GET("users/{username}/followers?per_page=100") + fun getFollowers(@Path("username") username: String): Call> - @GET("users/{username}/following") - fun getFollowing(@Path("username") username: String): Call + @GET("users/{username}/following?per_page=100") + fun getFollowing(@Path("username") username: String): Call> } \ No newline at end of file diff --git a/app/src/main/java/com/aurelioklv/githubuser/data/response/FollowersFollowingResponse.kt b/app/src/main/java/com/aurelioklv/githubuser/data/response/FollowersFollowingResponse.kt index 69e4005..1af6f46 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/data/response/FollowersFollowingResponse.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/data/response/FollowersFollowingResponse.kt @@ -2,18 +2,6 @@ package com.aurelioklv.githubuser.data.response import com.google.gson.annotations.SerializedName -data class FollowersResponse( - - @field:SerializedName("FollowersResponse") - val followers: List, -) - -data class FollowingResponse( - - @field:SerializedName("FollowingResponse") - val following: List, -) - data class FollowerFollowingItem( @field:SerializedName("gists_url") diff --git a/app/src/main/java/com/aurelioklv/githubuser/data/response/UserSearchResponse.kt b/app/src/main/java/com/aurelioklv/githubuser/data/response/UserSearchResponse.kt index c9b4133..960fdce 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/data/response/UserSearchResponse.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/data/response/UserSearchResponse.kt @@ -4,72 +4,72 @@ import com.google.gson.annotations.SerializedName data class UserSearchResponse( - @field:SerializedName("total_count") - val totalCount: Int, + @field:SerializedName("total_count") + val totalCount: Int, - @field:SerializedName("incomplete_results") - val incompleteResults: Boolean, + @field:SerializedName("incomplete_results") + val incompleteResults: Boolean, - @field:SerializedName("items") - val items: List + @field:SerializedName("items") + val items: List, ) data class UserSearchItem( - @field:SerializedName("gists_url") - val gistsUrl: String, + @field:SerializedName("gists_url") + val gistsUrl: String, - @field:SerializedName("repos_url") - val reposUrl: String, + @field:SerializedName("repos_url") + val reposUrl: String, - @field:SerializedName("following_url") - val followingUrl: String, + @field:SerializedName("following_url") + val followingUrl: String, - @field:SerializedName("starred_url") - val starredUrl: String, + @field:SerializedName("starred_url") + val starredUrl: String, - @field:SerializedName("login") - val login: String, + @field:SerializedName("login") + val login: String, - @field:SerializedName("followers_url") - val followersUrl: String, + @field:SerializedName("followers_url") + val followersUrl: String, - @field:SerializedName("type") - val type: String, + @field:SerializedName("type") + val type: String, - @field:SerializedName("url") - val url: String, + @field:SerializedName("url") + val url: String, - @field:SerializedName("subscriptions_url") - val subscriptionsUrl: String, + @field:SerializedName("subscriptions_url") + val subscriptionsUrl: String, - @field:SerializedName("score") - val score: Any, + @field:SerializedName("score") + val score: Any, - @field:SerializedName("received_events_url") - val receivedEventsUrl: String, + @field:SerializedName("received_events_url") + val receivedEventsUrl: String, - @field:SerializedName("avatar_url") - val avatarUrl: String, + @field:SerializedName("avatar_url") + val avatarUrl: String, - @field:SerializedName("events_url") - val eventsUrl: String, + @field:SerializedName("events_url") + val eventsUrl: String, - @field:SerializedName("html_url") - val htmlUrl: String, + @field:SerializedName("html_url") + val htmlUrl: String, - @field:SerializedName("site_admin") - val siteAdmin: Boolean, + @field:SerializedName("site_admin") + val siteAdmin: Boolean, - @field:SerializedName("id") - val id: Int, + @field:SerializedName("id") + val id: Int, - @field:SerializedName("gravatar_id") - val gravatarId: String, + @field:SerializedName("gravatar_id") + val gravatarId: String, - @field:SerializedName("node_id") - val nodeId: String, + @field:SerializedName("node_id") + val nodeId: String, - @field:SerializedName("organizations_url") - val organizationsUrl: String + @field:SerializedName("organizations_url") + val organizationsUrl: String, ) diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsActivity.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsActivity.kt index 2777970..d594e1a 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsActivity.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsActivity.kt @@ -5,15 +5,19 @@ import android.util.Log import android.view.View import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels +import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.viewpager2.widget.ViewPager2 import com.aurelioklv.githubuser.R import com.aurelioklv.githubuser.data.response.UserResponse import com.aurelioklv.githubuser.databinding.ActivityDetailsBinding import com.aurelioklv.githubuser.ui.formatCount import com.bumptech.glide.Glide +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator class DetailsActivity : AppCompatActivity() { private lateinit var binding: ActivityDetailsBinding @@ -35,16 +39,27 @@ class DetailsActivity : AppCompatActivity() { if (intent != null && intent.hasExtra(EXTRA_USERNAME)) { username = intent.getStringExtra(EXTRA_USERNAME) - viewModel.getUserDetails(username!!) + viewModel.getUserDetails(username!!, this) } - viewModel.user.observe(this) { - setUserDetails(it) - } + setupViewPager() + observeLiveData() + } - viewModel.isLoading.observe(this) { - showLoading(it) - } + private fun setupViewPager() { + val sectionsPagerAdapter = SectionsPagerAdapter(this, username!!) + val viewPager: ViewPager2 = findViewById(R.id.view_pager) + viewPager.adapter = sectionsPagerAdapter + + val tabs: TabLayout = findViewById(R.id.tabs) + TabLayoutMediator(tabs, viewPager) { tab, position -> + tab.text = resources.getString(TAB_TITLES[position]) + }.attach() + } + + private fun observeLiveData() { + viewModel.user.observe(this) { setUserDetails(it) } + viewModel.isLoading.observe(this) { showLoading(it) } } private fun setUserDetails(userResponse: UserResponse) { @@ -93,5 +108,8 @@ class DetailsActivity : AppCompatActivity() { companion object { const val TAG = "DetailsActivity" const val EXTRA_USERNAME = "username" + + @StringRes + private val TAB_TITLES = intArrayOf(R.string.tab_text_1, R.string.tab_text_2) } } \ No newline at end of file diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsViewModel.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsViewModel.kt index 0c0035d..8c155a4 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsViewModel.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/details/DetailsViewModel.kt @@ -1,9 +1,12 @@ package com.aurelioklv.githubuser.ui.details +import android.content.Context import android.util.Log +import android.widget.Toast import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.aurelioklv.githubuser.R import com.aurelioklv.githubuser.data.api.ApiConfig import com.aurelioklv.githubuser.data.response.UserResponse import retrofit2.Call @@ -17,7 +20,7 @@ class DetailsViewModel : ViewModel() { private val _isLoading = MutableLiveData() val isLoading: LiveData = _isLoading - fun getUserDetails(username: String) { + fun getUserDetails(username: String, context: Context) { _isLoading.value = true val client = ApiConfig.getApiService().getUserDetails(username) client.enqueue(object : Callback { @@ -25,16 +28,27 @@ class DetailsViewModel : ViewModel() { _isLoading.value = false if (response.isSuccessful) { val responseBody = response.body() - if (responseBody != null) { - _user.value = responseBody + responseBody?.let { + _user.value = it } } else { Log.e(TAG, "onResponse !isSuccessFul: $response") + Toast.makeText( + context, + context.getString(R.string.error_message), + Toast.LENGTH_LONG + ).show() } } override fun onFailure(call: Call, t: Throwable) { + _isLoading.value = false Log.e(TAG, "onFailure: ${t.message}") + Toast.makeText( + context, + context.getString(R.string.error_message), + Toast.LENGTH_LONG + ).show() } }) } diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/details/SectionsPagerAdapter.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/details/SectionsPagerAdapter.kt new file mode 100644 index 0000000..4897550 --- /dev/null +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/details/SectionsPagerAdapter.kt @@ -0,0 +1,23 @@ +package com.aurelioklv.githubuser.ui.details + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.aurelioklv.githubuser.ui.details.follow.FollowFragment + +class SectionsPagerAdapter(activity: AppCompatActivity, private val username: String) : + FragmentStateAdapter(activity) { + override fun getItemCount(): Int { + return 2 + } + + override fun createFragment(position: Int): Fragment { + val fragment = FollowFragment() + fragment.arguments = Bundle().apply { + putInt(FollowFragment.ARG_SECTION_NUMBER, position + 1) + putString(FollowFragment.ARG_USERNAME, username) + } + return fragment + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowAdapter.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowAdapter.kt new file mode 100644 index 0000000..f85a7fa --- /dev/null +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowAdapter.kt @@ -0,0 +1,60 @@ +package com.aurelioklv.githubuser.ui.details.follow + +import android.content.Intent +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.aurelioklv.githubuser.data.response.FollowerFollowingItem +import com.aurelioklv.githubuser.databinding.UserSearchItemBinding +import com.aurelioklv.githubuser.ui.details.DetailsActivity +import com.bumptech.glide.Glide + +class FollowAdapter : + ListAdapter(DIFF_CALLBACK) { + class MyViewHolder(private val binding: UserSearchItemBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(item: FollowerFollowingItem) { + Glide.with(itemView.context) + .load(item.avatarUrl) + .into(binding.ivUserAvatar) + binding.tvUsername.text = item.login + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { + val binding = + UserSearchItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return MyViewHolder(binding) + } + + override fun onBindViewHolder(holder: MyViewHolder, position: Int) { + val userSearchItem = getItem(position) + holder.bind(userSearchItem) + + holder.itemView.setOnClickListener { + val intent = Intent(holder.itemView.context, DetailsActivity::class.java) + intent.putExtra(DetailsActivity.EXTRA_USERNAME, userSearchItem.login) + holder.itemView.context.startActivity(intent) + } + } + + companion object { + val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: FollowerFollowingItem, + newItem: FollowerFollowingItem, + ): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame( + oldItem: FollowerFollowingItem, + newItem: FollowerFollowingItem, + ): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowFragment.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowFragment.kt new file mode 100644 index 0000000..49459ea --- /dev/null +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowFragment.kt @@ -0,0 +1,68 @@ +package com.aurelioklv.githubuser.ui.details.follow + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import com.aurelioklv.githubuser.data.response.FollowerFollowingItem +import com.aurelioklv.githubuser.databinding.FragmentFollowBinding + +class FollowFragment : Fragment() { + private var _binding: FragmentFollowBinding? = null + private val binding get() = _binding!! + + private val viewModel: FollowViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentFollowBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val sectionNumber = arguments?.getInt(ARG_SECTION_NUMBER, 0) + val username = arguments?.getString(ARG_USERNAME) + + viewModel.setData(username!!, sectionNumber!!, requireContext()) + + setupRecyclerView() + observeLiveData() + } + + private fun setupRecyclerView() { + val layoutManager = LinearLayoutManager(requireActivity()) + binding.rvFollow.layoutManager = layoutManager + val itemDecoration = DividerItemDecoration(requireActivity(), layoutManager.orientation) + binding.rvFollow.addItemDecoration(itemDecoration) + } + + private fun observeLiveData() { + viewModel.followData.observe(viewLifecycleOwner) { setData(it) } + viewModel.isLoading.observe(viewLifecycleOwner) { showLoading(it) } + } + + private fun setData(data: List) { + val adapter = FollowAdapter() + adapter.submitList(data) + binding.rvFollow.adapter = adapter + } + + private fun showLoading(isLoading: Boolean) { + binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE + } + + + companion object { + const val TAG = "FollowFragment" + const val ARG_SECTION_NUMBER = "section_number" + const val ARG_USERNAME = "username" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowViewModel.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowViewModel.kt new file mode 100644 index 0000000..2952670 --- /dev/null +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/details/follow/FollowViewModel.kt @@ -0,0 +1,65 @@ +package com.aurelioklv.githubuser.ui.details.follow + +import android.content.Context +import android.util.Log +import android.widget.Toast +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.aurelioklv.githubuser.R +import com.aurelioklv.githubuser.data.api.ApiConfig +import com.aurelioklv.githubuser.data.response.FollowerFollowingItem +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class FollowViewModel : ViewModel() { + private val _followData = MutableLiveData>() + val followData: LiveData> = _followData + + private val _isLoading = MutableLiveData() + val isLoading: LiveData = _isLoading + + fun setData(username: String, type: Int, context: Context) { + _isLoading.value = true + val client = when (type) { + 1 -> ApiConfig.getApiService().getFollowers(username) + else -> ApiConfig.getApiService().getFollowing(username) + } + client.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response>, + ) { + _isLoading.value = false + if (response.isSuccessful) { + val responseBody = response.body() + responseBody?.let { + _followData.value = it + } + } else { + Log.e(TAG, "onResponse !isSuccessful: $response") + Toast.makeText( + context, + context.getString(R.string.error_message), + Toast.LENGTH_LONG + ).show() + } + } + + override fun onFailure(call: Call>, t: Throwable) { + _isLoading.value = false + Log.e(TAG, "onFailure: ${t.message}") + Toast.makeText( + context, + context.getString(R.string.error_message), + Toast.LENGTH_LONG + ).show() + } + }) + } + + companion object { + const val TAG = "FollowViewModel" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainActivity.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainActivity.kt index ff046d9..a4b7c84 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainActivity.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainActivity.kt @@ -29,19 +29,12 @@ class MainActivity : AppCompatActivity() { insets } - val layoutManager = LinearLayoutManager(this) - binding.rvUsers.layoutManager = layoutManager - - val itemDecoration = DividerItemDecoration(this, layoutManager.orientation) - binding.rvUsers.addItemDecoration(itemDecoration) - - viewModel.users.observe(this) { - setUserSearchResult(it) - } - viewModel.isLoading.observe(this) { - showLoading(it) - } + setupSearchBar() + setupRecyclerView() + observeLiveData() + } + private fun setupSearchBar() { with(binding) { searchView.setupWithSearchBar(searchBar) searchView.editText.setOnEditorActionListener { v, actionId, event -> @@ -53,6 +46,19 @@ class MainActivity : AppCompatActivity() { } } + private fun setupRecyclerView() { + val layoutManager = LinearLayoutManager(this) + binding.rvUsers.layoutManager = layoutManager + + val itemDecoration = DividerItemDecoration(this, layoutManager.orientation) + binding.rvUsers.addItemDecoration(itemDecoration) + } + + private fun observeLiveData() { + viewModel.users.observe(this) { setUserSearchResult(it) } + viewModel.isLoading.observe(this) { showLoading(it) } + } + private fun showLoading(isLoading: Boolean) { binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE } diff --git a/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainViewModel.kt b/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainViewModel.kt index e74b099..af6a496 100644 --- a/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/aurelioklv/githubuser/ui/main/MainViewModel.kt @@ -18,7 +18,7 @@ class MainViewModel : ViewModel() { val isLoading: LiveData = _isLoading init { - searchUser(initialQuery) + searchUser(INITIAL_QUERY) } fun searchUser(query: String) { @@ -48,7 +48,7 @@ class MainViewModel : ViewModel() { } companion object { - private val initialQuery = "android" + const val INITIAL_QUERY = "android" const val TAG = "MainViewModel" } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml index b17c8b9..ba9d365 100644 --- a/app/src/main/res/layout/activity_details.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -9,10 +9,10 @@ @@ -86,4 +86,23 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_follow.xml b/app/src/main/res/layout/fragment_follow.xml new file mode 100644 index 0000000..911d286 --- /dev/null +++ b/app/src/main/res/layout/fragment_follow.xml @@ -0,0 +1,21 @@ + + + + + + + \ 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 05898e3..8906f20 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,4 +13,7 @@ %d following follower bio + + Hello blank fragment + Something went wrong. Try again later \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2cfe3b8..a4816ac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ activityKtx = "1.8.0" agp = "8.3.0" circleimageview = "3.1.0" +fragmentKtx = "1.6.2" glide = "4.16.0" kotlin = "1.9.0" coreKtx = "1.12.0" @@ -18,6 +19,7 @@ retrofit = "2.9.0" [libraries] activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } circleimageview = { module = "de.hdodenhof:circleimageview", version.ref = "circleimageview" } converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }