Check user validity and add a code view fragment

main
Clément FRÉVILLE 2 years ago
parent 6687ce099d
commit 3b47bb7e7f

@ -0,0 +1,11 @@
package fr.uca.iut.clfreville2.teaiswarm
import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
object TeaIsWarm {
val service: RepositoryService by lazy {
GiteaService()
}
}

@ -17,9 +17,9 @@ import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_NAME import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_NAME
import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_OWNER import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_OWNER
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
import fr.uca.iut.clfreville2.teaiswarm.model.CommitActivity import fr.uca.iut.clfreville2.teaiswarm.model.CommitActivity
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier 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 fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -28,7 +28,7 @@ import java.io.IOException
class ActivityListFragment : Fragment(R.layout.activity_list) { class ActivityListFragment : Fragment(R.layout.activity_list) {
private val service = GiteaService() private val service = TeaIsWarm.service
var repository: RepositoryIdentifier? = null var repository: RepositoryIdentifier? = null
var sha: String? = null var sha: String? = null

@ -0,0 +1,33 @@
package fr.uca.iut.clfreville2.teaiswarm.fragment
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
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.model.RepositoryIdentifier
import kotlinx.coroutines.launch
class CodeViewFragment : Fragment(R.layout.code_view_fragment) {
private lateinit var content: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val service = TeaIsWarm.service
val bundle = requireArguments()
val repository = RepositoryIdentifier(
bundle.getString(REPOSITORY_OWNER)!!,
bundle.getString(REPOSITORY_NAME)!!
)
content = view.findViewById(R.id.code_content_view)
viewLifecycleOwner.lifecycleScope.launch {
val contents = service.retrieveFileContents(repository, bundle.getString(FILE_PATH)!!)
content.text = contents.content
}
}
}

@ -10,16 +10,16 @@ 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 fr.uca.iut.clfreville2.teaiswarm.* import fr.uca.iut.clfreville2.teaiswarm.*
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.GiteaService
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
const val FILE_PATH = "file_path" const val FILE_PATH = "file_path"
class RepositoryDetailsFragment : Fragment(R.layout.repository_details) { class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
private val service = GiteaService() private val service = TeaIsWarm.service
private lateinit var repositoryName: TextView private lateinit var repositoryName: TextView
private lateinit var repositoryDescription: TextView private lateinit var repositoryDescription: TextView
private lateinit var activity: Button private lateinit var activity: Button
@ -61,6 +61,14 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
REPOSITORY_NAME to currentRepositoryName, REPOSITORY_NAME to currentRepositoryName,
FILE_PATH to (currentFilePath ?: "") + file.name FILE_PATH to (currentFilePath ?: "") + file.name
) )
findNavController().navigate(R.id.repository_details_fragment, bundle) when (file.type) {
FileType.FILE -> {
findNavController().navigate(R.id.code_view_fragment, bundle)
}
FileType.DIR -> {
findNavController().navigate(R.id.repository_details_fragment, bundle)
}
else -> {}
}
} }
} }

@ -12,9 +12,9 @@ import androidx.paging.*
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
import fr.uca.iut.clfreville2.teaiswarm.adapter.RepositoryListAdapter import fr.uca.iut.clfreville2.teaiswarm.adapter.RepositoryListAdapter
import fr.uca.iut.clfreville2.teaiswarm.model.Repository import fr.uca.iut.clfreville2.teaiswarm.model.Repository
import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -23,7 +23,7 @@ import java.io.IOException
class RepositoryListFragment(private val username: String, private val onClick: (Repository) -> Unit) : Fragment(R.layout.repository_list) { class RepositoryListFragment(private val username: String, private val onClick: (Repository) -> Unit) : Fragment(R.layout.repository_list) {
private val service = GiteaService() private val service = TeaIsWarm.service
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

@ -5,11 +5,16 @@ import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Toast
import androidx.core.content.edit import androidx.core.content.edit
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import fr.uca.iut.clfreville2.teaiswarm.R import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
import fr.uca.iut.clfreville2.teaiswarm.USERNAME import fr.uca.iut.clfreville2.teaiswarm.USERNAME
import fr.uca.iut.clfreville2.teaiswarm.model.Owner
import kotlinx.coroutines.launch
class SetupConfigFragment(private val preferences: SharedPreferences) : Fragment(R.layout.setup_config) { class SetupConfigFragment(private val preferences: SharedPreferences) : Fragment(R.layout.setup_config) {
@ -21,10 +26,22 @@ class SetupConfigFragment(private val preferences: SharedPreferences) : Fragment
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)
confirmButton.setOnClickListener { confirmButton.setOnClickListener {
preferences.edit { val username = usernameInput.text.toString()
putString(USERNAME, usernameInput.text.toString()) lifecycleScope.launch {
val owner = TeaIsWarm.service.searchOwner(username)
if (owner is Owner) {
preferences.edit {
putString(USERNAME, usernameInput.text.toString())
}
view.findNavController().navigate(R.id.repository_list_fragment)
} else {
Toast.makeText(
view.context,
resources.getText(R.string.owner_not_found),
Toast.LENGTH_SHORT
).show()
}
} }
view.findNavController().navigate(R.id.repository_list_fragment)
} }
} }
} }

@ -0,0 +1,3 @@
package fr.uca.iut.clfreville2.teaiswarm.model
data class FileContent(val type: FileType, val size: Int, val content: String, val last_commit_sha: String)

@ -8,6 +8,7 @@ import fr.uca.iut.clfreville2.teaiswarm.model.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import retrofit2.HttpException
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET import retrofit2.http.GET
@ -15,6 +16,8 @@ import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
import java.util.* import java.util.*
const val HTTP_NOT_FOUND = 404;
interface GiteaApiService { interface GiteaApiService {
@GET("users/{username}/repos") @GET("users/{username}/repos")
@ -25,6 +28,12 @@ interface GiteaApiService {
@GET("repos/{owner}/{repo}/contents/{filePath}") @GET("repos/{owner}/{repo}/contents/{filePath}")
suspend fun listFileContents(@Path("owner") owner: String, @Path("repo") repo: String, @Path("filePath") filePath: String): List<VersionedFile> suspend fun listFileContents(@Path("owner") owner: String, @Path("repo") repo: String, @Path("filePath") filePath: String): List<VersionedFile>
@GET("repos/{owner}/{repo}/contents/{filePath}")
suspend fun retrieveFileContents(@Path("owner") owner: String, @Path("repo") repo: String, @Path("filePath") filePath: String): FileContent
@GET("users/{owner}")
suspend fun searchOwner(@Path("owner") owner: String): Owner?
} }
class GiteaService(private val handle: GiteaApiService) : RepositoryService { class GiteaService(private val handle: GiteaApiService) : RepositoryService {
@ -54,6 +63,23 @@ class GiteaService(private val handle: GiteaApiService) : RepositoryService {
override suspend fun listFileContents(repository: RepositoryIdentifiable, filePath: String): List<VersionedFile> = withContext(Dispatchers.IO) { override suspend fun listFileContents(repository: RepositoryIdentifiable, filePath: String): List<VersionedFile> = withContext(Dispatchers.IO) {
handle.listFileContents(repository.identifier.owner, repository.identifier.name, filePath) handle.listFileContents(repository.identifier.owner, repository.identifier.name, filePath)
} }
override suspend fun retrieveFileContents(
repository: RepositoryIdentifiable,
filePath: String
): FileContent = withContext(Dispatchers.IO) {
handle.retrieveFileContents(repository.identifier.owner, repository.identifier.name, filePath)
}
override suspend fun searchOwner(owner: String): Owner? = try {
handle.searchOwner(owner)
} catch (ex: HttpException) {
if (ex.code() == HTTP_NOT_FOUND) {
null
} else {
throw ex
}
}
} }
private const val CODEFIRST_API_BASE = "https://codefirst.iut.uca.fr/git/api/v1/" private const val CODEFIRST_API_BASE = "https://codefirst.iut.uca.fr/git/api/v1/"

@ -9,4 +9,8 @@ interface RepositoryService {
suspend fun listCommits(repository: RepositoryIdentifiable, sha: String?, page: Int): List<CommitActivity> 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>
suspend fun retrieveFileContents(repository: RepositoryIdentifiable, filePath: String): FileContent
suspend fun searchOwner(owner: String): Owner?
} }

@ -55,6 +55,20 @@ class StubRepositoryService : RepositoryService {
"CONVENTIONS.md", "CONVENTIONS.md",
"README.md" "README.md"
).map { VersionedFile(it, if (it.contains(".")) { FileType.FILE } else { FileType.DIR }) } ).map { VersionedFile(it, if (it.contains(".")) { FileType.FILE } else { FileType.DIR }) }
// See KT-2425
override suspend fun retrieveFileContents(
repository: RepositoryIdentifiable,
filePath: String
) = FileContent(
FileType.FILE, 82, """
#!/bin/bash
container_id=$(docker run --detach nginx)
docker stop ${'$'}container_id""", randomCommitSha()
)
override suspend fun searchOwner(owner: String): Owner =
Owner(1, owner)
} }
val CHAR_POOL = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray(); val CHAR_POOL = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();

@ -0,0 +1,11 @@
<?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:id="@+id/code_content_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

@ -4,20 +4,27 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<EditText <LinearLayout
android:id="@+id/username_input"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/configure_button" android:orientation="vertical"
android:autofillHints="" />
<Button
android:id="@+id/configure_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/configure"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<EditText
android:id="@+id/username_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints=""
app:layout_constraintBottom_toTopOf="@+id/configure_button" />
<Button
android:id="@+id/configure_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/configure" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -28,4 +28,8 @@
android:id="@+id/repository_details_fragment" android:id="@+id/repository_details_fragment"
android:name="fr.uca.iut.clfreville2.teaiswarm.fragment.RepositoryDetailsFragment" android:name="fr.uca.iut.clfreville2.teaiswarm.fragment.RepositoryDetailsFragment"
android:label="RepositoryDetailsFragment" /> android:label="RepositoryDetailsFragment" />
<fragment
android:id="@+id/code_view_fragment"
android:name="fr.uca.iut.clfreville2.teaiswarm.fragment.CodeViewFragment"
android:label="CodeViewFragment" />
</navigation> </navigation>

@ -14,4 +14,5 @@
<string name="username">Username</string> <string name="username">Username</string>
<string name="configure">Configure</string> <string name="configure">Configure</string>
<string name="activity">Activity</string> <string name="activity">Activity</string>
<string name="owner_not_found">User not found</string>
</resources> </resources>
Loading…
Cancel
Save