Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
6 changes: 6 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'com.google.devtools.ksp'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services'

Expand Down Expand Up @@ -61,6 +63,10 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.8.0'
implementation 'androidx.core:core-ktx:1.19.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.11.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.11.0'
implementation 'androidx.activity:activity-ktx:1.8.0'
implementation "com.google.dagger:hilt-android:$hilt_version"
ksp "com.google.dagger:hilt-compiler:$hilt_version"
implementation 'androidx.constraintlayout:constraintlayout:2.2.2'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0'
testImplementation 'junit:junit:4.13.2'
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:name=".CourseGrabApplication"
android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
android:icon="@mipmap/ic_launcher"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,31 @@ package com.cornellappdev.coursegrab

import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.cornellappdev.coursegrab.databinding.ActivityCourseDetailsBinding
import com.cornellappdev.coursegrab.models.Course
import com.cornellappdev.coursegrab.models.SearchResult
import com.cornellappdev.coursegrab.networking.CourseGrabRepository
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

@AndroidEntryPoint
class CourseDetailsActivity : AppCompatActivity() {
private lateinit var binding: ActivityCourseDetailsBinding

private lateinit var sectionsRecyclerView: RecyclerView
private lateinit var sectionsViewAdapter: RecyclerView.Adapter<*>
private lateinit var sectionsViewManager: RecyclerView.LayoutManager

private val repository: CourseGrabRepository by lazy {
CourseGrabRepository(PreferencesHelper(this))
}
private val viewModel: CourseDetailsViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -44,44 +41,26 @@ class CourseDetailsActivity : AppCompatActivity() {
binding.courseDetails.text =
if (courseDetails.sections.isNotEmpty()) courseDetails.sections.first().instructors.first() else "To Be Assigned"

// Available Courses Adapter
sectionsViewManager = LinearLayoutManager(this@CourseDetailsActivity)
sectionsViewAdapter = SectionAdapter(courseDetails.sections, this@CourseDetailsActivity)

sectionsRecyclerView = binding.sectionsRecyclerview.apply {
layoutManager = sectionsViewManager
adapter = sectionsViewAdapter
binding.sectionsRecyclerview.apply {
layoutManager = LinearLayoutManager(this@CourseDetailsActivity)
adapter = SectionAdapter(courseDetails.sections, this@CourseDetailsActivity)
}

binding.backBtn.setOnClickListener { finish() }
}

fun addCourse(courseId: Int, context: Context) {
lifecycleScope.launch {
repository.addTracking(courseId).onFailure { error ->
Log.e(TAG, "Failed to track course $courseId", error)
Toast.makeText(
context,
error.message ?: "Couldn't track that course.",
Toast.LENGTH_SHORT
).show()
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.trackingErrors.collect { error ->
Toast.makeText(this@CourseDetailsActivity, error, Toast.LENGTH_SHORT).show()
}
}
}
}

fun removeCourse(courseId: Int, context: Context) {
lifecycleScope.launch {
repository.removeTracking(courseId).onFailure { error ->
Log.e(TAG, "Failed to untrack course $courseId", error)
Toast.makeText(
context,
error.message ?: "Couldn't remove that course.",
Toast.LENGTH_SHORT
).show()
}
}
binding.backBtn.setOnClickListener { finish() }
}

fun addCourse(courseId: Int) = viewModel.addCourse(courseId)

fun removeCourse(courseId: Int) = viewModel.removeCourse(courseId)

class SectionAdapter(
private val availableCourses: List<Course>,
private val context: Context
Expand All @@ -108,7 +87,7 @@ class CourseDetailsActivity : AppCompatActivity() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {

holder.sectionTitle.text = availableCourses[position].section
holder.sectionStatus.setImageResource(if (availableCourses[position].status == "OPEN") R.drawable.ic_status_open else R.drawable.ic_status_closed)
holder.sectionStatus.setImageResource(if (availableCourses[position].isOpen) R.drawable.ic_status_open else R.drawable.ic_status_closed)

//change tracking text
holder.trackText.text = availableCourses[position].num_tracking.toString() + " Tracking"
Expand All @@ -121,8 +100,7 @@ class CourseDetailsActivity : AppCompatActivity() {

holder.removeButton.setOnClickListener {
(context as CourseDetailsActivity).removeCourse(
availableCourses[position].catalog_num,
context
availableCourses[position].catalog_num
)
holder.removeButton.visibility = View.GONE
holder.trackButton.visibility = View.VISIBLE
Expand All @@ -136,8 +114,7 @@ class CourseDetailsActivity : AppCompatActivity() {

holder.trackButton.setOnClickListener {
(context as CourseDetailsActivity).addCourse(
availableCourses[position].catalog_num,
context
availableCourses[position].catalog_num
)
holder.trackButton.visibility = View.GONE
holder.removeButton.visibility = View.VISIBLE
Expand All @@ -153,8 +130,4 @@ class CourseDetailsActivity : AppCompatActivity() {
// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = availableCourses.size
}

companion object {
private const val TAG = "CourseDetailsActivity"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.cornellappdev.coursegrab

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.cornellappdev.coursegrab.networking.CourseGrabRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class CourseDetailsViewModel @Inject constructor(
private val repository: CourseGrabRepository
) : ViewModel() {

private val _trackingErrors = Channel<String>(Channel.BUFFERED)
val trackingErrors = _trackingErrors.receiveAsFlow()

fun addCourse(courseId: Int) = track(courseId, adding = true)

fun removeCourse(courseId: Int) = track(courseId, adding = false)

private fun track(courseId: Int, adding: Boolean) {
viewModelScope.launch {
val result = if (adding) repository.addTracking(courseId)
else repository.removeTracking(courseId)

result.onFailure { error ->
Log.e(TAG, "Failed to ${if (adding) "track" else "untrack"} $courseId", error)
_trackingErrors.send(
error.message ?: if (adding) "Couldn't track that course."
else "Couldn't remove that course."
)
}
}
}

private companion object {
const val TAG = "CourseDetailsViewModel"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cornellappdev.coursegrab

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class CourseGrabApplication : Application()
Loading