Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ object IntentData {
*/
const val MESSAGE = "message"
const val AUTH_KEY = "auth_key"
const val WEB_URL = "web_url"

/**
* Actions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.krunal.demo.uicomponents.constraintLayouts.GuidelineBarrierFragment
import com.krunal.demo.uicomponents.constraintLayouts.RelativeFragment
import com.krunal.demo.uicomponents.picker.DatePickerFragment
import com.krunal.demo.uicomponents.picker.TimePickerFragment
import com.krunal.demo.webservices.withoutlibrary.ui.fragments.NewsListFragment

object ComponentDetailRepository {

Expand All @@ -55,6 +56,8 @@ object ComponentDetailRepository {
"App Component", appComponents
), ComponentDetail.ActivityComponent(
"Search and Web view", SearchWebActivity::class.java
), ComponentDetail.FragmentComponent(
"News List", NewsListFragment::class.java
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ object WebViewRepository {
"m.youtube.com"
)

fun getRandomUrl(): String = urls.random()
fun getRandomUrl(): String = "https://google.com"//urls.random()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown

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.kt appears 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:

private fun setupWebView() {
    // Add any WebView setup code here
    // For example, you can set up the WebView client and enable JavaScript
    // ...
}

Other than that, the code looks good. Please let me know if you need further assistance.

Expand Down Expand Up @@ -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
Copy link
Copy Markdown

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.kt appears 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 addition of arguments?.getString(IntentData.WEB_URL)?.let(viewModel::loadUrl) inside the setupWebView() function is a valid change. It retrieves the WEB_URL argument from the fragment's arguments bundle and passes it to the loadUrl() function of the viewModel. This allows the WebView to load the specified URL.

Overall, the code looks good. Please let me know if you need further assistance.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.krunal.demo.uicomponents.adapters

import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.media.ThumbnailUtils
import android.view.View
import android.widget.ImageView
import android.widget.TextView
Expand All @@ -10,6 +12,12 @@ import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.ColorUtils
import androidx.databinding.BindingAdapter
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.net.HttpURLConnection
import java.net.URL

@BindingAdapter("drawableResource")
fun ImageView.setDrawableResource(@DrawableRes drawableResource: Int) {
Expand Down Expand Up @@ -58,4 +66,17 @@ fun View.setGradientBackground(gradientStartColor: Int?, gradientEndColor: Int?,
}
}
}
}

@BindingAdapter("imageUrl")
fun ImageView.bindImage(url: String?) {
if (url == null) return

findViewTreeLifecycleOwner()?.lifecycleScope?.launch(Dispatchers.IO) {
runCatching {
val urlConnection = URL(url).openConnection() as HttpURLConnection
val bitmap = BitmapFactory.decodeStream(urlConnection.inputStream)
setImageBitmap(ThumbnailUtils.extractThumbnail(bitmap, 200, 200))
}
}
}
7 changes: 7 additions & 0 deletions Demo/app/src/main/java/com/krunal/demo/utils/AppConstants.kt
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
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 AIReviewRadar

The code changes in the NewsApi.kt file look good. Here's my review:

  • The package name and imports are correct.
  • The NewsApi object is defined correctly.
  • The HEADLINE_URL constant is defined properly using string interpolation.
  • The getNews() function is implemented correctly as a suspend function.
  • The HTTP request is made using HttpURLConnection, and the response is read and processed correctly.
  • The JSON response is parsed using JSONObject and the required data is extracted.
  • The articles are parsed from the JSON response and added to the articles list.
  • The NewsResponse object is created with the extracted data and returned.

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()
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 AIReviewRadar

Review:

  • The NewsAdapter class is missing a package declaration. Please add package com.krunal.demo.webservices.withoutlibrary.ui.adapters at the top of the file.

  • The NewsAdapter class should be marked as open or internal to indicate its visibility. Since it's not specified, I assume it should be internal for now.

  • The onClick parameter in the NewsAdapter constructor should have a more descriptive name. Consider renaming it to something like onNewsItemClick.

  • The newsList property should be declared as private to encapsulate access to it.

  • The NewsViewHolder class should be marked as inner since it accesses the newsList property of the outer class.

  • In the bind function of NewsViewHolder, consider using a lambda expression instead of an anonymous function for the click listener. This can make the code more concise and readable. For example: binding.root.setOnClickListener { onClick(newsItem) }

  • In the onCreateViewHolder function, consider using the inflate extension function directly on the ViewGroup instead of creating a LayoutInflater object. This can simplify the code. For example: val binding = ItemNewsBinding.inflate(parent.inflater, parent, false)

  • The submitList function is missing a return type. It should be declared as fun submitList(list: List<NewsItem>): Unit.

  • Instead of calling notifyDataSetChanged() in the submitList function, consider using notifyItemRangeInserted() or notifyItemRangeRemoved() to provide more granular updates if possible. This can improve performance when dealing with large data sets.

  • Add a newline at the end of the file to follow coding conventions.

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) }
)
}
}
}
31 changes: 31 additions & 0 deletions Demo/app/src/main/res/layout/fragment_news_list.xml
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>
Loading