parent
1f85bf2f71
commit
8f503b64a4
@ -1,45 +1,57 @@
|
|||||||
package fr.uca.iut.clfreville2.teaiswarm
|
package fr.uca.iut.clfreville2.teaiswarm
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
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.network.GiteaService
|
import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
const val FILE_PATH = "file_path"
|
||||||
|
|
||||||
class RepositoryDetailActivity : AppCompatActivity() {
|
class RepositoryDetailActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val service = GiteaService()
|
private val service = GiteaService()
|
||||||
private lateinit var repositoryName: TextView
|
private lateinit var repositoryName: TextView
|
||||||
private lateinit var versionedFiles: RecyclerView
|
private lateinit var versionedFiles: RecyclerView
|
||||||
|
|
||||||
|
private lateinit var currentRepositoryOwner: String
|
||||||
|
private lateinit var currentRepositoryName: String
|
||||||
|
private var currentFilePath: String? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_repository_detail)
|
setContentView(R.layout.activity_repository_detail)
|
||||||
|
|
||||||
repositoryName = findViewById(R.id.repository_detail_name)
|
repositoryName = findViewById(R.id.repository_detail_name)
|
||||||
|
|
||||||
var currentRepositoryOwner: String? = null
|
|
||||||
var currentRepositoryName: String? = null
|
|
||||||
val bundle: Bundle? = intent.extras
|
val bundle: Bundle? = intent.extras
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
currentRepositoryOwner = bundle.getString(REPOSITORY_OWNER)
|
currentRepositoryOwner = bundle.getString(REPOSITORY_OWNER)!!
|
||||||
currentRepositoryName = bundle.getString(REPOSITORY_NAME)
|
currentRepositoryName = bundle.getString(REPOSITORY_NAME)!!
|
||||||
|
currentFilePath = bundle.getString(FILE_PATH)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRepositoryName?.let {
|
repositoryName.text = currentRepositoryName
|
||||||
repositoryName.text = currentRepositoryName
|
|
||||||
}
|
|
||||||
|
|
||||||
versionedFiles = findViewById(R.id.versioned_files_view)
|
versionedFiles = findViewById(R.id.versioned_files_view)
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch {
|
||||||
val repos = service.listFileContents(RepositoryIdentifier(currentRepositoryOwner!!, currentRepositoryName!!), "")
|
val repos = service.listFileContents(RepositoryIdentifier(currentRepositoryOwner, currentRepositoryName), currentFilePath ?: "")
|
||||||
lifecycleScope.launch {
|
versionedFiles.adapter = FileListAdapter(repos) {
|
||||||
versionedFiles.adapter = FileListAdapter(repos) {}
|
file -> adapterOnClick(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun adapterOnClick(file: VersionedFile) {
|
||||||
|
val intent = Intent(this, RepositoryDetailActivity()::class.java)
|
||||||
|
intent.putExtra(REPOSITORY_OWNER, currentRepositoryOwner)
|
||||||
|
intent.putExtra(REPOSITORY_NAME, currentRepositoryName)
|
||||||
|
intent.putExtra(FILE_PATH, (currentFilePath ?: "") + file.name)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,149 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.fragment
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.paging.*
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.R
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.CommitActivity
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class ActivityListFragment : Fragment(R.layout.activity_list) {
|
||||||
|
|
||||||
|
private val service = GiteaService()
|
||||||
|
var repository: RepositoryIdentifier? = null
|
||||||
|
var sha: String? = null
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
updateCommits()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCommits() {
|
||||||
|
val viewModel by viewModels<ActivityViewModel>(
|
||||||
|
factoryProducer = {
|
||||||
|
ActivityViewModelFactory(
|
||||||
|
service,
|
||||||
|
repository!!,
|
||||||
|
sha
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val pagingAdapter = ActivityAdapter(ActivityComparator)
|
||||||
|
val recyclerView = requireView().findViewById<RecyclerView>(R.id.activity_list_view)
|
||||||
|
recyclerView.adapter = pagingAdapter
|
||||||
|
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.flow.collectLatest { pagingData ->
|
||||||
|
pagingAdapter.submitData(pagingData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivitySource(
|
||||||
|
private val service: RepositoryService,
|
||||||
|
private val repository: RepositoryIdentifier,
|
||||||
|
private val sha: String?
|
||||||
|
) : PagingSource<Int, CommitActivity>() {
|
||||||
|
|
||||||
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CommitActivity> =
|
||||||
|
try {
|
||||||
|
val nextPageNumber = params.key ?: 1
|
||||||
|
val response = service.listCommits(repository, sha, nextPageNumber)
|
||||||
|
LoadResult.Page(
|
||||||
|
data = response,
|
||||||
|
prevKey = nextPageNumber - 1,
|
||||||
|
nextKey = nextPageNumber + 1
|
||||||
|
)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
LoadResult.Error(e)
|
||||||
|
} catch (e: HttpException) {
|
||||||
|
LoadResult.Error(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, CommitActivity>): Int? =
|
||||||
|
state.anchorPosition?.let { anchorPosition ->
|
||||||
|
val anchorPage = state.closestPageToPosition(anchorPosition)
|
||||||
|
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivityAdapter(diffCallback: DiffUtil.ItemCallback<CommitActivity>) :
|
||||||
|
PagingDataAdapter<CommitActivity, ViewHolder>(diffCallback) {
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int
|
||||||
|
): ViewHolder {
|
||||||
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.activity_row_item, parent, false)
|
||||||
|
return ViewHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
|
||||||
|
holder.bind(getItem(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
private val commitNameView: TextView
|
||||||
|
|
||||||
|
init {
|
||||||
|
commitNameView = view.findViewById(R.id.commit_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(commit: CommitActivity?) {
|
||||||
|
commit?.let {
|
||||||
|
commitNameView.text = it.commit.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ActivityComparator : DiffUtil.ItemCallback<CommitActivity>() {
|
||||||
|
override fun areItemsTheSame(oldItem: CommitActivity, newItem: CommitActivity): Boolean =
|
||||||
|
oldItem.sha == newItem.sha
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: CommitActivity, newItem: CommitActivity): Boolean =
|
||||||
|
oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivityViewModel(
|
||||||
|
private val service: RepositoryService,
|
||||||
|
private val repository: RepositoryIdentifier,
|
||||||
|
private val sha: String?
|
||||||
|
) : ViewModel() {
|
||||||
|
val flow = Pager(
|
||||||
|
PagingConfig(pageSize = 10, enablePlaceholders = true)
|
||||||
|
) {
|
||||||
|
ActivitySource(service, repository, sha)
|
||||||
|
}.flow
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivityViewModelFactory(
|
||||||
|
private val service: RepositoryService,
|
||||||
|
private val repository: RepositoryIdentifier,
|
||||||
|
private val sha: String?
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
if (modelClass.isAssignableFrom(ActivityViewModel::class.java)) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return ActivityViewModel(service, repository, sha) as T
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("Unknown ViewModel class")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
data class Author(val name: String, val email: String, val date: Date)
|
@ -0,0 +1,3 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
|
data class CommitActivity(val sha: String, val commit: Commit, val author: Owner?, val committer: Owner?)
|
@ -0,0 +1,14 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
enum class FileType {
|
||||||
|
@Json(name = "file")
|
||||||
|
FILE,
|
||||||
|
@Json(name = "dir")
|
||||||
|
DIR,
|
||||||
|
@Json(name = "symlink")
|
||||||
|
SYMLINK,
|
||||||
|
@Json(name = "submodule")
|
||||||
|
SUBMODULE
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
|
data class Commit(val author: Author, val committer: Author, val message: String)
|
@ -0,0 +1,3 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
|
data class Verification(val verified: Boolean)
|
@ -1,3 +1,3 @@
|
|||||||
package fr.uca.iut.clfreville2.teaiswarm.model
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
data class VersionedFile(val name: String)
|
data class VersionedFile(val name: String, val type: FileType)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package fr.uca.iut.clfreville2.teaiswarm.network
|
package fr.uca.iut.clfreville2.teaiswarm.network
|
||||||
|
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.Repository
|
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifiable
|
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile
|
|
||||||
|
|
||||||
interface RepositoryService {
|
interface RepositoryService {
|
||||||
|
|
||||||
suspend fun listActiveRepositories(username: String, page: Int): List<Repository>
|
suspend fun listActiveRepositories(username: String, page: Int): List<Repository>
|
||||||
|
|
||||||
|
suspend fun listCommits(repository: RepositoryIdentifiable, sha: String?, page: Int): List<CommitActivity>
|
||||||
|
|
||||||
suspend fun listFileContents(repository: RepositoryIdentifiable, filePath: String): List<VersionedFile>
|
suspend fun listFileContents(repository: RepositoryIdentifiable, filePath: String): List<VersionedFile>
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/activity_list_view" />
|
||||||
|
</FrameLayout>
|
@ -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">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/commit_name" />
|
||||||
|
</LinearLayout>
|
Loading…
Reference in new issue