diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5d7a6cb..a63b20c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,7 +51,6 @@ dependencies { implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.auth0.android:jwtdecode:2.0.1") - implementation(libs.androidx.recyclerview) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9bd5106..a595186 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,32 +1,26 @@ + xmlns:tools="http://schemas.android.com/tools" > + android:allowBackup="true" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/backup_rules" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/Theme.KanbanCloneAndroid" + android:usesCleartextTraffic="true" > + android:name="com.campusaula.edbole.kanban_clone_android.LoginActivity" + android:exported="false" /> - - + android:name="com.campusaula.edbole.kanban_clone_android.MainActivity" + android:exported="true" > diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/LoginActivity.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/LoginActivity.kt similarity index 69% rename from app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/LoginActivity.kt rename to app/src/main/java/com/campusaula/edbole/kanban_clone_android/LoginActivity.kt index 8b08d11..3c4ca83 100644 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/LoginActivity.kt +++ b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/LoginActivity.kt @@ -1,31 +1,25 @@ -package com.campusaula.edbole.kanban_clone_android.ui +package com.campusaula.edbole.kanban_clone_android -import android.content.Intent import android.os.Bundle -import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.AppCompatButton -import androidx.appcompat.widget.AppCompatEditText import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.lifecycleScope -import com.campusaula.edbole.kanban_clone_android.R import com.campusaula.edbole.kanban_clone_android.network.ApiService import com.campusaula.edbole.kanban_clone_android.network.RetrofitInstance import com.campusaula.edbole.kanban_clone_android.kanban.ErrorResponse -import com.campusaula.edbole.kanban_clone_android.kanban.UserLogin import com.google.gson.Gson import kotlinx.coroutines.launch import retrofit2.Retrofit class LoginActivity : AppCompatActivity() { - private lateinit var emailInput : AppCompatEditText - private lateinit var passwordInput : AppCompatEditText + private lateinit var emailInput : androidx.appcompat.widget.AppCompatEditText + private lateinit var passwordInput : androidx.appcompat.widget.AppCompatEditText - private lateinit var loginButton : AppCompatButton - private lateinit var logonButton : AppCompatButton + private lateinit var loginButton : androidx.appcompat.widget.AppCompatButton + private lateinit var logonButton : androidx.appcompat.widget.AppCompatButton private lateinit var retrofit : Retrofit private lateinit var api : ApiService @@ -53,14 +47,14 @@ class LoginActivity : AppCompatActivity() { val password = passwordInput.text.toString() if (email.isEmpty() && password.isEmpty()) { - Toast.makeText(this, "Please enter email and password", Toast.LENGTH_SHORT).show() + android.widget.Toast.makeText(this, "Please enter email and password", android.widget.Toast.LENGTH_SHORT).show() return@setOnClickListener } lifecycleScope.launch{ try { val loginResponse = api.login( - UserLogin( + com.campusaula.edbole.kanban_clone_android.kanban.UserLogin( email = email, password = password ) @@ -73,14 +67,10 @@ class LoginActivity : AppCompatActivity() { // Después del login exitoso OkHttp/CookieJar habrá guardado las cookies. val authValue = RetrofitInstance.getAuthCookieForUrl(baseUrl) if (authValue != null) { - Toast.makeText(this@LoginActivity, "Auth cookie guardada", Toast.LENGTH_SHORT).show() + android.widget.Toast.makeText(this@LoginActivity, "Auth cookie guardada", android.widget.Toast.LENGTH_SHORT).show() } else { - Toast.makeText(this@LoginActivity, "Login OK pero no se encontró cookie de auth", Toast.LENGTH_SHORT).show() + android.widget.Toast.makeText(this@LoginActivity, "Login OK pero no se encontró cookie de auth", android.widget.Toast.LENGTH_SHORT).show() } - // Navegar a MainActivity - val intent = Intent(this@LoginActivity, MainActivity::class.java) - startActivity(intent) - finish() } else { if (loginResponse.code() == 401) { // parse error body if possible @@ -95,14 +85,14 @@ class LoginActivity : AppCompatActivity() { // clear stored cookies for base host RetrofitInstance.clearCookiesForHost(baseHost) - Toast.makeText(this@LoginActivity, "Login failed (401): $errMsg", Toast.LENGTH_SHORT).show() + android.widget.Toast.makeText(this@LoginActivity, "Login failed (401): $errMsg", android.widget.Toast.LENGTH_SHORT).show() } else { - Toast.makeText(this@LoginActivity, "Login failed: ${loginResponse.code()}", Toast.LENGTH_SHORT).show() + android.widget.Toast.makeText(this@LoginActivity, "Login failed: ${loginResponse.code()}", android.widget.Toast.LENGTH_SHORT).show() } } } catch (ex: Exception){ - Toast.makeText(this@LoginActivity, "Login failed: ${ex.message}", Toast.LENGTH_SHORT).show() + android.widget.Toast.makeText(this@LoginActivity, "Login failed: ${ex.message}", android.widget.Toast.LENGTH_SHORT).show() } } diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/CreateProjectActivity.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/MainActivity.kt similarity index 74% rename from app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/CreateProjectActivity.kt rename to app/src/main/java/com/campusaula/edbole/kanban_clone_android/MainActivity.kt index c516c8d..87828e1 100644 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/CreateProjectActivity.kt +++ b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/MainActivity.kt @@ -1,17 +1,16 @@ -package com.campusaula.edbole.kanban_clone_android.ui +package com.campusaula.edbole.kanban_clone_android import android.os.Bundle import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat -import com.campusaula.edbole.kanban_clone_android.R -class CreateProjectActivity : AppCompatActivity() { +class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_create_project) + setContentView(R.layout.activity_main) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/kanban/Project.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/kanban/Project.kt index 70c8c21..f0a25b8 100644 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/kanban/Project.kt +++ b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/kanban/Project.kt @@ -8,11 +8,6 @@ class Project{ val description: String = "" val users: List = emptyList() val tasks: List = emptyList() - - - override fun toString(): String { - return "Project(id=$id, name='$name', description='$description', users=$users, tasks=$tasks)" - } } data class ProjectBase( diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/network/ApiService.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/network/ApiService.kt index 6d9184b..e79377c 100644 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/network/ApiService.kt +++ b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/network/ApiService.kt @@ -11,7 +11,7 @@ interface ApiService { @POST("auth/login/") suspend fun login(@Body userLogin: UserLogin): Response - @GET("me/logout/") + @POST("me/logout/") suspend fun logout(): Response @DELETE("me/delete-me/") diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/MainActivity.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/MainActivity.kt deleted file mode 100644 index fe71980..0000000 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/MainActivity.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.campusaula.edbole.kanban_clone_android.ui - -import android.content.Intent -import android.os.Bundle -import android.widget.Button -import android.widget.TextView -import android.widget.Toast -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.RecyclerView -import com.campusaula.edbole.kanban_clone_android.R -import com.campusaula.edbole.kanban_clone_android.kanban.Project -import com.campusaula.edbole.kanban_clone_android.network.ApiService -import com.campusaula.edbole.kanban_clone_android.network.RetrofitInstance -import com.google.android.material.floatingactionbutton.FloatingActionButton -import kotlinx.coroutines.launch - -class MainActivity : AppCompatActivity() { - - private lateinit var api: ApiService - private lateinit var projectList : List - - private lateinit var loggedInAs: TextView - private lateinit var logoutButton: Button - private lateinit var addProjectActionButton: FloatingActionButton - private lateinit var projectsRecyclerView: RecyclerView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } - - api = RetrofitInstance.getRetrofit(applicationContext).create(ApiService::class.java) - projectList = emptyList() - - /* Activity components */ - loggedInAs = findViewById(R.id.loggedInAs) - logoutButton = findViewById(R.id.logoutButton) - addProjectActionButton = findViewById(R.id.addProjectActionButton) - projectsRecyclerView = findViewById(R.id.projectsRecyclerView) - projectsRecyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this) - val adapter = ProjectItemAdapter(projectList) { project -> - val intent = Intent(this, ProjectDetailActivity::class.java) - intent.putExtra("project_id", project.id) - startActivity(intent) - } - projectsRecyclerView.adapter = adapter - - addProjectActionButton.setOnClickListener { - val intent = Intent(this, CreateProjectActivity::class.java) - startActivity(intent) - } - - /* Getting the logged-in user info */ - lifecycleScope.launch{ - - val getMe = api.getMe() - if (getMe.isSuccessful){ - val user = getMe.body() - loggedInAs.text = "Logged in as: ${user?.name}" - projectList = api.getAllProjects().body()!! - adapter.submitList(projectList) - } else { - val intent = Intent(this@MainActivity, LoginActivity::class.java) - startActivity(intent) - } - - } - - logoutButton.setOnClickListener { - lifecycleScope.launch { - val logoutResponse = api.logout() - if (logoutResponse.isSuccessful) { - // Clear cookies for the API host - RetrofitInstance.clearCookiesForHost( - "10.0.2.2:8000" - ) - // Navigate back to the login screen - val intent = - Intent(this@MainActivity, LoginActivity::class.java) - startActivity(intent) - finish() // Optional: close the MainActivity so it's removed from the back stack - } else { - Toast.makeText( - this@MainActivity, - "Logout failed", - Toast.LENGTH_SHORT - ).show() - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/ProjectDetailActivity.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/ProjectDetailActivity.kt deleted file mode 100644 index 5790ade..0000000 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/ProjectDetailActivity.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.campusaula.edbole.kanban_clone_android.ui - -import android.health.connect.datatypes.units.Percentage -import android.os.Bundle -import android.util.Log -import android.widget.TextView -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.lifecycle.lifecycleScope -import com.campusaula.edbole.kanban_clone_android.R -import com.campusaula.edbole.kanban_clone_android.kanban.Project -import com.campusaula.edbole.kanban_clone_android.kanban.Task -import com.campusaula.edbole.kanban_clone_android.kanban.TaskStatus -import com.campusaula.edbole.kanban_clone_android.network.ApiService -import com.campusaula.edbole.kanban_clone_android.network.RetrofitInstance -import com.google.android.material.floatingactionbutton.FloatingActionButton -import kotlinx.coroutines.launch - -class ProjectDetailActivity : AppCompatActivity() { - - private lateinit var api: ApiService - - private lateinit var projectTitleText : TextView - private lateinit var projectDescriptionText : TextView - private lateinit var completedPercentageText: TextView - private lateinit var returnActionButton: FloatingActionButton - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_project_detail) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } - api = RetrofitInstance.getRetrofit(applicationContext).create(ApiService::class.java) - projectTitleText = findViewById(R.id.projectTitleText) - projectDescriptionText = findViewById(R.id.projectDescriptionText) - completedPercentageText = findViewById(R.id.completedPercentageText) - returnActionButton = findViewById(R.id.returnActionButton) - returnActionButton.setOnClickListener { finish() } - - val projectId : Int = intent.getIntExtra("project_id", -1) - - if (projectId > 0) { - Log.d("ProjectDetailActivity", "Received project ID: $projectId") - lifecycleScope.launch { - try { - val projectResponse = api.getProjectById(projectId) - - if (projectResponse.isSuccessful && projectResponse.body() != null) { - Log.d("ProjectDetailActivity", "Fetched project: ${projectResponse.body()!!.name}") - val project = projectResponse.body()!! - - - Log.d("ProjectDetailActivity", "Displaying project details for: $project") - - projectTitleText.text = project.name - projectDescriptionText.text = project.description - - var percentageFinished = 0.0; - val tasks: List = project.tasks - val totalTasks: Int = tasks.size - val perTaskPercentage = if (totalTasks > 0) (1.0 / totalTasks)*100 else 0.0 - - for (task in tasks) { - if (task.status == TaskStatus.COMPLETED) { - percentageFinished += perTaskPercentage - } - } - completedPercentageText.text = "Completed: ${"%.2f".format(percentageFinished * 100)}%" - - - } else { - Log.e("ProjectDetailActivity", "Failed to fetch project: ${projectResponse.code()} - ${projectResponse.message()}") - finish() - } - } catch (e: Exception) { - Log.e("ProjectDetailActivity", "Error fetching project", e) - finish() - } - } - } else { - Log.e("ProjectDetailActivity", "No project ID found in intent") - finish() - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/ProjectItemAdapter.kt b/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/ProjectItemAdapter.kt deleted file mode 100644 index 53856b0..0000000 --- a/app/src/main/java/com/campusaula/edbole/kanban_clone_android/ui/ProjectItemAdapter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.campusaula.edbole.kanban_clone_android.ui - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.campusaula.edbole.kanban_clone_android.R -import com.campusaula.edbole.kanban_clone_android.kanban.Project - -class ProjectItemAdapter( - private var items: List, - private val onItemClick: ((Project) -> Unit)? = null -) : RecyclerView.Adapter() { - - fun submitList(newList: List) { - items = newList - notifyDataSetChanged() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_project, parent, false) - return ViewHolder(view) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val project = items[position] - holder.bind(project) - holder.itemView.setOnClickListener { onItemClick?.invoke(project) } - } - - override fun getItemCount(): Int = items.size - - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val nameTv: TextView = itemView.findViewById(R.id.projectName) - private val descTv: TextView = itemView.findViewById(R.id.projectDescription) - - fun bind(project: Project) { - nameTv.text = project.id.toString() + " " + project.name - descTv.text = project.description - } - } -} diff --git a/app/src/main/res/layout/activity_create_project.xml b/app/src/main/res/layout/activity_create_project.xml deleted file mode 100644 index 68de28d..0000000 --- a/app/src/main/res/layout/activity_create_project.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index ac739f4..4330ddd 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -5,7 +5,7 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="com.campusaula.edbole.kanban_clone_android.ui.LoginActivity"> + tools:context="com.campusaula.edbole.kanban_clone_android.LoginActivity"> + tools:context="com.campusaula.edbole.kanban_clone_android.MainActivity"> - -