chrisbanes / tivi

Tivi is a TV show tracking Android app, which connects to trakt.tv
Apache License 2.0
6.6k stars 877 forks source link

Please, help me for this. #905

Closed esperantgada closed 2 years ago

esperantgada commented 2 years ago

I'm seaching solution for this since five days. I'm one of GADS 2021 scholars and have Google Android Developer Certification exam in few days, this month. I'm working on a small project that includes Room Database with four tables and there're two tables Student and Course with N to N relationship. My goal is to to search a student by his name and display all the courses taken by this one in a recycler view using ListAdapter or Paging library but with my code, it doesn't work and I don't know the reason. So I'm looking for a way to do that.I know if I find a solution for this, I can use it for other parts of the project. I really need help for this. I want to use it in an other app I want to publish on PlayStore, that app will be my first app. Here is what I did in the project I'm working with:

MY TWO TABLES OR ENTITIES:

@Entity(tableName = "course_table")
data class Course(
    @PrimaryKey(autoGenerate = false)
    val courseName : String,

    @ColumnInfo(name = "course_duration")
    val courseDuration : String
)
@Entity(tableName = "student_table")
data class Student(
    @PrimaryKey(autoGenerate = false)
    val studentName : String,

    val semester : Int,

    val schoolName : String
)

RELATIONS BETWEEN TABLES

@Entity(primaryKeys = ["studentName", "courseName"])
data class StudentAndCourseTogether(
    val studentName : String,
    val courseName : String
)
data class CourseAndStudent(
    @Embedded
    val course : Course,

    @Relation(
        parentColumn = "courseName",
        entityColumn = "studentName",
        associateBy = Junction(StudentAndCourseTogether::class)
    )

    val students : List<Student>
)
data class StudentAndCourse(
    @Embedded
    val student : Student,

    @Relation(parentColumn = "studentName", entityColumn = "courseName", associateBy = Junction(StudentAndCourseTogether::class))
    val courses : List<Course>
)

MY QUERY TO GET ALL COURSES TAKEN BY A STUDENT

@Transaction
    @Query("SELECT * FROM student_table WHERE studentName = :studentName")
    fun getAllCoursesByStudentName(studentName: String) : Flow<List<StudentAndCourse>>

MY REPOSITORY CODE

val allCourseByStudentName = allItemsByNameDao.getAllCoursesByStudentName(studentName = "Esperant")

MY VIEWMODEL CODE

val coursesByStudentName : LiveData<List<StudentAndCourse>> = allItemsByNameRepository.allCourseByStudentName.asLiveData()

MY ADAPTER CODE

class CourseByStudentNameAdapter :
    ListAdapter<Course,
            CourseByStudentNameAdapter.CourseByStudentNameViewHolder>(DiffCallback) {

    class CourseByStudentNameViewHolder(private val binding: CourseByStudentNameItemBinding) :
        RecyclerView.ViewHolder(binding.root){

        fun bind(course: Course){
            binding.courseName.text = course.courseName
            binding.courseDuration.text = course.courseDuration
        }

    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int,
    ): CourseByStudentNameViewHolder {
        val inflatedLayout = CourseByStudentNameItemBinding.inflate(LayoutInflater.from(
            parent.context), parent, false)

        return CourseByStudentNameViewHolder(inflatedLayout)
    }

    override fun onBindViewHolder(holder: CourseByStudentNameViewHolder, position: Int) {
        val currentCourse = getItem(position)
        if (currentCourse != null) {
            holder.bind(currentCourse)
        }
    }

    companion object DiffCallback : DiffUtil.ItemCallback<Course>(){
        override fun areItemsTheSame(
            oldItem: Course,
            newItem: Course
        ): Boolean = oldItem.courseName == newItem.courseName

        override fun areContentsTheSame(
            oldItem: Course,
            newItem: Course
        ): Boolean = oldItem == newItem

    }
}

MY ASSOCIETED FRAGMENT CODE

@AndroidEntryPoint
class AllCourseByStudentNameFragment : Fragment() {

    private var _binding : FragmentAllCourseByStudentNameBinding? = null
    private val binding get() = _binding!!

    private val viewModel : AllItemsByNameViewModel by activityViewModels()

    private lateinit var adapter : CourseByStudentNameAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setHasOptionsMenu(true)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        // Inflate the layout for this fragment
        _binding = FragmentAllCourseByStudentNameBinding.inflate(inflater)
        return binding.root
    }

    @SuppressLint("LongLogTag")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        adapter = CourseByStudentNameAdapter()
        binding.apply {
            recyclerView.adapter = adapter
            recyclerView.layoutManager = LinearLayoutManager(requireContext())
            recyclerView.setHasFixedSize(true)
        }

        viewModel.setName(enteredName = "Esperant")

        viewModel.coursesByStudentName.observe(viewLifecycleOwner){
            for (course in it){
                adapter.submitList(course.courses)
            }
            Log.d(TAG, "The returned list size is ${it.size}")
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()

        _binding = null
    }
}

MY XML CODE

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.appcompat.widget.LinearLayoutCompat
        android:id="@+id/linear_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center"
        android:layout_marginTop="4dp"
        style="@style/itemListTextStyle"
        android:background="@drawable/item_layout_background"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <TextView
            android:id="@+id/course_name"
            android:layout_width="match_parent"
            tools:text="Computer"
            android:gravity="center"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/course_duration"
            android:layout_width="match_parent"
            tools:text="15 hours"
            android:gravity="center"
            android:layout_height="wrap_content"/>

    </androidx.appcompat.widget.LinearLayoutCompat>

</androidx.constraintlayout.widget.ConstraintLayout>

The project repository link

https://github.com/esperantgada/Room_With_Multiple_Tables

I'll be very glad to hear from you, it's very important for me. Thanks in advance

chrisbanes commented 2 years ago

I'm afraid this isn't the place to ask for development help. I'd recommend asking on StackOverflow.