From e089cb7e7b5664b04ba3e1d48f7240707bc5a5bc Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Sun, 9 Apr 2023 22:07:42 +0200 Subject: [PATCH] Add comment support --- app/build.gradle | 12 +++- app/src/main/AndroidManifest.xml | 1 + .../iut/clfreville2/teaiswarm/TeaIsWarm.kt | 7 +- .../teaiswarm/adapter/CommentListAdapter.kt | 71 +++++++++++++++++++ .../{ => adapter}/FileListAdapter.kt | 3 +- .../clfreville2/teaiswarm/db/CommentDao.kt | 20 ++++++ .../clfreville2/teaiswarm/db/CommentEntity.kt | 6 ++ .../clfreville2/teaiswarm/db/TeaDatabase.kt | 26 +++++++ .../fragment/ActivityListFragment.kt | 3 +- .../teaiswarm/fragment/CodeViewFragment.kt | 2 +- .../teaiswarm/fragment/CommentListFragment.kt | 55 ++++++++++++++ .../fragment/RepositoryDetailsFragment.kt | 23 ++++-- .../fragment/RepositoryListFragment.kt | 3 +- .../teaiswarm/fragment/SetupConfigFragment.kt | 3 +- .../main/res/layout/comment_list_fragment.xml | 9 +++ app/src/main/res/layout/comment_view_item.xml | 12 ++++ .../main/res/layout/repository_details.xml | 5 ++ build.gradle | 2 +- 18 files changed, 249 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/adapter/CommentListAdapter.kt rename app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/{ => adapter}/FileListAdapter.kt (93%) create mode 100644 app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentDao.kt create mode 100644 app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentEntity.kt create mode 100644 app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/TeaDatabase.kt create mode 100644 app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CommentListFragment.kt create mode 100644 app/src/main/res/layout/comment_list_fragment.xml create mode 100644 app/src/main/res/layout/comment_view_item.xml diff --git a/app/build.gradle b/app/build.gradle index 16c1f2d..3d56247 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,6 +3,8 @@ plugins { id 'org.jetbrains.kotlin.android' } +apply plugin: 'kotlin-kapt' + android { namespace 'fr.uca.iut.clfreville2.teaiswarm' compileSdk 33 @@ -10,7 +12,7 @@ android { defaultConfig { applicationId "fr.uca.iut.clfreville2.teaiswarm" minSdk 21 - targetSdk 32 + targetSdk 33 versionCode 1 versionName "1.0" @@ -40,8 +42,9 @@ dependencies { def paging_version = "3.1.1" def fragment_version = "1.5.6" 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 'com.google.android.material:material:1.8.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' @@ -60,7 +63,10 @@ dependencies { implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.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' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 568cfe1..9a92ee7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.TeaIsWarm" + android:name=".TeaIsWarm" tools:targetApi="31"> (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() { + 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> = + commentDao.getForRepository(repository.owner, repository.name).asLiveData() + } + + class CommentViewModelFactory( + private val repository: RepositoryIdentifier, + private val commentDao: CommentDao + ) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(CommentViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return CommentViewModel(repository, commentDao) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/FileListAdapter.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/adapter/FileListAdapter.kt similarity index 93% rename from app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/FileListAdapter.kt rename to app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/adapter/FileListAdapter.kt index 0339bc7..fa31d0a 100644 --- a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/FileListAdapter.kt +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/adapter/FileListAdapter.kt @@ -1,10 +1,11 @@ -package fr.uca.iut.clfreville2.teaiswarm +package fr.uca.iut.clfreville2.teaiswarm.adapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView +import fr.uca.iut.clfreville2.teaiswarm.R import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile class FileListAdapter(private val dataSet: List, private val onClick: (VersionedFile) -> Unit) : diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentDao.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentDao.kt new file mode 100644 index 0000000..159fd9a --- /dev/null +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentDao.kt @@ -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> +} \ No newline at end of file diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentEntity.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentEntity.kt new file mode 100644 index 0000000..11dd160 --- /dev/null +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/CommentEntity.kt @@ -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) diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/TeaDatabase.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/TeaDatabase.kt new file mode 100644 index 0000000..723d57f --- /dev/null +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/db/TeaDatabase.kt @@ -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 } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/ActivityListFragment.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/ActivityListFragment.kt index 63491e6..c268cc7 100644 --- a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/ActivityListFragment.kt +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/ActivityListFragment.kt @@ -31,7 +31,7 @@ import java.io.IOException class ActivityListFragment : Fragment(R.layout.activity_list) { - private val service = TeaIsWarm.service + private lateinit var service: RepositoryService var repository: RepositoryIdentifier? = null var sha: String? = null @@ -39,6 +39,7 @@ class ActivityListFragment : Fragment(R.layout.activity_list) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + service = (activity?.application as TeaIsWarm).service if (view is SwipeRefreshLayout) { view.setOnRefreshListener { updateCommits() diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CodeViewFragment.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CodeViewFragment.kt index 1c0404d..fbac142 100644 --- a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CodeViewFragment.kt +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CodeViewFragment.kt @@ -18,7 +18,7 @@ class CodeViewFragment : Fragment(R.layout.code_view_fragment) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val service = TeaIsWarm.service + val service = (activity?.application as TeaIsWarm).service val bundle = requireArguments() val repository = RepositoryIdentifier( bundle.getString(REPOSITORY_OWNER)!!, diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CommentListFragment.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CommentListFragment.kt new file mode 100644 index 0000000..9a512c2 --- /dev/null +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/CommentListFragment.kt @@ -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) + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryDetailsFragment.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryDetailsFragment.kt index ca50fc3..79529ec 100644 --- a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryDetailsFragment.kt +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryDetailsFragment.kt @@ -6,14 +6,18 @@ import android.widget.Button import android.widget.TextView import androidx.core.os.bundleOf import androidx.fragment.app.Fragment +import androidx.fragment.app.add +import androidx.fragment.app.commit import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.RecyclerView import com.google.android.material.tabs.TabLayout 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.RepositoryIdentifier import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile +import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService import kotlinx.coroutines.launch import kotlin.io.path.Path @@ -21,13 +25,13 @@ const val FILE_PATH = "file_path" 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 repositoryName: TextView private lateinit var repositoryDescription: TextView private lateinit var repositoryStars: TextView private lateinit var repositoryForks: TextView - private lateinit var activity: Button + private lateinit var activityButton: Button private lateinit var versionedFiles: RecyclerView private lateinit var currentRepositoryOwner: String @@ -36,12 +40,13 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + service = (activity?.application as TeaIsWarm).service repositoryOwner = view.findViewById(R.id.repository_detail_owner) repositoryName = view.findViewById(R.id.repository_detail_name) repositoryDescription = view.findViewById(R.id.repository_detail_description) repositoryStars = view.findViewById(R.id.repo_stars) 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() 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) } @@ -75,6 +80,16 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) { resources.getStringArray(R.array.repository_tabs).forEach { tabLayout.addTab(tabLayout.newTab().setText(it)) } + + parentFragmentManager.commit { + setReorderingAllowed(true) + add( + R.id.comment_fragment_container, args = bundleOf( + REPOSITORY_OWNER to currentRepositoryOwner, + REPOSITORY_NAME to currentRepositoryName + ) + ) + } } private fun adapterOnClick(file: VersionedFile) { diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryListFragment.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryListFragment.kt index 8a6f4a8..62d1c42 100644 --- a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryListFragment.kt +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/RepositoryListFragment.kt @@ -35,7 +35,7 @@ class RepositoryListFragment( private val onClick: (Repository) -> Unit ) : Fragment(R.layout.repository_list) { - private val service = TeaIsWarm.service + private lateinit var service: RepositoryService private var lateInit = true private var search: SearchSettings by Delegates.observable(initialSearch) { _, _, _ -> if (!lateInit) { @@ -54,6 +54,7 @@ class RepositoryListFragment( view.isRefreshing = false } } + service = (activity?.application as TeaIsWarm).service recyclerView = view.findViewById(R.id.repositories_view) val spinner: Spinner = view.findViewById(R.id.sort_by_spinner) ArrayAdapter.createFromResource( diff --git a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/SetupConfigFragment.kt b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/SetupConfigFragment.kt index f2766e3..542fdcd 100644 --- a/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/SetupConfigFragment.kt +++ b/app/src/main/java/fr/uca/iut/clfreville2/teaiswarm/fragment/SetupConfigFragment.kt @@ -25,10 +25,11 @@ class SetupConfigFragment(private val preferences: SharedPreferences) : Fragment super.onViewCreated(view, savedInstanceState) usernameInput = view.findViewById(R.id.username_input) confirmButton = view.findViewById(R.id.configure_button) + val service = (activity?.application as TeaIsWarm).service confirmButton.setOnClickListener { val username = usernameInput.text.toString() lifecycleScope.launch { - val owner = TeaIsWarm.service.searchOwner(username) + val owner = service.searchOwner(username) if (owner is Owner) { preferences.edit { putString(USERNAME, usernameInput.text.toString()) diff --git a/app/src/main/res/layout/comment_list_fragment.xml b/app/src/main/res/layout/comment_list_fragment.xml new file mode 100644 index 0000000..da254e6 --- /dev/null +++ b/app/src/main/res/layout/comment_list_fragment.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/comment_view_item.xml b/app/src/main/res/layout/comment_view_item.xml new file mode 100644 index 0000000..0601527 --- /dev/null +++ b/app/src/main/res/layout/comment_view_item.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/repository_details.xml b/app/src/main/res/layout/repository_details.xml index 8bdbe18..d67eb05 100644 --- a/app/src/main/res/layout/repository_details.xml +++ b/app/src/main/res/layout/repository_details.xml @@ -120,4 +120,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layoutManager="LinearLayoutManager"/> + + diff --git a/build.gradle b/build.gradle index 61ca049..99673a9 100644 --- a/build.gradle +++ b/build.gradle @@ -2,5 +2,5 @@ plugins { id 'com.android.application' 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 } \ No newline at end of file