App con inicio y base de datos pero sin direccion
This commit is contained in:
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hackathon-gituber-4fbb7edead2842bd8517a7b78b908771" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,22 +1,18 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
id("com.google.gms.google-services")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.basurapp"
|
||||
compileSdk {
|
||||
version = release(36) {
|
||||
minorApiLevel = 1
|
||||
}
|
||||
}
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.basurapp"
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -29,19 +25,38 @@ android {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.androidx.activity)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
// AndroidX (versiones compatibles con SDK 34)
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.11.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.activity:activity-ktx:1.8.2")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||
|
||||
// Firebase
|
||||
implementation(platform("com.google.firebase:firebase-bom:32.7.4"))
|
||||
implementation("com.google.firebase:firebase-auth-ktx")
|
||||
implementation("com.google.firebase:firebase-firestore-ktx")
|
||||
|
||||
// Coroutines
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
}
|
||||
29
app/google-services.json
Normal file
29
app/google-services.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "168486816372",
|
||||
"project_id": "hackon-4abe2",
|
||||
"storage_bucket": "hackon-4abe2.firebasestorage.app"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:168486816372:android:aff8739eafd01d6e025f21",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.basurapp"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyB-UnjmxI61A4V4VxsJ1ukIYt0idhfocu8"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@@ -10,9 +13,13 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.BasurApp">
|
||||
android:theme="@style/Theme.BasurApp"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".RegisterActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -20,6 +27,9 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
88
app/src/main/java/com/example/basurapp/LoginActivity.kt
Normal file
88
app/src/main/java/com/example/basurapp/LoginActivity.kt
Normal file
@@ -0,0 +1,88 @@
|
||||
package com.example.basurapp
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Patterns
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.example.basurapp.databinding.ActivityLoginBinding
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
private val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance() }
|
||||
private val db: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
// Padding para system bars
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->
|
||||
val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
// Si ya hay sesión activa, ir directo al Main
|
||||
if (auth.currentUser != null) {
|
||||
goToMain()
|
||||
return
|
||||
}
|
||||
|
||||
binding.tvCreateAccount.setOnClickListener {
|
||||
startActivity(Intent(this, RegisterActivity::class.java))
|
||||
}
|
||||
|
||||
binding.btnEnter.setOnClickListener {
|
||||
attemptLogin()
|
||||
}
|
||||
}
|
||||
|
||||
private fun attemptLogin() {
|
||||
val email = binding.etEmail.text?.toString()?.trim().orEmpty()
|
||||
val password = binding.etPassword.text?.toString().orEmpty()
|
||||
|
||||
// Validaciones
|
||||
if (email.isEmpty() || password.isEmpty()) {
|
||||
toast(getString(R.string.error_empty_fields))
|
||||
return
|
||||
}
|
||||
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||
binding.tilEmail.error = getString(R.string.error_invalid_email)
|
||||
return
|
||||
}
|
||||
binding.tilEmail.error = null
|
||||
|
||||
setLoading(true)
|
||||
auth.signInWithEmailAndPassword(email, password)
|
||||
.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
goToMain()
|
||||
} else {
|
||||
setLoading(false)
|
||||
toast(task.exception?.localizedMessage ?: "Error al iniciar sesión")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToMain() {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun setLoading(loading: Boolean) {
|
||||
binding.progressBar.visibility = if (loading) View.VISIBLE else View.GONE
|
||||
binding.btnEnter.isEnabled = !loading
|
||||
}
|
||||
|
||||
private fun toast(msg: String) {
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,136 @@
|
||||
package com.example.basurapp
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import android.view.MenuItem
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.basurapp.databinding.ActivityMainBinding
|
||||
import com.example.basurapp.fragments.ControlFragment
|
||||
import com.example.basurapp.fragments.HomeFragment
|
||||
import com.example.basurapp.fragments.InfoDesperdiciosFragment
|
||||
import com.example.basurapp.fragments.ReportarFragment
|
||||
import com.example.basurapp.models.User
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
|
||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var toggle: ActionBarDrawerToggle
|
||||
|
||||
private val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance() }
|
||||
private val db: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
|
||||
|
||||
private var currentUser: User? = null
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
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
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.title = ""
|
||||
|
||||
// Botón hamburguesa
|
||||
toggle = ActionBarDrawerToggle(
|
||||
this,
|
||||
binding.drawerLayout,
|
||||
binding.toolbar,
|
||||
R.string.drawer_open,
|
||||
R.string.drawer_close
|
||||
)
|
||||
binding.drawerLayout.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
|
||||
binding.navigationView.setNavigationItemSelectedListener(this)
|
||||
|
||||
loadCurrentUser()
|
||||
}
|
||||
|
||||
private fun loadCurrentUser() {
|
||||
val uid = auth.currentUser?.uid
|
||||
if (uid == null) {
|
||||
goToLogin()
|
||||
return
|
||||
}
|
||||
db.collection("users").document(uid).get()
|
||||
.addOnSuccessListener { doc ->
|
||||
val user = doc.toObject(User::class.java)
|
||||
if (user == null) {
|
||||
goToLogin()
|
||||
return@addOnSuccessListener
|
||||
}
|
||||
currentUser = user
|
||||
setupForRole(user)
|
||||
}
|
||||
.addOnFailureListener {
|
||||
goToLogin()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupForRole(user: User) {
|
||||
binding.navigationView.menu.clear()
|
||||
if (user.role == User.ROLE_WORKER) {
|
||||
binding.navigationView.inflateMenu(R.menu.drawer_menu_worker)
|
||||
loadFragment(ControlFragment.newInstance(ControlFragment.MODE_USER_REPORTS))
|
||||
} else {
|
||||
binding.navigationView.inflateMenu(R.menu.drawer_menu_user)
|
||||
loadFragment(HomeFragment())
|
||||
}
|
||||
|
||||
val header = binding.navigationView.getHeaderView(0)
|
||||
header.findViewById<TextView>(R.id.tvUserName)?.text = user.name
|
||||
header.findViewById<TextView>(R.id.tvUserEmail)?.text = user.email
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
val fragment: Fragment? = when (item.itemId) {
|
||||
R.id.nav_home -> HomeFragment()
|
||||
R.id.nav_info -> InfoDesperdiciosFragment()
|
||||
R.id.nav_report -> ReportarFragment()
|
||||
R.id.nav_user_reports -> ControlFragment.newInstance(ControlFragment.MODE_USER_REPORTS)
|
||||
R.id.nav_worker_reports -> ControlFragment.newInstance(ControlFragment.MODE_WORKER_REPORTS)
|
||||
R.id.nav_logout -> {
|
||||
auth.signOut()
|
||||
goToLogin()
|
||||
null
|
||||
}
|
||||
R.id.nav_exit -> {
|
||||
finishAffinity()
|
||||
null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
fragment?.let { loadFragment(it) }
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadFragment(fragment: Fragment) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, fragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun goToLogin() {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentUser(): User? = currentUser
|
||||
}
|
||||
111
app/src/main/java/com/example/basurapp/RegisterActivity.kt
Normal file
111
app/src/main/java/com/example/basurapp/RegisterActivity.kt
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.example.basurapp
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Patterns
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.example.basurapp.databinding.ActivityRegisterBinding
|
||||
import com.example.basurapp.models.User
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
|
||||
class RegisterActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityRegisterBinding
|
||||
private val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance() }
|
||||
private val db: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityRegisterBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->
|
||||
val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
binding.btnRegister.setOnClickListener {
|
||||
attemptRegister()
|
||||
}
|
||||
}
|
||||
|
||||
private fun attemptRegister() {
|
||||
val name = binding.etName.text?.toString()?.trim().orEmpty()
|
||||
val email = binding.etEmail.text?.toString()?.trim().orEmpty()
|
||||
val password = binding.etPassword.text?.toString().orEmpty()
|
||||
val confirmPassword = binding.etConfirmPassword.text?.toString().orEmpty()
|
||||
val role = if (binding.rbWorker.isChecked) User.ROLE_WORKER else User.ROLE_USER
|
||||
|
||||
// Validaciones
|
||||
if (name.isEmpty() || email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) {
|
||||
toast(getString(R.string.error_empty_fields))
|
||||
return
|
||||
}
|
||||
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||
binding.tilEmail.error = getString(R.string.error_invalid_email)
|
||||
return
|
||||
}
|
||||
binding.tilEmail.error = null
|
||||
|
||||
if (password.length < 6) {
|
||||
binding.tilPassword.error = getString(R.string.error_short_password)
|
||||
return
|
||||
}
|
||||
binding.tilPassword.error = null
|
||||
|
||||
if (password != confirmPassword) {
|
||||
binding.tilConfirmPassword.error = getString(R.string.error_passwords_dont_match)
|
||||
return
|
||||
}
|
||||
binding.tilConfirmPassword.error = null
|
||||
|
||||
setLoading(true)
|
||||
auth.createUserWithEmailAndPassword(email, password)
|
||||
.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
val uid = auth.currentUser?.uid ?: return@addOnCompleteListener
|
||||
val user = User(
|
||||
id = uid,
|
||||
name = name,
|
||||
email = email,
|
||||
role = role
|
||||
)
|
||||
// Guardar en Firestore
|
||||
db.collection("users").document(uid).set(user)
|
||||
.addOnSuccessListener {
|
||||
toast(getString(R.string.success_register))
|
||||
goToMain()
|
||||
}
|
||||
.addOnFailureListener { e ->
|
||||
setLoading(false)
|
||||
toast(e.localizedMessage ?: "Error guardando usuario")
|
||||
}
|
||||
} else {
|
||||
setLoading(false)
|
||||
toast(task.exception?.localizedMessage ?: "Error al registrar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToMain() {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun setLoading(loading: Boolean) {
|
||||
binding.progressBar.visibility = if (loading) View.VISIBLE else View.GONE
|
||||
binding.btnRegister.isEnabled = !loading
|
||||
}
|
||||
|
||||
private fun toast(msg: String) {
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.example.basurapp.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.example.basurapp.databinding.ItemReportBinding
|
||||
import com.example.basurapp.models.Report
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class ReportsAdapter : ListAdapter<Report, ReportsAdapter.ReportViewHolder>(DIFF) {
|
||||
|
||||
private val dateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault())
|
||||
|
||||
inner class ReportViewHolder(val binding: ItemReportBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(report: Report) {
|
||||
binding.tvSubject.text = report.subject
|
||||
binding.tvMessage.text = report.message
|
||||
binding.tvDate.text = dateFormat.format(Date(report.timestamp))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReportViewHolder {
|
||||
val binding = ItemReportBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
return ReportViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ReportViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DIFF = object : DiffUtil.ItemCallback<Report>() {
|
||||
override fun areItemsTheSame(oldItem: Report, newItem: Report) = oldItem.id == newItem.id
|
||||
override fun areContentsTheSame(oldItem: Report, newItem: Report) = oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.example.basurapp.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.example.basurapp.databinding.ItemRouteBinding
|
||||
import com.example.basurapp.models.Route
|
||||
|
||||
class RoutesAdapter(
|
||||
private val onClick: (Route) -> Unit
|
||||
) : ListAdapter<Route, RoutesAdapter.RouteViewHolder>(DIFF) {
|
||||
|
||||
inner class RouteViewHolder(val binding: ItemRouteBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(route: Route) {
|
||||
binding.tvRouteName.text = route.name
|
||||
binding.truckProgress.setRoute(route.stops.size, route.truckPosition)
|
||||
binding.root.setOnClickListener { onClick(route) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RouteViewHolder {
|
||||
val binding = ItemRouteBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
return RouteViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RouteViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DIFF = object : DiffUtil.ItemCallback<Route>() {
|
||||
override fun areItemsTheSame(oldItem: Route, newItem: Route) = oldItem.id == newItem.id
|
||||
override fun areContentsTheSame(oldItem: Route, newItem: Route) = oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.example.basurapp.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.example.basurapp.R
|
||||
import com.example.basurapp.databinding.DialogRouteInfoBinding
|
||||
import com.example.basurapp.models.Route
|
||||
import com.example.basurapp.models.RouteStop
|
||||
|
||||
class RouteInfoDialog : DialogFragment() {
|
||||
|
||||
private var route: Route? = null
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
route = arguments?.getSerializable(ARG_ROUTE) as? Route
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val binding = DialogRouteInfoBinding.inflate(LayoutInflater.from(requireContext()))
|
||||
|
||||
val r = route
|
||||
if (r != null) {
|
||||
binding.tvRouteTitle.text = "${getString(R.string.route_label_prefix)} ${r.name}"
|
||||
// Agregar dinámicamente cada parada
|
||||
binding.layoutStops.removeAllViews()
|
||||
r.stops.forEach { stop ->
|
||||
binding.layoutStops.addView(buildStopRow(stop))
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnAccept.setOnClickListener { dismiss() }
|
||||
binding.btnExit.setOnClickListener { dismiss() }
|
||||
|
||||
return AlertDialog.Builder(requireContext())
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun buildStopRow(stop: RouteStop): TextView {
|
||||
return TextView(requireContext()).apply {
|
||||
text = "Paso por ${stop.name} a las ${stop.time}"
|
||||
textSize = 14f
|
||||
setTextColor(resources.getColor(R.color.gray_900, null))
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply { topMargin = 12 }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_ROUTE = "route"
|
||||
|
||||
fun newInstance(route: Route): RouteInfoDialog {
|
||||
return RouteInfoDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(ARG_ROUTE, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.example.basurapp.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.example.basurapp.adapters.ReportsAdapter
|
||||
import com.example.basurapp.databinding.FragmentControlBinding
|
||||
import com.example.basurapp.models.Report
|
||||
import com.example.basurapp.models.User
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
import com.google.firebase.firestore.Query
|
||||
|
||||
class ControlFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentControlBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val db: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
|
||||
private lateinit var reportsAdapter: ReportsAdapter
|
||||
|
||||
private var mode: String = MODE_USER_REPORTS
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mode = arguments?.getString(ARG_MODE) ?: MODE_USER_REPORTS
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentControlBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
reportsAdapter = ReportsAdapter()
|
||||
binding.rvReports.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.rvReports.adapter = reportsAdapter
|
||||
|
||||
loadReports()
|
||||
}
|
||||
|
||||
private fun loadReports() {
|
||||
val roleFilter = if (mode == MODE_WORKER_REPORTS) User.ROLE_WORKER else User.ROLE_USER
|
||||
|
||||
db.collection("reports")
|
||||
.whereEqualTo("userRole", roleFilter)
|
||||
.orderBy("timestamp", Query.Direction.DESCENDING)
|
||||
.get()
|
||||
.addOnSuccessListener { result ->
|
||||
val reports = result.documents.mapNotNull { it.toObject(Report::class.java) }
|
||||
if (reports.isEmpty()) {
|
||||
binding.tvEmpty.visibility = View.VISIBLE
|
||||
binding.rvReports.visibility = View.GONE
|
||||
} else {
|
||||
binding.tvEmpty.visibility = View.GONE
|
||||
binding.rvReports.visibility = View.VISIBLE
|
||||
reportsAdapter.submitList(reports)
|
||||
}
|
||||
}
|
||||
.addOnFailureListener {
|
||||
binding.tvEmpty.visibility = View.VISIBLE
|
||||
binding.rvReports.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MODE_USER_REPORTS = "user_reports"
|
||||
const val MODE_WORKER_REPORTS = "worker_reports"
|
||||
private const val ARG_MODE = "mode"
|
||||
|
||||
fun newInstance(mode: String): ControlFragment {
|
||||
return ControlFragment().apply {
|
||||
arguments = Bundle().apply { putString(ARG_MODE, mode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
app/src/main/java/com/example/basurapp/fragments/HomeFragment.kt
Normal file
117
app/src/main/java/com/example/basurapp/fragments/HomeFragment.kt
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.example.basurapp.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.example.basurapp.adapters.RoutesAdapter
|
||||
import com.example.basurapp.databinding.HomeFragmentBinding
|
||||
import com.example.basurapp.dialogs.RouteInfoDialog
|
||||
import com.example.basurapp.models.Route
|
||||
import com.example.basurapp.models.RouteStop
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
|
||||
private var _binding: HomeFragmentBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance() }
|
||||
private val db: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
|
||||
|
||||
private lateinit var routesAdapter: RoutesAdapter
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = HomeFragmentBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Adapter de rutas
|
||||
routesAdapter = RoutesAdapter { route ->
|
||||
RouteInfoDialog.newInstance(route).show(parentFragmentManager, "route_info")
|
||||
}
|
||||
binding.rvRoutes.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.rvRoutes.adapter = routesAdapter
|
||||
|
||||
binding.btnAddAddress.setOnClickListener {
|
||||
Toast.makeText(requireContext(), "Función Agregar dirección (pendiente)", Toast.LENGTH_SHORT).show()
|
||||
// TODO: Abrir Activity o dialog para agregar dirección
|
||||
}
|
||||
|
||||
checkUserAddress()
|
||||
}
|
||||
|
||||
private fun checkUserAddress() {
|
||||
val uid = auth.currentUser?.uid ?: return
|
||||
db.collection("addresses")
|
||||
.whereEqualTo("userId", uid)
|
||||
.get()
|
||||
.addOnSuccessListener { result ->
|
||||
if (result.isEmpty) {
|
||||
showNoAddressState()
|
||||
} else {
|
||||
showRoutesState()
|
||||
}
|
||||
}
|
||||
.addOnFailureListener {
|
||||
showNoAddressState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showNoAddressState() {
|
||||
binding.layoutNoAddress.visibility = View.VISIBLE
|
||||
binding.rvRoutes.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showRoutesState() {
|
||||
binding.layoutNoAddress.visibility = View.GONE
|
||||
binding.rvRoutes.visibility = View.VISIBLE
|
||||
loadRoutes()
|
||||
}
|
||||
|
||||
private fun loadRoutes() {
|
||||
// Por ahora datos de ejemplo (después se conectan a Firestore)
|
||||
val sampleRoutes = listOf(
|
||||
Route(
|
||||
id = "1",
|
||||
name = "Dirección 1",
|
||||
neighborhood = "Centro",
|
||||
stops = listOf(
|
||||
RouteStop("Inicio", "07:00"),
|
||||
RouteStop("Calle 5 de Mayo", "07:30"),
|
||||
RouteStop("Plaza Central", "08:00"),
|
||||
RouteStop("Av. Juárez", "08:30"),
|
||||
RouteStop("Final de ruta", "09:00")
|
||||
),
|
||||
truckPosition = 2
|
||||
),
|
||||
Route(
|
||||
id = "2",
|
||||
name = "Dirección 1",
|
||||
neighborhood = "Zona Norte",
|
||||
stops = listOf(
|
||||
RouteStop("Inicio", "10:00"),
|
||||
RouteStop("Mercado Norte", "10:30"),
|
||||
RouteStop("Parque Hidalgo", "11:00"),
|
||||
RouteStop("Final de ruta", "11:30")
|
||||
),
|
||||
truckPosition = 1
|
||||
)
|
||||
)
|
||||
routesAdapter.submitList(sampleRoutes)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.example.basurapp.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.basurapp.databinding.FragmentInfoDesperdiciosBinding
|
||||
|
||||
class InfoDesperdiciosFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentInfoDesperdiciosBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentInfoDesperdiciosBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.example.basurapp.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.basurapp.MainActivity
|
||||
import com.example.basurapp.R
|
||||
import com.example.basurapp.databinding.FragmentReportarBinding
|
||||
import com.example.basurapp.models.Report
|
||||
import com.example.basurapp.models.User
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
|
||||
class ReportarFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentReportarBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance() }
|
||||
private val db: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentReportarBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.btnSend.setOnClickListener {
|
||||
sendReport()
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendReport() {
|
||||
val subject = binding.etSubject.text?.toString()?.trim().orEmpty()
|
||||
val message = binding.etMessage.text?.toString()?.trim().orEmpty()
|
||||
|
||||
if (subject.isEmpty() || message.isEmpty()) {
|
||||
Toast.makeText(requireContext(), getString(R.string.error_empty_fields), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val uid = auth.currentUser?.uid ?: return
|
||||
val userRole = (activity as? MainActivity)?.getCurrentUser()?.role ?: User.ROLE_USER
|
||||
|
||||
val reportId = db.collection("reports").document().id
|
||||
val report = Report(
|
||||
id = reportId,
|
||||
userId = uid,
|
||||
userRole = userRole,
|
||||
subject = subject,
|
||||
message = message,
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
binding.btnSend.isEnabled = false
|
||||
db.collection("reports").document(reportId).set(report)
|
||||
.addOnSuccessListener {
|
||||
Toast.makeText(requireContext(), getString(R.string.success_report), Toast.LENGTH_SHORT).show()
|
||||
binding.etSubject.setText("")
|
||||
binding.etMessage.setText("")
|
||||
binding.btnSend.isEnabled = true
|
||||
}
|
||||
.addOnFailureListener { e ->
|
||||
binding.btnSend.isEnabled = true
|
||||
Toast.makeText(requireContext(), e.localizedMessage ?: "Error", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
14
app/src/main/java/com/example/basurapp/models/Adress.kt
Normal file
14
app/src/main/java/com/example/basurapp/models/Adress.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.example.basurapp.models
|
||||
|
||||
/**
|
||||
* Dirección de un usuario. Colección "addresses" en Firestore.
|
||||
*/
|
||||
data class Address(
|
||||
val id: String = "",
|
||||
val userId: String = "",
|
||||
val street: String = "",
|
||||
val number: String = "",
|
||||
val neighborhood: String = "",
|
||||
val city: String = "",
|
||||
val reference: String = ""
|
||||
)
|
||||
14
app/src/main/java/com/example/basurapp/models/Report.kt
Normal file
14
app/src/main/java/com/example/basurapp/models/Report.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.example.basurapp.models
|
||||
|
||||
/**
|
||||
* Reporte hecho por un usuario o trabajador.
|
||||
* Colección "reports" en Firestore.
|
||||
*/
|
||||
data class Report(
|
||||
val id: String = "",
|
||||
val userId: String = "",
|
||||
val userRole: String = "", // "user" | "worker"
|
||||
val subject: String = "",
|
||||
val message: String = "",
|
||||
val timestamp: Long = System.currentTimeMillis()
|
||||
)
|
||||
16
app/src/main/java/com/example/basurapp/models/Route.kt
Normal file
16
app/src/main/java/com/example/basurapp/models/Route.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.example.basurapp.models
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class Route(
|
||||
val id: String = "",
|
||||
val name: String = "",
|
||||
val neighborhood: String = "",
|
||||
val stops: List<RouteStop> = emptyList(),
|
||||
val truckPosition: Int = 0
|
||||
) : Serializable
|
||||
|
||||
data class RouteStop(
|
||||
val name: String = "",
|
||||
val time: String = ""
|
||||
) : Serializable
|
||||
18
app/src/main/java/com/example/basurapp/models/User.kt
Normal file
18
app/src/main/java/com/example/basurapp/models/User.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.example.basurapp.models
|
||||
|
||||
/**
|
||||
* Modelo de usuario. Se guarda en Firestore en la colección "users".
|
||||
* El id corresponde al UID de Firebase Auth.
|
||||
*/
|
||||
data class User(
|
||||
val id: String = "",
|
||||
val name: String = "",
|
||||
val email: String = "",
|
||||
val role: String = ROLE_USER,
|
||||
val photoUrl: String = ""
|
||||
) {
|
||||
companion object {
|
||||
const val ROLE_USER = "user"
|
||||
const val ROLE_WORKER = "worker"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.example.basurapp.views
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.example.basurapp.R
|
||||
|
||||
class TruckProgressView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
private var stopCount: Int = 4
|
||||
private var truckPosition: Int = 0
|
||||
|
||||
private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = Color.BLACK
|
||||
strokeWidth = 6f
|
||||
}
|
||||
|
||||
private val stopPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = Color.BLACK
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
private val truckBodyPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = context.getColor(R.color.green_primary)
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
private val truckCabinPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = context.getColor(R.color.green_dark)
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
private val wheelPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = Color.BLACK
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
fun setRoute(stops: Int, position: Int) {
|
||||
stopCount = stops.coerceAtLeast(2)
|
||||
truckPosition = position.coerceIn(0, stopCount - 1)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
val w = width.toFloat()
|
||||
val h = height.toFloat()
|
||||
val padding = 40f
|
||||
val lineY = h / 2 + 20
|
||||
|
||||
// Línea horizontal
|
||||
canvas.drawLine(padding, lineY, w - padding, lineY, linePaint)
|
||||
|
||||
// Paradas (círculos)
|
||||
val step = (w - padding * 2) / (stopCount - 1)
|
||||
for (i in 0 until stopCount) {
|
||||
val cx = padding + step * i
|
||||
canvas.drawCircle(cx, lineY, 14f, stopPaint)
|
||||
}
|
||||
|
||||
// Camión sobre la parada actual
|
||||
val truckX = padding + step * truckPosition
|
||||
drawTruck(canvas, truckX, lineY - 30f)
|
||||
}
|
||||
|
||||
private fun drawTruck(canvas: Canvas, x: Float, y: Float) {
|
||||
// Caja del camión
|
||||
canvas.drawRect(x - 30f, y - 20f, x + 10f, y + 10f, truckBodyPaint)
|
||||
// Cabina
|
||||
canvas.drawRect(x + 10f, y - 10f, x + 30f, y + 10f, truckCabinPaint)
|
||||
// Ruedas
|
||||
canvas.drawCircle(x - 20f, y + 12f, 6f, wheelPaint)
|
||||
canvas.drawCircle(x + 20f, y + 12f, 6f, wheelPaint)
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/drawable/bg_logo_circle.xml
Normal file
5
app/src/main/res/drawable/bg_logo_circle.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,11v2h10v-2L7,11zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
||||
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/bg_rol_unselected.xml
Normal file
5
app/src/main/res/drawable/bg_rol_unselected.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M440,680L520,680L520,520L680,520L680,440L520,440L520,280L440,280L440,440L280,440L280,520L440,520L440,680ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200Z"/>
|
||||
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_truck.xml
Normal file
5
app/src/main/res/drawable/ic_truck.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11L5,11z"/>
|
||||
|
||||
</vector>
|
||||
110
app/src/main/res/layout/activity_login.xml
Normal file
110
app/src/main/res/layout/activity_login.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:padding="24dp"
|
||||
tools:context=".LoginActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:text="@string/login_title"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:letterSpacing="0.05"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogo"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@color/gray_300"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:src="@mipmap/ic_launcher_round"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvTitle" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilEmail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:hint="@string/hint_email"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/ivLogo">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etEmail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilPassword"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="@string/hint_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilEmail">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCreateAccount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/create_account"
|
||||
android:textColor="@color/green_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEnter"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/btn_enter"
|
||||
android:textSize="16sp"
|
||||
app:cornerRadius="28dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvCreateAccount" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,19 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:id="@+id/drawerLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello Adri!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/gray_200">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/gray_200"
|
||||
app:titleTextColor="@color/gray_900" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/navigationView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:background="@color/gray_100"
|
||||
app:headerLayout="@layout/nav_header"
|
||||
app:menu="@menu/drawer_menu_user" />
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
173
app/src/main/res/layout/activity_register.xml
Normal file
173
app/src/main/res/layout/activity_register.xml
Normal file
@@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:fillViewport="true"
|
||||
tools:context=".RegisterActivity">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/register_title"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogo"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@color/gray_300"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:src="@mipmap/ic_launcher_round"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvTitle" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/hint_name"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/ivLogo">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPersonName" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilEmail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="@string/hint_email"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilName">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etEmail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilPassword"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="@string/hint_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilEmail">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilConfirmPassword"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="@string/hint_confirm_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilPassword">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etConfirmPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRoleLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/select_role"
|
||||
android:textColor="@color/gray_700"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilConfirmPassword" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rgRole"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRoleLabel">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbUser"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:checked="true"
|
||||
android:text="@string/role_user" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbWorker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/role_worker" />
|
||||
</RadioGroup>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnRegister"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/btn_enter"
|
||||
android:textSize="16sp"
|
||||
app:cornerRadius="28dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rgRole" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/btnRegister"
|
||||
app:layout_constraintEnd_toEndOf="@id/btnRegister"
|
||||
app:layout_constraintStart_toStartOf="@id/btnRegister"
|
||||
app:layout_constraintTop_toTopOf="@id/btnRegister"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
55
app/src/main/res/layout/dialog_route_info.xml
Normal file
55
app/src/main/res/layout/dialog_route_info.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/gray_200"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRouteTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutStops"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRouteTitle" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnAccept"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/btn_accept"
|
||||
app:cornerRadius="24dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnExit"
|
||||
app:layout_constraintHorizontal_chainStyle="spread"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layoutStops" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnExit"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/btn_exit"
|
||||
app:cornerRadius="24dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btnAccept"
|
||||
app:layout_constraintTop_toBottomOf="@id/layoutStops" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
36
app/src/main/res/layout/fragment_control.xml
Normal file
36
app/src/main/res/layout/fragment_control.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/control_title"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvReports"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="48dp"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/item_report" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmpty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="No hay reportes"
|
||||
android:textColor="@color/gray_500"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
||||
85
app/src/main/res/layout/fragment_info_desperdicios.xml
Normal file
85
app/src/main/res/layout/fragment_info_desperdicios.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/info_waste_title"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/green_primary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="Tipos de desperdicios"
|
||||
android:textColor="@color/green_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="• Orgánico: restos de comida, cáscaras, hojas\n• Inorgánico reciclable: papel, cartón, plástico, vidrio, metal\n• Inorgánico no reciclable: pañales, papel sanitario\n• Peligroso: pilas, baterías, medicamentos, electrónicos"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="Cómo separar correctamente"
|
||||
android:textColor="@color/green_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="Utiliza bolsas o contenedores separados para cada tipo de residuo. Limpia y seca los reciclables antes de desecharlos. No mezcles residuos peligrosos con la basura común."
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="Consejos para reducir"
|
||||
android:textColor="@color/green_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="• Usa bolsas reutilizables\n• Compra a granel cuando puedas\n• Composta los residuos orgánicos\n• Repara antes de reemplazar"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
66
app/src/main/res/layout/fragment_reportar.xml
Normal file
66
app/src/main/res/layout/fragment_reportar.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/report_title"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilSubject"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:hint="@string/hint_subject">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etSubject"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="@string/hint_message">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="180dp"
|
||||
android:gravity="top|start"
|
||||
android:inputType="textMultiLine"
|
||||
android:scrollbars="vertical" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnSend"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="24dp"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingEnd="32dp"
|
||||
android:text="@string/btn_send"
|
||||
app:cornerRadius="24dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
57
app/src/main/res/layout/home_fragment.xml
Normal file
57
app/src/main/res/layout/home_fragment.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<!-- Estado "sin dirección" -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_address_title"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_routes"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnAddAddress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/add_address"
|
||||
app:cornerRadius="24dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Lista de rutas -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvRoutes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/item_route"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
41
app/src/main/res/layout/item_report.xml
Normal file
41
app/src/main/res/layout/item_report.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSubject"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/green_primary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:textColor="@color/gray_500"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
31
app/src/main/res/layout/item_route.xml
Normal file
31
app/src/main/res/layout/item_route.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRouteName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.example.basurapp.views.TruckProgressView
|
||||
android:id="@+id/truckProgress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="8dp" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
35
app/src/main/res/layout/nav_header.xml
Normal file
35
app/src/main/res/layout/nav_header.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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="wrap_content"
|
||||
android:background="@color/gray_200"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivUserPhoto"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:background="@color/gray_300"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:src="@mipmap/ic_launcher_round" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUserName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="usuario"
|
||||
android:textColor="@color/gray_900"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUserEmail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="@color/gray_700"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
27
app/src/main/res/menu/drawer_menu_user.xml
Normal file
27
app/src/main/res/menu/drawer_menu_user.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<group android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/nav_home"
|
||||
android:title="@string/add_address" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_info"
|
||||
android:title="@string/menu_info_waste" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_report"
|
||||
android:title="@string/menu_report" />
|
||||
</group>
|
||||
|
||||
<group android:id="@+id/group_bottom">
|
||||
<item
|
||||
android:id="@+id/nav_logout"
|
||||
android:title="@string/menu_logout" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_exit"
|
||||
android:title="@string/menu_exit" />
|
||||
</group>
|
||||
</menu>
|
||||
23
app/src/main/res/menu/drawer_menu_worker.xml
Normal file
23
app/src/main/res/menu/drawer_menu_worker.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<group android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/nav_user_reports"
|
||||
android:title="@string/menu_user_reports" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_worker_reports"
|
||||
android:title="@string/menu_worker_reports" />
|
||||
</group>
|
||||
|
||||
<group android:id="@+id/group_bottom">
|
||||
<item
|
||||
android:id="@+id/nav_logout"
|
||||
android:title="@string/menu_logout" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_exit"
|
||||
android:title="@string/menu_exit" />
|
||||
</group>
|
||||
</menu>
|
||||
@@ -1,5 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<!-- Paleta verde (reciclaje) -->
|
||||
<color name="green_primary">#2E7D32</color>
|
||||
<color name="green_dark">#1B5E20</color>
|
||||
<color name="green_light">#66BB6A</color>
|
||||
<color name="green_pale">#E8F5E9</color>
|
||||
|
||||
<!-- Neutros -->
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="black">#000000</color>
|
||||
<color name="gray_100">#F5F5F5</color>
|
||||
<color name="gray_200">#EEEEEE</color>
|
||||
<color name="gray_300">#E0E0E0</color>
|
||||
<color name="gray_400">#BDBDBD</color>
|
||||
<color name="gray_500">#9E9E9E</color>
|
||||
<color name="gray_700">#616161</color>
|
||||
<color name="gray_900">#212121</color>
|
||||
|
||||
<!-- Estados -->
|
||||
<color name="error">#D32F2F</color>
|
||||
<color name="success">#388E3C</color>
|
||||
</resources>
|
||||
@@ -1,3 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">BasurApp</string>
|
||||
|
||||
<!-- Login -->
|
||||
<string name="login_title">Inicio de sesión</string>
|
||||
<string name="hint_email">Correo electrónico</string>
|
||||
<string name="hint_password">Contraseña</string>
|
||||
<string name="create_account">Crear usuario</string>
|
||||
<string name="btn_enter">Entrar</string>
|
||||
|
||||
<!-- Register -->
|
||||
<string name="register_title">Crear usuario</string>
|
||||
<string name="hint_name">Nombre completo</string>
|
||||
<string name="hint_confirm_password">Confirmar contraseña</string>
|
||||
<string name="role_user">Usuario</string>
|
||||
<string name="role_worker">Trabajador</string>
|
||||
<string name="select_role">Selecciona tu rol:</string>
|
||||
|
||||
<!-- Main -->
|
||||
<string name="no_address_title">Sin dirección</string>
|
||||
<string name="no_routes">NO RUTAS</string>
|
||||
<string name="add_address">Agregar dirección</string>
|
||||
|
||||
<!-- Drawer -->
|
||||
<string name="drawer_open">Abrir menú</string>
|
||||
<string name="drawer_close">Cerrar menú</string>
|
||||
<string name="menu_info_waste">Información sobre desperdicios</string>
|
||||
<string name="menu_report">Reportar problema</string>
|
||||
<string name="menu_logout">Cerrar sesión</string>
|
||||
<string name="menu_exit">Salir</string>
|
||||
<string name="menu_user_reports">Reportes de usuarios</string>
|
||||
<string name="menu_worker_reports">Reportes de trabajadores</string>
|
||||
|
||||
<!-- Info / Reportar -->
|
||||
<string name="info_waste_title">Información de desperdicios</string>
|
||||
<string name="report_title">Reportes</string>
|
||||
<string name="hint_subject">Asunto</string>
|
||||
<string name="hint_message">Mensaje aquí…</string>
|
||||
<string name="btn_send">Enviar</string>
|
||||
|
||||
<!-- Trabajador -->
|
||||
<string name="control_title">Control</string>
|
||||
|
||||
<!-- Diálogo de ruta -->
|
||||
<string name="btn_accept">Aceptar</string>
|
||||
<string name="btn_exit">Salir</string>
|
||||
|
||||
<!-- Mensajes -->
|
||||
<string name="error_empty_fields">Completa todos los campos</string>
|
||||
<string name="error_passwords_dont_match">Las contraseñas no coinciden</string>
|
||||
<string name="error_invalid_email">Correo inválido</string>
|
||||
<string name="error_short_password">La contraseña debe tener al menos 6 caracteres</string>
|
||||
<string name="success_register">Cuenta creada correctamente</string>
|
||||
<string name="success_report">Reporte enviado</string>
|
||||
|
||||
<string name="route_label_prefix">Ruta</string>
|
||||
</resources>
|
||||
@@ -1,9 +1,15 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.BasurApp" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Customize your light theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||
</style>
|
||||
<style name="Theme.BasurApp" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="colorPrimary">@color/green_primary</item>
|
||||
<item name="colorPrimaryVariant">@color/green_dark</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
|
||||
<style name="Theme.BasurApp" parent="Base.Theme.BasurApp" />
|
||||
<item name="colorSecondary">@color/green_light</item>
|
||||
<item name="colorOnSecondary">@color/white</item>
|
||||
|
||||
<item name="android:statusBarColor">@color/green_dark</item>
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
|
||||
<item name="android:windowBackground">@color/white</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
id("com.google.gms.google-services") version "4.4.4" apply false
|
||||
}
|
||||
Submodule hackathon-gituber-4fbb7edead2842bd8517a7b78b908771 updated: ec8d93ba60...165d9d2e96
Reference in New Issue
Block a user