-
Notifications
You must be signed in to change notification settings - Fork 1
TE7-T893: Add Web service without library #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/TE7-T1049_AppNavigation
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import androidx.fragment.app.viewModels | |
| import androidx.lifecycle.Lifecycle | ||
| import androidx.lifecycle.lifecycleScope | ||
| import androidx.lifecycle.repeatOnLifecycle | ||
| import com.krunal.demo.appcomponents.utils.IntentData | ||
| import com.krunal.demo.databinding.FragmentWebViewBinding | ||
| import com.krunal.demo.searchwebview.helpers.WebClient | ||
| import com.krunal.demo.searchwebview.ui.viewmodels.WebViewViewModel | ||
|
|
@@ -41,6 +42,8 @@ class WebViewFragment : Fragment() { | |
|
|
||
| @SuppressLint("SetJavaScriptEnabled") | ||
| private fun setupWebView() { | ||
| arguments?.getString(IntentData.WEB_URL)?.let(viewModel::loadUrl) | ||
|
|
||
| binding.webView.apply { | ||
| webViewClient = WebClient() | ||
| settings.javaScriptEnabled = true | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 AIReviewRadar The code in The addition of Overall, the code looks good. Please let me know if you need further assistance. |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.krunal.demo.utils | ||
|
|
||
| object AppConstants { | ||
|
|
||
| const val NEWS_API_KEY = "fa3d5d7db3f8448dac9f98e82f1108ce" | ||
| const val NEST_BASE_URL = "https://newsapi.org/v2" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.data.apis | ||
|
|
||
| import com.krunal.demo.utils.AppConstants | ||
| import com.krunal.demo.webservices.withoutlibrary.data.models.api.ArticleResponse | ||
| import com.krunal.demo.webservices.withoutlibrary.data.models.api.NewsResponse | ||
| import com.krunal.demo.webservices.withoutlibrary.data.models.api.SourceResponse | ||
| import org.json.JSONObject | ||
| import java.io.BufferedReader | ||
| import java.io.InputStreamReader | ||
| import java.net.HttpURLConnection | ||
| import java.net.URL | ||
|
|
||
| object NewsApi { | ||
|
|
||
| private const val HEADLINE_URL = | ||
| "${AppConstants.NEST_BASE_URL}/top-headlines?country=in&apiKey=${AppConstants.NEWS_API_KEY}" | ||
|
|
||
| suspend fun getNews(): NewsResponse? { | ||
| var newsResponse: NewsResponse? = null | ||
|
|
||
| runCatching { | ||
| val url = URL(HEADLINE_URL) | ||
| val urlConnection = url.openConnection() as HttpURLConnection | ||
| urlConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:221.0) Gecko/20100101 Firefox/31.0") | ||
| val br = BufferedReader(InputStreamReader(urlConnection.inputStream)) | ||
| val response = br.readText() | ||
|
|
||
| val newsJson = JSONObject(response) | ||
| val status = newsJson.getString("status") | ||
| val totalResults = newsJson.getInt("totalResults") | ||
|
|
||
| val articles = mutableListOf<ArticleResponse>() | ||
|
|
||
| val articlesJson = newsJson.getJSONArray("articles") | ||
|
|
||
| for (i in 0 until articlesJson.length()) { | ||
| if (i == 19) { | ||
| print("") | ||
| } | ||
| val articleJson = articlesJson.getJSONObject(i) | ||
| val sourceJson = articleJson.getJSONObject("source") | ||
| val source = | ||
| SourceResponse(sourceJson.optString("id"), sourceJson.optString("name")) | ||
| val article = ArticleResponse( | ||
| source = source, | ||
| author = articleJson.optString("author"), | ||
| title = articleJson.getString("title"), | ||
| description = articleJson.getString("description"), | ||
| url = articleJson.getString("url"), | ||
| urlToImage = articleJson.getString("urlToImage"), | ||
| publishedAt = articleJson.getString("publishedAt"), | ||
| content = articleJson.getString("content"), | ||
| ) | ||
| articles.add(article) | ||
| } | ||
|
|
||
| newsResponse = NewsResponse( | ||
| status, totalResults, articles | ||
| ) | ||
| } | ||
| return newsResponse | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 AIReviewRadar The code changes in the
Overall, the code follows the guidelines and appears to be correct. I would recommend adding a newline at the end of the file for consistency. Please let me know if you have any specific concerns or if there's anything else I can assist you with. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.data.models.api | ||
|
|
||
| data class ArticleResponse( | ||
| val source: SourceResponse, | ||
| val author: String? = null, | ||
| val title: String, | ||
| val description: String, | ||
| val url: String, | ||
| val urlToImage: String, | ||
| val publishedAt: String, | ||
| val content: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.data.models.api | ||
|
|
||
| data class NewsResponse( | ||
| val status: String, | ||
| val totalResult: Int, | ||
| val articles: List<ArticleResponse> | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.data.models.api | ||
|
|
||
| data class SourceResponse( | ||
| val id: String? = null, | ||
| val name: String? = null | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.data.models.local | ||
|
|
||
| data class NewsItem( | ||
| val title: String, | ||
| val description: String, | ||
| val url: String, | ||
| val imageUrl: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.repositories | ||
|
|
||
| import com.krunal.demo.webservices.withoutlibrary.data.apis.NewsApi | ||
| import com.krunal.demo.webservices.withoutlibrary.data.models.api.ArticleResponse | ||
|
|
||
| object NewsRepository { | ||
|
|
||
| suspend fun getNewsArticles(): List<ArticleResponse> = | ||
| NewsApi.getNews()?.articles ?: emptyList() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.ui.adapters | ||
|
|
||
| import android.annotation.SuppressLint | ||
| import android.view.LayoutInflater | ||
| import android.view.ViewGroup | ||
| import androidx.recyclerview.widget.RecyclerView | ||
| import com.krunal.demo.databinding.ItemNewsBinding | ||
| import com.krunal.demo.webservices.withoutlibrary.data.models.local.NewsItem | ||
|
|
||
| class NewsAdapter(val onClick: (newsItem: NewsItem) -> Unit) : RecyclerView.Adapter<NewsAdapter.NewsViewHolder>() { | ||
|
|
||
| private val newsList: MutableList<NewsItem> = mutableListOf() | ||
|
|
||
| class NewsViewHolder( | ||
| private val binding: ItemNewsBinding, | ||
| val onClick: (NewsItem) -> Unit | ||
| ) : RecyclerView.ViewHolder(binding.root) { | ||
|
|
||
| fun bind(newsItem: NewsItem) { | ||
| binding.newsItem = newsItem | ||
| binding.root.setOnClickListener { | ||
| onClick(newsItem) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder { | ||
| val layoutInflater = LayoutInflater.from(parent.context) | ||
| val binding = ItemNewsBinding.inflate(layoutInflater, parent, false) | ||
| return NewsViewHolder(binding, onClick) | ||
| } | ||
|
|
||
| override fun getItemCount(): Int = newsList.count() | ||
|
|
||
| override fun onBindViewHolder(holder: NewsViewHolder, position: Int) { | ||
| holder.bind(newsList[position]) | ||
| } | ||
|
|
||
| @SuppressLint("NotifyDataSetChanged") | ||
| fun submitList(list: List<NewsItem>) { | ||
| newsList.clear() | ||
| newsList.addAll(list) | ||
| notifyDataSetChanged() | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 AIReviewRadar Review:
Overall, the code looks good and follows standard RecyclerView adapter implementation. However, please address the mentioned issues for better code quality. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.ui.fragments | ||
|
|
||
| import android.content.Intent | ||
| import android.os.Bundle | ||
| import android.view.LayoutInflater | ||
| import android.view.View | ||
| import android.view.ViewGroup | ||
| import androidx.core.net.toUri | ||
| import androidx.fragment.app.Fragment | ||
| import androidx.fragment.app.viewModels | ||
| import androidx.lifecycle.Lifecycle | ||
| import androidx.lifecycle.lifecycleScope | ||
| import androidx.lifecycle.repeatOnLifecycle | ||
| import androidx.recyclerview.widget.DividerItemDecoration | ||
| import com.krunal.demo.databinding.FragmentNewsListBinding | ||
| import com.krunal.demo.webservices.withoutlibrary.ui.adapters.NewsAdapter | ||
| import com.krunal.demo.webservices.withoutlibrary.ui.viewmodels.NewsViewModel | ||
| import kotlinx.coroutines.launch | ||
|
|
||
| class NewsListFragment : Fragment() { | ||
|
|
||
| private lateinit var binding: FragmentNewsListBinding | ||
| private val viewModel: NewsViewModel by viewModels() | ||
|
|
||
| override fun onCreateView( | ||
| inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? | ||
| ): View { | ||
| binding = FragmentNewsListBinding.inflate(layoutInflater, container, false) | ||
| binding.lifecycleOwner = viewLifecycleOwner | ||
| binding.viewModel = viewModel | ||
| return binding.root | ||
| } | ||
|
|
||
| override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| super.onViewCreated(view, savedInstanceState) | ||
| setupUI() | ||
| } | ||
|
|
||
| private fun setupUI() { | ||
| val adapter = NewsAdapter { newsItem -> | ||
| Intent(Intent.ACTION_VIEW, newsItem.url.toUri()).also { intent -> | ||
| activity?.startActivity(intent) | ||
| } | ||
| } | ||
|
|
||
| binding.rvNewsList.apply { | ||
| this.adapter = adapter | ||
| addItemDecoration( | ||
| DividerItemDecoration( | ||
| requireContext(), DividerItemDecoration.VERTICAL | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| viewLifecycleOwner.lifecycleScope.launch { | ||
| viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| viewModel.newsList.collect { list -> | ||
| adapter.submitList(list) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.krunal.demo.webservices.withoutlibrary.ui.viewmodels | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import androidx.lifecycle.viewModelScope | ||
| import com.krunal.demo.webservices.withoutlibrary.data.models.local.NewsItem | ||
| import com.krunal.demo.webservices.withoutlibrary.repositories.NewsRepository | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.flow.MutableStateFlow | ||
| import kotlinx.coroutines.flow.StateFlow | ||
| import kotlinx.coroutines.launch | ||
|
|
||
| class NewsViewModel : ViewModel() { | ||
|
|
||
| private val _newsList: MutableStateFlow<List<NewsItem>> = MutableStateFlow(emptyList()) | ||
| val newsList: StateFlow<List<NewsItem>> = _newsList | ||
|
|
||
| init { | ||
| loadData() | ||
| } | ||
|
|
||
| private fun loadData() { | ||
| viewModelScope.launch(Dispatchers.IO) { | ||
| _newsList.emit( | ||
| NewsRepository.getNewsArticles() | ||
| .map { NewsItem(it.title, it.description, it.url, it.urlToImage) } | ||
| ) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
|
|
||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| xmlns:tools="http://schemas.android.com/tools"> | ||
|
|
||
| <data> | ||
|
|
||
| <variable | ||
| name="viewModel" | ||
| type="com.krunal.demo.webservices.withoutlibrary.ui.viewmodels.NewsViewModel" /> | ||
| </data> | ||
|
|
||
| <androidx.constraintlayout.widget.ConstraintLayout | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent" | ||
| tools:context=".searchwebview.ui.fragments.SearchViewFragment"> | ||
|
|
||
| <androidx.recyclerview.widget.RecyclerView | ||
| android:id="@+id/rvNewsList" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="0dp" | ||
| android:layout_marginHorizontal="@dimen/dp_16" | ||
| app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | ||
| app:layout_constraintBottom_toBottomOf="parent" | ||
| app:layout_constraintEnd_toEndOf="parent" | ||
| app:layout_constraintStart_toStartOf="parent" | ||
| app:layout_constraintTop_toTopOf="parent" /> | ||
|
|
||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||
| </layout> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 AIReviewRadar
The code in
Demo/app/src/main/java/com/krunal/demo/searchwebview/ui/fragments/WebViewFragment.ktappears to follow the provided guidelines. There are no syntax errors, and the code structure looks fine. I don't see any security issues or logic errors in this file.The only suggestion I have is to add a comment explaining the purpose of the
setupWebView()function. This will make the code more self-explanatory for future developers who may work on it. Here's an example:Other than that, the code looks good. Please let me know if you need further assistance.