Add comment support

main
Clément FRÉVILLE 2 years ago
parent 8577dabf76
commit e089cb7e7b

@ -3,6 +3,8 @@ plugins {
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
} }
apply plugin: 'kotlin-kapt'
android { android {
namespace 'fr.uca.iut.clfreville2.teaiswarm' namespace 'fr.uca.iut.clfreville2.teaiswarm'
compileSdk 33 compileSdk 33
@ -10,7 +12,7 @@ android {
defaultConfig { defaultConfig {
applicationId "fr.uca.iut.clfreville2.teaiswarm" applicationId "fr.uca.iut.clfreville2.teaiswarm"
minSdk 21 minSdk 21
targetSdk 32 targetSdk 33
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -40,8 +42,9 @@ dependencies {
def paging_version = "3.1.1" def paging_version = "3.1.1"
def fragment_version = "1.5.6" def fragment_version = "1.5.6"
def preference_version = "1.2.0" def preference_version = "1.2.0"
def room_version = "2.5.1"
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.10.0'
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0' implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
@ -60,7 +63,10 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'io.coil-kt:coil:2.3.0' implementation 'io.coil-kt:coil:2.3.0'
//implementation 'androidx.core:core-ktx:+' implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

@ -14,6 +14,7 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.TeaIsWarm" android:theme="@style/Theme.TeaIsWarm"
android:name=".TeaIsWarm"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

@ -1,10 +1,15 @@
package fr.uca.iut.clfreville2.teaiswarm package fr.uca.iut.clfreville2.teaiswarm
import android.app.Application
import fr.uca.iut.clfreville2.teaiswarm.db.TeaDatabase
import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
object TeaIsWarm { class TeaIsWarm : Application() {
val database: TeaDatabase by lazy {
TeaDatabase.getInstance(this)
}
val service: RepositoryService by lazy { val service: RepositoryService by lazy {
GiteaService() GiteaService()
} }

@ -0,0 +1,71 @@
package fr.uca.iut.clfreville2.teaiswarm.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.db.CommentDao
import fr.uca.iut.clfreville2.teaiswarm.db.CommentEntity
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
class CommentListAdapter : ListAdapter<CommentEntity, CommentListAdapter.CommentViewHolder>(CommentComparator) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.comment_view_item, parent, false)
return CommentViewHolder(view)
}
override fun onBindViewHolder(holder: CommentViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current.comment)
}
class CommentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val commentText: EditText = itemView.findViewById(R.id.comment_edit_text)
fun bind(text: String?) {
commentText.setText(text)
}
}
object CommentComparator : DiffUtil.ItemCallback<CommentEntity>() {
override fun areItemsTheSame(oldItem: CommentEntity, newItem: CommentEntity): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: CommentEntity, newItem: CommentEntity): Boolean {
return oldItem.comment == newItem.comment
}
}
class CommentViewModel(
repository: RepositoryIdentifier,
commentDao: CommentDao
) : ViewModel() {
val allItems: LiveData<List<CommentEntity>> =
commentDao.getForRepository(repository.owner, repository.name).asLiveData()
}
class CommentViewModelFactory(
private val repository: RepositoryIdentifier,
private val commentDao: CommentDao
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(CommentViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return CommentViewModel(repository, commentDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}

@ -1,10 +1,11 @@
package fr.uca.iut.clfreville2.teaiswarm package fr.uca.iut.clfreville2.teaiswarm.adapter
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile
class FileListAdapter(private val dataSet: List<VersionedFile>, private val onClick: (VersionedFile) -> Unit) : class FileListAdapter(private val dataSet: List<VersionedFile>, private val onClick: (VersionedFile) -> Unit) :

@ -0,0 +1,20 @@
package fr.uca.iut.clfreville2.teaiswarm.db
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface CommentDao {
@Insert
fun insert(comment: CommentEntity)
@Update
fun update(comment: CommentEntity)
@Delete
fun delete(comment: CommentEntity)
@Query("SELECT * FROM comment WHERE owner = :owner AND repository = :repository")
fun getForRepository(owner: String, repository: String): Flow<List<CommentEntity>>
}

@ -0,0 +1,6 @@
package fr.uca.iut.clfreville2.teaiswarm.db
import androidx.room.Entity
@Entity(tableName = "comment", primaryKeys = ["id"])
data class CommentEntity(val id: Int? = null, val owner: String, val repository: String, val comment: String)

@ -0,0 +1,26 @@
package fr.uca.iut.clfreville2.teaiswarm.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [CommentEntity::class], version = 1, exportSchema = false)
abstract class TeaDatabase : RoomDatabase() {
abstract fun commentDao(): CommentDao
companion object {
@Volatile
private var instance: TeaDatabase? = null
fun getInstance(ctx: Context): TeaDatabase =
instance ?: synchronized(this) {
Room.databaseBuilder(
ctx.applicationContext,
TeaDatabase::class.java,
"tea.db"
).build().also { instance = it }
}
}
}

@ -31,7 +31,7 @@ import java.io.IOException
class ActivityListFragment : Fragment(R.layout.activity_list) { class ActivityListFragment : Fragment(R.layout.activity_list) {
private val service = TeaIsWarm.service private lateinit var service: RepositoryService
var repository: RepositoryIdentifier? = null var repository: RepositoryIdentifier? = null
var sha: String? = null var sha: String? = null
@ -39,6 +39,7 @@ class ActivityListFragment : Fragment(R.layout.activity_list) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
service = (activity?.application as TeaIsWarm).service
if (view is SwipeRefreshLayout) { if (view is SwipeRefreshLayout) {
view.setOnRefreshListener { view.setOnRefreshListener {
updateCommits() updateCommits()

@ -18,7 +18,7 @@ class CodeViewFragment : Fragment(R.layout.code_view_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val service = TeaIsWarm.service val service = (activity?.application as TeaIsWarm).service
val bundle = requireArguments() val bundle = requireArguments()
val repository = RepositoryIdentifier( val repository = RepositoryIdentifier(
bundle.getString(REPOSITORY_OWNER)!!, bundle.getString(REPOSITORY_OWNER)!!,

@ -0,0 +1,55 @@
package fr.uca.iut.clfreville2.teaiswarm.fragment
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_NAME
import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_OWNER
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
import fr.uca.iut.clfreville2.teaiswarm.adapter.CommentListAdapter
import fr.uca.iut.clfreville2.teaiswarm.db.CommentEntity
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
class CommentListFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val owner = arguments?.getString(REPOSITORY_OWNER)!!
val repo = arguments?.getString(REPOSITORY_NAME)!!
val repository = RepositoryIdentifier(owner, repo)
val viewModel: CommentListAdapter.CommentViewModel by activityViewModels {
CommentListAdapter.CommentViewModelFactory(
repository,
(activity?.application as TeaIsWarm).database.commentDao()
)
}
(activity?.application as TeaIsWarm).database.commentDao().insert(
CommentEntity(
null,
owner,
repo,
"test"
)
)
recyclerView = view.findViewById(R.id.comment_list)
val adapter = CommentListAdapter()
recyclerView.adapter = adapter
viewModel.allItems.observe(this.viewLifecycleOwner) { items ->
items.let {
adapter.submitList(it)
}
}
recyclerView.layoutManager = LinearLayoutManager(context)
}
}

@ -6,14 +6,18 @@ import android.widget.Button
import android.widget.TextView import android.widget.TextView
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.add
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import fr.uca.iut.clfreville2.teaiswarm.* import fr.uca.iut.clfreville2.teaiswarm.*
import fr.uca.iut.clfreville2.teaiswarm.adapter.FileListAdapter
import fr.uca.iut.clfreville2.teaiswarm.model.FileType import fr.uca.iut.clfreville2.teaiswarm.model.FileType
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.io.path.Path import kotlin.io.path.Path
@ -21,13 +25,13 @@ const val FILE_PATH = "file_path"
class RepositoryDetailsFragment : Fragment(R.layout.repository_details) { class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
private val service = TeaIsWarm.service private lateinit var service: RepositoryService
private lateinit var repositoryOwner: TextView private lateinit var repositoryOwner: TextView
private lateinit var repositoryName: TextView private lateinit var repositoryName: TextView
private lateinit var repositoryDescription: TextView private lateinit var repositoryDescription: TextView
private lateinit var repositoryStars: TextView private lateinit var repositoryStars: TextView
private lateinit var repositoryForks: TextView private lateinit var repositoryForks: TextView
private lateinit var activity: Button private lateinit var activityButton: Button
private lateinit var versionedFiles: RecyclerView private lateinit var versionedFiles: RecyclerView
private lateinit var currentRepositoryOwner: String private lateinit var currentRepositoryOwner: String
@ -36,12 +40,13 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
service = (activity?.application as TeaIsWarm).service
repositoryOwner = view.findViewById(R.id.repository_detail_owner) repositoryOwner = view.findViewById(R.id.repository_detail_owner)
repositoryName = view.findViewById(R.id.repository_detail_name) repositoryName = view.findViewById(R.id.repository_detail_name)
repositoryDescription = view.findViewById(R.id.repository_detail_description) repositoryDescription = view.findViewById(R.id.repository_detail_description)
repositoryStars = view.findViewById(R.id.repo_stars) repositoryStars = view.findViewById(R.id.repo_stars)
repositoryForks = view.findViewById(R.id.repo_forks) repositoryForks = view.findViewById(R.id.repo_forks)
activity = view.findViewById(R.id.repository_detail_activity) activityButton = view.findViewById(R.id.repository_detail_activity)
val bundle = requireArguments() val bundle = requireArguments()
currentRepositoryOwner = bundle.getString(REPOSITORY_OWNER)!! currentRepositoryOwner = bundle.getString(REPOSITORY_OWNER)!!
@ -67,7 +72,7 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
} }
} }
activity.setOnClickListener { activityButton.setOnClickListener {
findNavController().navigate(R.id.activity_list_fragment, bundle) findNavController().navigate(R.id.activity_list_fragment, bundle)
} }
@ -75,6 +80,16 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
resources.getStringArray(R.array.repository_tabs).forEach { resources.getStringArray(R.array.repository_tabs).forEach {
tabLayout.addTab(tabLayout.newTab().setText(it)) tabLayout.addTab(tabLayout.newTab().setText(it))
} }
parentFragmentManager.commit {
setReorderingAllowed(true)
add<CommentListFragment>(
R.id.comment_fragment_container, args = bundleOf(
REPOSITORY_OWNER to currentRepositoryOwner,
REPOSITORY_NAME to currentRepositoryName
)
)
}
} }
private fun adapterOnClick(file: VersionedFile) { private fun adapterOnClick(file: VersionedFile) {

@ -35,7 +35,7 @@ class RepositoryListFragment(
private val onClick: (Repository) -> Unit private val onClick: (Repository) -> Unit
) : Fragment(R.layout.repository_list) { ) : Fragment(R.layout.repository_list) {
private val service = TeaIsWarm.service private lateinit var service: RepositoryService
private var lateInit = true private var lateInit = true
private var search: SearchSettings by Delegates.observable(initialSearch) { _, _, _ -> private var search: SearchSettings by Delegates.observable(initialSearch) { _, _, _ ->
if (!lateInit) { if (!lateInit) {
@ -54,6 +54,7 @@ class RepositoryListFragment(
view.isRefreshing = false view.isRefreshing = false
} }
} }
service = (activity?.application as TeaIsWarm).service
recyclerView = view.findViewById(R.id.repositories_view) recyclerView = view.findViewById(R.id.repositories_view)
val spinner: Spinner = view.findViewById(R.id.sort_by_spinner) val spinner: Spinner = view.findViewById(R.id.sort_by_spinner)
ArrayAdapter.createFromResource( ArrayAdapter.createFromResource(

@ -25,10 +25,11 @@ class SetupConfigFragment(private val preferences: SharedPreferences) : Fragment
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
usernameInput = view.findViewById(R.id.username_input) usernameInput = view.findViewById(R.id.username_input)
confirmButton = view.findViewById(R.id.configure_button) confirmButton = view.findViewById(R.id.configure_button)
val service = (activity?.application as TeaIsWarm).service
confirmButton.setOnClickListener { confirmButton.setOnClickListener {
val username = usernameInput.text.toString() val username = usernameInput.text.toString()
lifecycleScope.launch { lifecycleScope.launch {
val owner = TeaIsWarm.service.searchOwner(username) val owner = service.searchOwner(username)
if (owner is Owner) { if (owner is Owner) {
preferences.edit { preferences.edit {
putString(USERNAME, usernameInput.text.toString()) putString(USERNAME, usernameInput.text.toString())

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/comment_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/comment_edit_text"
android:layout_width="match_parent"
android:layout_height="150dp"
android:inputType="text|textMultiLine" />
</LinearLayout>

@ -120,4 +120,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"/> app:layoutManager="LinearLayoutManager"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/comment_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout> </LinearLayout>

@ -2,5 +2,5 @@
plugins { plugins {
id 'com.android.application' version '7.3.0' apply false id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20-RC' apply false id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
} }
Loading…
Cancel
Save