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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,8 @@ If you find ShowCase useful and would like to support its development, please co
<a href="https://www.paypal.me/WirelessAlien">
<img src="https://github.com/user-attachments/assets/d2b47113-80e3-40f7-aeb1-a4e07c56c2ef" alt="paypal" width="100" />
</a>

## Team Members
* Mehmet Eren Tutas
* Doga Celebi
* Gaye Gulmez
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,15 @@ class CastActivity : BaseActivity() {
castMovieArrayList.add(actorMovies)
}
}

castMovieArrayList.sortWith(
compareByDescending<JSONObject> {
it.optDouble("vote_average", 0.0)
}.thenByDescending {
it.optDouble("vote_count", 0.0)
}.thenByDescending {
it.optDouble("popularity", 0.0)
}
)
castMovieAdapter = ShowBaseAdapter(context, castMovieArrayList, mShowGenreList!!, preferences.getBoolean(SHOWS_LIST_PREFERENCE, true))
binding.castMovieRecyclerView.adapter = castMovieAdapter
}
Expand All @@ -685,6 +693,15 @@ class CastActivity : BaseActivity() {
crewMovieArrayList.add(crewMovies)
}
}
crewMovieArrayList.sortWith(
compareByDescending<JSONObject> {
it.optDouble("vote_average", 0.0)
}.thenByDescending {
it.optDouble("vote_count", 0.0)
}.thenByDescending {
it.optDouble("popularity", 0.0)
}
)

crewMovieAdapter = ShowBaseAdapter(context, crewMovieArrayList, mShowGenreList!!, preferences.getBoolean(SHOWS_LIST_PREFERENCE, true))
binding.crewMovieRecyclerView.adapter = crewMovieAdapter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,25 +787,25 @@ class DetailActivity : BaseActivity(), ListTmdbBottomSheetFragment.OnListCreated
}

val shareText = """
*${title}*
🎬 $title

*Overview:*
$overview
${if (isMovie) "Movie" else "TV Show"}
⭐ TMDB: ${binding.rating.text}
IMDb: ${binding.imdbRatingChip.text.toString().replace("IMDb ", "")}
Metacritic: $metacriticRating
Rotten Tomatoes: $rottenTomatoRating

*TMDB Link:*
$tmdbLink
Overview:
$overview

*Ratings:*
- TMDB: ${binding.rating.text}
- IMDb: ${binding.imdbRatingChip.text.toString().replace("IMDb ", "")}
- Metacritic: $metacriticRating
- Rotten Tomatoes: $rottenTomatoRating
My MovieDB Notes:
• My Rating: $userRatingString
• My Review: ${reviewString.ifBlank { getString(R.string.rating_na) }}
• Watched: $timesWatchedString time(s)

- My Review: $reviewString
- I watched: $timesWatchedString times
- My $userRatingString

""".trimIndent()
Open on TMDB:
$tmdbLink
""".trimIndent()

val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.type = "text/plain"
Expand Down Expand Up @@ -4374,10 +4374,26 @@ class DetailActivity : BaseActivity(), ListTmdbBottomSheetFragment.OnListCreated
val reader = JSONObject(response)
val similarMovieArray = reader.getJSONArray("results")
similarMovieArrayList.clear()

for (i in 0 until similarMovieArray.length()) {
val movieData = similarMovieArray.getJSONObject(i)
similarMovieArrayList.add(movieData)

val isAdult = movieData.optBoolean("adult", false)
val posterPath = movieData.optString("poster_path", "")

if (!isAdult && posterPath.isNotBlank() && posterPath != "null") {
similarMovieArrayList.add(movieData)
}
}

similarMovieArrayList.sortWith(
compareByDescending<JSONObject> {
it.optDouble("vote_average", 0.0)
}.thenByDescending {
it.optDouble("popularity", 0.0)
}
)

similarMovieAdapter = SimilarMovieBaseAdapter(
similarMovieArrayList, applicationContext
)
Expand All @@ -4387,6 +4403,7 @@ class DetailActivity : BaseActivity(), ListTmdbBottomSheetFragment.OnListCreated
je.printStackTrace()
}
}

hideEmptyRecyclerView(binding.movieRecyclerView, binding.similarMovieTitle)
}

Expand Down Expand Up @@ -4511,12 +4528,42 @@ class DetailActivity : BaseActivity(), ListTmdbBottomSheetFragment.OnListCreated
val reviewsObject = movieData.getJSONObject("reviews")
val resultsArray = reviewsObject.getJSONArray("results")

// Extract the first three reviews
// Filter and prioritize reviews before showing them on the detail screen
val spoilerKeywords = listOf(
"spoiler", "ending", "final scene", "dies", "death", "killed", "killer", "reveals"
)

val reviewList = mutableListOf<JSONObject>()
for (i in 0 until minOf(3, resultsArray.length())) {
reviewList.add(resultsArray.getJSONObject(i))

for (i in 0 until resultsArray.length()) {
val review = resultsArray.getJSONObject(i)
val content = review.optString("content", "").trim()

if (content.isBlank() || content.length < 50) {
continue
}

val containsSpoiler = spoilerKeywords.any { keyword ->
content.contains(keyword, ignoreCase = true)
}

if (containsSpoiler) {
review.put("content", "⚠️ Possible spoiler\n\n$content")
}

reviewList.add(review)
}

reviewList.sortWith(
compareByDescending<JSONObject> {
it.optJSONObject("author_details")?.optDouble("rating", 0.0) ?: 0.0
}.thenByDescending {
it.optString("content", "").length
}
)

val limitedReviewList = reviewList.take(3)

// Set up the RecyclerView
val reviewAdapter = object : RecyclerView.Adapter<ReviewAdapter.ReviewViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReviewAdapter.ReviewViewHolder {
Expand All @@ -4525,10 +4572,10 @@ class DetailActivity : BaseActivity(), ListTmdbBottomSheetFragment.OnListCreated
}

override fun onBindViewHolder(holder: ReviewAdapter.ReviewViewHolder, position: Int) {
holder.bind(reviewList[position])
holder.bind(limitedReviewList[position])
}

override fun getItemCount(): Int = reviewList.size
override fun getItemCount(): Int = limitedReviewList.size
}

binding.recyclerViewReviews.layoutManager = LinearLayoutManager(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.json.JSONObject
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Date

class NowPlayingMovieAdapter(private val mShowArrayList: ArrayList<JSONObject>?) :
RecyclerView.Adapter<NowPlayingMovieAdapter.ShowItemViewHolder>() {
Expand Down Expand Up @@ -73,16 +74,36 @@ class NowPlayingMovieAdapter(private val mShowArrayList: ArrayList<JSONObject>?)
if (showData.has(KEY_NAME)) R.drawable.ic_tv_show else R.drawable.ic_movie
)

var dateString = if (showData.has(KEY_DATE_MOVIE)) showData.getString(KEY_DATE_MOVIE) else showData.getString(KEY_DATE_SERIES)
val rawDate = if (showData.has(KEY_DATE_MOVIE)) {
showData.optString(KEY_DATE_MOVIE, "")
} else {
showData.optString(KEY_DATE_SERIES, "")
}

val originalFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val localFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault())

try {
val date = originalFormat.parse(dateString)
val localFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault())
dateString = localFormat.format(date)
val releaseDate = originalFormat.parse(rawDate)
val today = Date()

if (releaseDate != null) {
val diffInMillis = releaseDate.time - today.time
val diffInDays = diffInMillis / (1000 * 60 * 60 * 24)

val formattedDate = localFormat.format(releaseDate)

holder.binding.date.text = when {
diffInDays > 0 -> "Upcoming • $formattedDate"
diffInDays in -30..0 -> "New • $formattedDate"
else -> formattedDate
}
Comment on lines +96 to +100

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Because of integer division and the time component of today, any future release date that is less than 24 hours away will result in diffInDays being 0, causing it to be incorrectly labeled as New instead of Upcoming. Using releaseDate.after(today) directly resolves this issue.

Suggested change
holder.binding.date.text = when {
diffInDays > 0 -> "Upcoming • $formattedDate"
diffInDays in -30..0 -> "New • $formattedDate"
else -> formattedDate
}
holder.binding.date.text = when {
releaseDate.after(today) -> "Upcoming • $formattedDate"
diffInDays >= -30 -> "New • $formattedDate"
else -> formattedDate
}

} else {
holder.binding.date.text = rawDate
}
} catch (e: ParseException) {
e.printStackTrace()
holder.binding.date.text = rawDate
}
holder.binding.date.text = dateString
} catch (e: JSONException) {
e.printStackTrace()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.wirelessalien.android.moviedb.R
import com.wirelessalien.android.moviedb.databinding.ReviewItemBinding
import org.json.JSONObject
import java.util.Locale
import android.graphics.Typeface

class ReviewAdapter :
PagingDataAdapter<JSONObject, ReviewAdapter.ReviewViewHolder>(ReviewDiffCallback()) {
Expand Down Expand Up @@ -77,18 +78,26 @@ class ReviewAdapter :
} else {
binding.root.context.getString(R.string.rating_na)
}
// Set content with max lines and click listener
// Set content with spoiler warning, max lines and click listener
val content = review.optString("content")
binding.textViewContent.text = content
binding.textViewContent.post {
if (binding.textViewContent.lineCount > 3) {
binding.textViewContent.maxLines = 3
isContentExpanded = false
} else {
isContentExpanded = true
}
val isSpoiler = content.startsWith("⚠️ Possible spoiler")

if (isSpoiler) {
val cleanedContent = content
.replace("⚠️ Possible spoiler", "")
.trim()

binding.textViewContent.text = "⚠️ SPOILER WARNING\n\n$cleanedContent"
binding.textViewContent.setTypeface(null, Typeface.BOLD)
binding.textViewContent.alpha = 0.9f
} else {
binding.textViewContent.text = content
binding.textViewContent.setTypeface(null, Typeface.NORMAL)
binding.textViewContent.alpha = 1.0f
}

binding.textViewContent.post {

binding.textViewContent.setOnClickListener {
Comment on lines +81 to 101

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The post block on textViewContent is left unclosed, which causes a compilation error. Additionally, the original logic to initialize isContentExpanded and set maxLines based on the line count was removed. Let's restore the block and close it properly.

            // Set content with spoiler warning, max lines and click listener
            val content = review.optString("content")
            val isSpoiler = content.startsWith("⚠️ Possible spoiler")

            if (isSpoiler) {
                val cleanedContent = content
                    .replace("⚠️ Possible spoiler", "")
                    .trim()

                binding.textViewContent.text = "⚠️ SPOILER WARNING\n\n$cleanedContent"
                binding.textViewContent.setTypeface(null, Typeface.BOLD)
                binding.textViewContent.alpha = 0.9f
            } else {
                binding.textViewContent.text = content
                binding.textViewContent.setTypeface(null, Typeface.NORMAL)
                binding.textViewContent.alpha = 1.0f
            }

            binding.textViewContent.post {
                if (binding.textViewContent.lineCount > 3) {
                    binding.textViewContent.maxLines = 3
                    isContentExpanded = false
                } else {
                    isContentExpanded = true
                }
            }

            binding.textViewContent.setOnClickListener {

if (binding.textViewContent.lineCount > 3) {
isContentExpanded = !isContentExpanded
Expand All @@ -111,6 +120,17 @@ class ReviewAdapter :
return oldItem.getString("id") == newItem.getString("id")
}

override fun areContentsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.toString() == newItem.toString()
}
}
}

class ReviewDiffCallback : DiffUtil.ItemCallback<JSONObject>() {
override fun areItemsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.optString("id") == newItem.optString("id")
}

override fun areContentsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.toString() == newItem.toString()
}
Comment on lines 120 to 136

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

There is a duplicated definition of ReviewDiffCallback and broken closing braces at the end of the file, which prevents compilation. Let's clean this up to have a single, correctly closed ReviewDiffCallback nested class.

Suggested change
return oldItem.getString("id") == newItem.getString("id")
}
override fun areContentsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.toString() == newItem.toString()
}
}
}
class ReviewDiffCallback : DiffUtil.ItemCallback<JSONObject>() {
override fun areItemsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.optString("id") == newItem.optString("id")
}
override fun areContentsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.toString() == newItem.toString()
}
return oldItem.optString("id") == newItem.optString("id")
}
override fun areContentsTheSame(oldItem: JSONObject, newItem: JSONObject): Boolean {
return oldItem.toString() == newItem.toString()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class HomeFragment : BaseFragment() {
private lateinit var mTVShowAdapter: NowPlayingMovieAdapter
private lateinit var mUpcomingMovieAdapter: NowPlayingMovieAdapter
private lateinit var mUpcomingTVAdapter: NowPlayingMovieAdapter
private lateinit var mTopRatedMovieArrayList: ArrayList<JSONObject>
private lateinit var mTopRatedMovieAdapter: NowPlayingMovieAdapter
private lateinit var binding: FragmentHomeBinding
private lateinit var activityBinding: ActivityMainBinding
private lateinit var menuProvider: MenuProvider
Expand All @@ -101,6 +103,7 @@ class HomeFragment : BaseFragment() {
fetchTrendingList()
fetchUpcomingMovies()
fetchUpcomingTVShows()

}
}
}
Expand All @@ -117,6 +120,7 @@ class HomeFragment : BaseFragment() {
showTrendingList()
showUpcomingMovieList()
showUpcomingTVShowList()
// showTopRatedMovieList()
activityBinding.fab2.visibility = View.GONE
activityBinding.fab.visibility = View.GONE
activityBinding.searchView.setupWithSearchBar(binding.searchbar)
Expand Down Expand Up @@ -227,11 +231,13 @@ class HomeFragment : BaseFragment() {
mUpcomingTVAdapter = NowPlayingMovieAdapter(mUpcomingTVShowArrayList)
mUpcomingMovieArrayList = ArrayList()
mUpcomingMovieAdapter = NowPlayingMovieAdapter(mUpcomingMovieArrayList)
mTopRatedMovieArrayList = ArrayList()
mTopRatedMovieAdapter = NowPlayingMovieAdapter(mTopRatedMovieArrayList)
(requireActivity() as BaseActivity).checkNetwork()
}

private fun setupRecyclerView(
recyclerView: RecyclerView?,
recyclerView: RecyclerView,
layoutManager: LinearLayoutManager,
adapter: RecyclerView.Adapter<*>?
) {
Expand All @@ -258,7 +264,10 @@ class HomeFragment : BaseFragment() {
val layoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
setupRecyclerView(binding.upcomingMovieRecyclerView, layoutManager, mUpcomingMovieAdapter)
}

private fun showTopRatedMovieList() {
val layoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
setupRecyclerView(binding.upcomingMovieRecyclerView, layoutManager, mTopRatedMovieAdapter)
}
private suspend fun fetchUpcomingTVShows() {
withContext(Dispatchers.Main) {
binding.shimmerFrameLayout5.visibility = View.VISIBLE
Expand All @@ -272,7 +281,7 @@ class HomeFragment : BaseFragment() {
val response = fetchData(url)
withContext(Dispatchers.Main) {
if (isAdded && !response.isNullOrEmpty()) {
handleUpcomingTVResponse(response)
handleMovieResponse(response)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

In fetchUpcomingTVShows(), calling handleMovieResponse(response) instead of handleUpcomingTVResponse(response) causes TV show data to be loaded into the movie adapter/recycler view, overriding the now playing movies and failing to hide the shimmer for upcoming TV shows. Let's restore the correct handler.

Suggested change
handleMovieResponse(response)
handleUpcomingTVResponse(response)

} else {
binding.shimmerFrameLayout5.visibility = View.GONE
binding.shimmerFrameLayout5.stopShimmer()
Expand Down Expand Up @@ -323,7 +332,7 @@ class HomeFragment : BaseFragment() {
val response = fetchData(url)
withContext(Dispatchers.Main) {
if (isAdded && !response.isNullOrEmpty()) {
handleMovieResponse(response)
//SDFKLNHGJKSHKDJFGHSJKLGHDFJKFDGHKJFDHJKFGDHJGIDFK

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The keyboard smash comment completely disables handling the movie response for now playing movies, leaving the UI in a permanent shimmer state when data is successfully fetched. Let's restore the correct call to handleMovieResponse(response).

Suggested change
//SDFKLNHGJKSHKDJFGHSJKLGHDFJKFDGHKJFDHJKFGDHJGIDFK
handleMovieResponse(response)

} else {
binding.shimmerFrameLayout2.visibility = View.GONE
binding.shimmerFrameLayout2.stopShimmer()
Expand Down Expand Up @@ -374,10 +383,12 @@ class HomeFragment : BaseFragment() {
val reader = JSONObject(response)
val arrayData = reader.getJSONArray("results")
mHomeShowArrayList.clear()

for (i in 0 until arrayData.length()) {
val websiteData = arrayData.getJSONObject(i)
mHomeShowArrayList.add(websiteData)
}

binding.nowPlayingRecyclerView.adapter = mHomeShowAdapter
mShowListLoaded = true
binding.shimmerFrameLayout2.visibility = View.GONE
Expand Down