From d96858d3c2689dadf923d88c593fd9d5b05f97e2 Mon Sep 17 00:00:00 2001 From: Tatiana Villa Ema Date: Sat, 2 May 2026 02:00:58 +0200 Subject: [PATCH] Estructura principal de la app --- README.md | 147 ++++---- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 17 + .../elfarodelperegrino/AlbergueAdapter.java | 7 + .../DetalleEtapaActivity.java | 149 ++++++-- .../DetalleServicioActivity.java | 94 +++++ .../es/tatvil/elfarodelperegrino/Etapa.java | 25 +- .../elfarodelperegrino/EtapasActivity.java | 115 ++++++ .../elfarodelperegrino/MainActivity.java | 192 ++-------- .../elfarodelperegrino/MainActivity2.class | 22 -- .../SimpleServicioAdapter.java | 85 +++++ .../res/drawable/ic_launcher_background.xml | 177 +--------- .../res/drawable/ic_launcher_foreground.xml | 67 +++- app/src/main/res/layout/Activity-main2.xml | 145 -------- .../res/layout/activity_detalle_etapa.xml | 215 +++++++++--- .../res/layout/activity_detalle_servicio.xml | 117 +++++++ app/src/main/res/layout/activity_etapas.xml | 57 +++ app/src/main/res/layout/activity_main.xml | 328 ++++++++++++++++-- app/src/main/res/raw/restaurantes.json | 6 + app/src/main/res/values/strings.xml | 23 ++ gradle/libs.versions.toml | 2 + settings.gradle.kts | 1 + 22 files changed, 1323 insertions(+), 669 deletions(-) create mode 100644 app/src/main/java/es/tatvil/elfarodelperegrino/DetalleServicioActivity.java create mode 100644 app/src/main/java/es/tatvil/elfarodelperegrino/EtapasActivity.java delete mode 100644 app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity2.class create mode 100644 app/src/main/java/es/tatvil/elfarodelperegrino/SimpleServicioAdapter.java delete mode 100644 app/src/main/res/layout/Activity-main2.xml create mode 100644 app/src/main/res/layout/activity_detalle_servicio.xml create mode 100644 app/src/main/res/layout/activity_etapas.xml diff --git a/README.md b/README.md index 5a08146..cdb4660 100644 --- a/README.md +++ b/README.md @@ -1,109 +1,104 @@ + # El Faro del Peregrino -Una aplicación espiritual para acompañar el camino interior y comunitario. -El Faro del Peregrino es una aplicación diseñada para ofrecer luz, guía y herramientas prácticas a quienes desean profundizar en su vida espiritual. Su propósito es integrar oración, reflexión, comunidad y belleza visual en una experiencia sencilla y significativa. +Una aplicación para acompañar el camino interior y comunitario, integrando oración, reflexión y comunidad en una experiencia visualmente inspiradora. --- -## Objetivos del proyecto -- Crear una app que acompañe el crecimiento espiritual personal. -- Integrar recursos como oraciones, meditaciones, lecturas y seguimiento del camino interior. -- Diseñar una interfaz cálida, estética y accesible. -- Construir una base sólida para futuras funcionalidades como: - - Comunidad de peregrinos - - Diario espiritual - - Ritmos diarios (Laudes, Vísperas, etc.) - - Notificaciones de oración - - Contenidos multimedia +## ✨ Propósito + +El Faro del Peregrino nace para ofrecer luz, guía y herramientas prácticas a quienes desean profundizar en su vida espiritual. Busca integrar recursos de oración, reflexión y comunidad en una app sencilla, cálida y significativa. --- -## Tecnologías utilizadas -Este proyecto está construido con: +## 🎯 Objetivos -- Java / Kotlin (según evolucione el código del módulo app/) -- Gradle como sistema de construcción -- Android Studio como entorno de desarrollo -- Estructura modular preparada para escalar +- Acompañar el crecimiento espiritual personal y comunitario. +- Integrar oraciones, meditaciones, lecturas y seguimiento del camino interior. +- Ofrecer una interfaz estética, accesible y acogedora. +- Sentar las bases para futuras funcionalidades: + - Comunidad de peregrinos + - Diario espiritual + - Ritmos diarios (Laudes, Vísperas, etc.) + - Notificaciones de oración + - Contenidos multimedia --- -## Estructura del repositorio -` +## 🛠️ Tecnologías + +- **Lenguajes:** Java / Kotlin (según evolución del módulo `app/`) +- **Build:** Gradle (con wrapper incluido) +- **IDE:** Android Studio (recomendado Flamingo o superior) +- **Arquitectura:** Modular y escalable + +--- + +## 📁 Estructura del repositorio + +``` el-faro-del-peregrino/ -│ -├── app/ -├── gradle/ -├── build.gradle.kts -├── settings.gradle.kts -├── gradle.properties -└── .idea/ -` +├── app/ # Código principal de la app +├── gradle/ # Configuración de Gradle +├── build.gradle.kts # Build script raíz +├── settings.gradle.kts # Configuración de módulos +├── gradle.properties # Propiedades globales +└── .idea/ # Configuración de Android Studio +``` --- -## Cómo ejecutar el proyecto +## 🚀 Cómo ejecutar el proyecto -1. Requisitos previos -- Android Studio Flamingo o superior -- JDK 17+ -- Gradle (incluido en el wrapper) +1. **Requisitos previos** + - Android Studio Flamingo o superior + - JDK 17+ + - Gradle (incluido en el wrapper) -2. Clonar el repositorio -`bash -git clone https://git.tatvil.es/tatiana/el-faro-del-peregrino.git -cd el-faro-del-peregrino -` +2. **Clonar el repositorio** + ```bash + git clone https://git.tatvil.es/tatiana/el-faro-del-peregrino.git + cd el-faro-del-peregrino + ``` -3. Abrir en Android Studio -- Selecciona Open -- Elige la carpeta del proyecto -- Android Studio descargará dependencias y sincronizará Gradle automáticamente +3. **Abrir en Android Studio** + - Selecciona *Open* + - Elige la carpeta del proyecto + - Android Studio descargará dependencias y sincronizará Gradle automáticamente -4. Ejecutar la app -- Conecta un dispositivo o usa un emulador -- Pulsa ▶️ Run +4. **Ejecutar la app** + - Conecta un dispositivo o usa un emulador + - Pulsa ▶️ *Run* --- -🗺️ Roadmap inicial -Este roadmap está alineado con el estilo profesional que ya usas en GitHub Issues. +## 🗺️ Roadmap -Versión 0.1 — Estructura base -- [ ] Configuración inicial del proyecto -- [ ] Pantalla de bienvenida -- [ ] Navegación básica +**Versión 0.1 — Estructura base** +- [ ] Configuración inicial del proyecto +- [ ] Pantalla de bienvenida +- [ ] Navegación básica - [ ] Tema visual inicial (colores, tipografías, iconografía) -Versión 0.2 — Contenidos espirituales -- [ ] Módulo de oraciones -- [ ] Lecturas del día -- [ ] Modo oscuro contemplativo +**Versión 0.2 — Contenidos espirituales** +- [ ] Módulo de oraciones +- [ ] Lecturas del día +- [ ] Modo oscuro contemplativo -Versión 0.3 — Herramientas del peregrino -- [ ] Diario espiritual -- [ ] Seguimiento del camino -- [ ] Notificaciones de ritmo diario +**Versión 0.3 — Herramientas del peregrino** +- [ ] Diario espiritual +- [ ] Seguimiento del camino +- [ ] Notificaciones de ritmo diario -Versión 1.0 — Comunidad -- [ ] Perfil del peregrino -- [ ] Espacios de oración compartida -- [ ] Mensajes y peticiones +**Versión 1.0 — Comunidad** +- [ ] Perfil del peregrino +- [ ] Espacios de oración compartida +- [ ] Mensajes y peticiones --- -🤝 Contribuciones -Este proyecto está en desarrollo activo. -Las ideas, mejoras y bugs se gestionan mediante GitHub Issues siguiendo un flujo profesional. +## 🌅 Inspiración ---- +> “El faro no navega por ti, pero ilumina el camino.” -📜 Licencia -Pendiente de definir. -Recomendación: MIT o Apache 2.0 para proyectos abiertos; licencia privada si será app personal o comercial. - ---- - -🕯️ Inspiración -> “El faro no navega por ti, pero ilumina el camino.” -Este proyecto nace para ofrecer luz, calma y dirección en medio del viaje interior. +Este proyecto busca ofrecer luz, calma y dirección en el viaje interior. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 41dfaac..935c82c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -46,5 +46,6 @@ dependencies { implementation("com.google.android.gms:play-services-location:21.3.0") implementation(libs.volley) implementation(libs.glide) + implementation(libs.mpandroidchart) annotationProcessor(libs.glide.compiler) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0621925..a7cc49b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/AlbergueAdapter.java b/app/src/main/java/es/tatvil/elfarodelperegrino/AlbergueAdapter.java index f72bda3..2f4c861 100644 --- a/app/src/main/java/es/tatvil/elfarodelperegrino/AlbergueAdapter.java +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/AlbergueAdapter.java @@ -1,5 +1,6 @@ package es.tatvil.elfarodelperegrino; +import android.content.Intent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -40,6 +41,12 @@ public class AlbergueAdapter extends RecyclerView.Adapter { + Intent intent = new Intent(v.getContext(), DetalleServicioActivity.class); + intent.putExtra("objeto", albergue); + v.getContext().startActivity(intent); + }); } @Override diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleEtapaActivity.java b/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleEtapaActivity.java index 57ce0d1..f8dea3d 100644 --- a/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleEtapaActivity.java +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleEtapaActivity.java @@ -1,10 +1,13 @@ package es.tatvil.elfarodelperegrino; +import android.graphics.Color; import android.location.Location; import android.os.Bundle; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; @@ -17,12 +20,20 @@ import com.android.volley.RequestQueue; import com.android.volley.toolbox.JsonObjectRequest; import com.android.volley.toolbox.Volley; import com.bumptech.glide.Glide; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Locale; public class DetalleEtapaActivity extends AppCompatActivity { @@ -39,24 +50,34 @@ public class DetalleEtapaActivity extends AppCompatActivity { ImageView ivImagen = findViewById(R.id.ivDetalleImagen); TextView tvTitulo = findViewById(R.id.tvDetalleTitulo); TextView tvDistancia = findViewById(R.id.tvDetalleDistancia); + TextView tvDesnivelPos = findViewById(R.id.tvDetalleDesnivelPos); + TextView tvDesnivelNeg = findViewById(R.id.tvDetalleDesnivelNeg); TextView tvHistoria = findViewById(R.id.tvDetalleHistoria); TextView tvReflexion = findViewById(R.id.tvDetalleReflexion); TextView tvClima = findViewById(R.id.tvDetalleClima); tvTitulo.setText(etapa.getOrigen() + " - " + etapa.getDestino()); - tvDistancia.setText("Distancia: " + etapa.getDistancia() + " km"); + tvDistancia.setText(etapa.getDistancia() + " km"); + tvDesnivelPos.setText("+" + etapa.getDesnivelPositivo() + "m"); + tvDesnivelNeg.setText("-" + etapa.getDesnivelNegativo() + "m"); tvHistoria.setText(etapa.getHitoHistorico()); tvReflexion.setText(etapa.getReflexionEspiritual()); + configurarGrafico(findViewById(R.id.elevationChart), etapa); + Glide.with(this) .load(etapa.getImagenUrl()) .placeholder(android.R.drawable.ic_menu_gallery) .into(ivImagen); - obtenerClima(etapa.getLatitud(), etapa.getLongitud(), tvClima); + obtenerClimaYPrevision(etapa.getLatitud(), etapa.getLongitud(), tvClima, findViewById(R.id.layoutForecast)); - findViewById(R.id.btnVerClima).setOnClickListener(v -> - obtenerClima(etapa.getLatitud(), etapa.getLongitud(), tvClima)); + // Botón Mapa + findViewById(R.id.btnVerMapa).setOnClickListener(v -> { + String uri = "geo:" + etapa.getLatitud() + "," + etapa.getLongitud() + "?q=" + etapa.getDestino(); + android.content.Intent intent = new android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(uri)); + startActivity(intent); + }); // Botón Albergues findViewById(R.id.btnVerAlbergues).setOnClickListener(v -> { @@ -76,7 +97,7 @@ public class DetalleEtapaActivity extends AppCompatActivity { if (cercanas.isEmpty()) { Toast.makeText(this, "No hay iglesias registradas cerca", Toast.LENGTH_SHORT).show(); } else { - mostrarDialogoSimple("Iglesias cercanas", cercanas); + mostrarDialogoServicios("Iglesias cercanas", cercanas, etapa); } }); @@ -92,7 +113,7 @@ public class DetalleEtapaActivity extends AppCompatActivity { if (cercanos.isEmpty()) { Toast.makeText(this, "No hay restaurantes registrados cerca", Toast.LENGTH_SHORT).show(); } else { - mostrarDialogoSimple("Restaurantes cercanos", cercanos); + mostrarDialogoServicios("Restaurantes cercanos", cercanos, etapa); } }); } @@ -126,18 +147,17 @@ public class DetalleEtapaActivity extends AppCompatActivity { .show(); } - private void mostrarDialogoSimple(String titulo, List items) { - StringBuilder sb = new StringBuilder(); - for (Object item : items) { - if (item instanceof Iglesia) sb.append("• ").append(((Iglesia) item).getNombre()).append("\n"); - if (item instanceof Restaurante) sb.append("• ").append(((Restaurante) item).getNombre()).append("\n"); - } + private void mostrarDialogoServicios(String titulo, List items, Etapa etapa) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + View view = LayoutInflater.from(this).inflate(R.layout.dialog_lista_albergues, null); + RecyclerView rv = view.findViewById(R.id.rvDialogo); + rv.setLayoutManager(new LinearLayoutManager(this)); + rv.setAdapter(new SimpleServicioAdapter(items, etapa.getLatitud(), etapa.getLongitud())); - new AlertDialog.Builder(this) - .setTitle(titulo) - .setMessage(sb.toString()) - .setPositiveButton("OK", null) - .show(); + builder.setTitle(titulo) + .setView(view) + .setPositiveButton("Cerrar", null) + .show(); } private double calcularDistancia(double lat1, double lon1, double lat2, double lon2) { @@ -146,21 +166,100 @@ public class DetalleEtapaActivity extends AppCompatActivity { return results[0] / 1000; // Retorna km } - private void obtenerClima(double lat, double lon, TextView tvClima) { - String url = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&appid=" + API_KEY + "&units=metric&lang=es"; + private void configurarGrafico(LineChart chart, Etapa etapa) { + List entries = new ArrayList<>(); + List perfil = etapa.getPerfilElevacion(); + for (int i = 0; i < perfil.size(); i++) { + entries.add(new Entry(i, perfil.get(i))); + } + + LineDataSet dataSet = new LineDataSet(entries, "Elevación (m)"); + dataSet.setColor(Color.parseColor("#4CAF50")); + dataSet.setCircleColor(Color.parseColor("#4CAF50")); + dataSet.setLineWidth(2f); + dataSet.setCircleRadius(3f); + dataSet.setDrawCircleHole(false); + dataSet.setValueTextColor(Color.WHITE); + dataSet.setDrawFilled(true); + dataSet.setFillDrawable(getResources().getDrawable(R.drawable.ic_launcher_background)); // O un gradiente simple + dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); + + LineData lineData = new LineData(dataSet); + chart.setData(lineData); + chart.getDescription().setEnabled(false); + chart.getLegend().setTextColor(Color.WHITE); + chart.getXAxis().setTextColor(Color.WHITE); + chart.getAxisLeft().setTextColor(Color.WHITE); + chart.getAxisRight().setEnabled(false); + chart.animateX(1500); + chart.invalidate(); + } + + private void obtenerClimaYPrevision(double lat, double lon, TextView tvClima, LinearLayout layoutForecast) { + // Clima actual + String urlWeather = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&appid=" + API_KEY + "&units=metric&lang=es"; + // Previsión (5 días cada 3 horas) + String urlForecast = "https://api.openweathermap.org/data/2.5/forecast?lat=" + lat + "&lon=" + lon + "&appid=" + API_KEY + "&units=metric&lang=es"; + RequestQueue queue = Volley.newRequestQueue(this); - JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, + + // Request para clima actual + JsonObjectRequest requestWeather = new JsonObjectRequest(Request.Method.GET, urlWeather, null, response -> { try { JSONObject main = response.getJSONObject("main"); double temp = main.getDouble("temp"); String desc = response.getJSONArray("weather").getJSONObject(0).getString("description"); - tvClima.setText("Clima actual: " + temp + "°C, " + desc); + tvClima.setText("Hoy: " + temp + "°C, " + desc); } catch (JSONException e) { - tvClima.setText("Error al procesar clima"); + tvClima.setText("Error en clima actual"); } - }, - error -> tvClima.setText("Error al obtener clima")); - queue.add(request); + }, error -> tvClima.setText("Error al conectar con clima")); + + // Request para previsión + JsonObjectRequest requestForecast = new JsonObjectRequest(Request.Method.GET, urlForecast, null, + response -> { + try { + JSONArray list = response.getJSONArray("list"); + layoutForecast.removeAllViews(); + // Tomamos un punto por día (cada 8 registros son 24h aprox) + for (int i = 8; i < list.length() && i <= 24; i += 8) { + JSONObject dayObj = list.getJSONObject(i); + double temp = dayObj.getJSONObject("main").getDouble("temp"); + String desc = dayObj.getJSONArray("weather").getJSONObject(0).getString("description"); + long dt = dayObj.getLong("dt") * 1000; + String fecha = new SimpleDateFormat("EEE", new Locale("es", "ES")).format(new Date(dt)); + + agregarVistaPrevision(layoutForecast, fecha, temp, desc); + } + } catch (JSONException e) { + e.printStackTrace(); + } + }, error -> {}); + + queue.add(requestWeather); + queue.add(requestForecast); + } + + private void agregarVistaPrevision(LinearLayout layout, String dia, double temp, String desc) { + LinearLayout v = new LinearLayout(this); + v.setOrientation(LinearLayout.VERTICAL); + v.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f)); + v.setGravity(Gravity.CENTER); + + TextView tvDia = new TextView(this); + tvDia.setText(dia.toUpperCase()); + tvDia.setTextColor(Color.WHITE); + tvDia.setTextSize(12); + + TextView tvTemp = new TextView(this); + tvTemp.setText((int)temp + "°"); + tvTemp.setTextColor(Color.parseColor("#FFEB3B")); + tvTemp.setTextSize(16); + tvTemp.setTypeface(null, android.graphics.Typeface.BOLD); + + layout.addView(v); + v.addView(tvDia); + v.addView(tvTemp); } } diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleServicioActivity.java b/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleServicioActivity.java new file mode 100644 index 0000000..58b0a1a --- /dev/null +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/DetalleServicioActivity.java @@ -0,0 +1,94 @@ +package es.tatvil.elfarodelperegrino; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import com.google.android.material.button.MaterialButton; + +public class DetalleServicioActivity extends AppCompatActivity { + + private String nombre, telefono, tipo, descripcion; + private double latitud, longitud; + private boolean tieneCocina; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detalle_servicio); + + // Obtener datos del Intent + Object object = getIntent().getSerializableExtra("objeto"); + + if (object instanceof Albergue) { + Albergue a = (Albergue) object; + nombre = a.getNombre(); + telefono = a.getTelefono(); + tipo = a.getTipo(); + descripcion = a.getComentario(); + latitud = a.getLatitud(); + longitud = a.getLongitud(); + tieneCocina = a.isTieneCocina(); + } else if (object instanceof Restaurante) { + Restaurante r = (Restaurante) object; + nombre = r.getNombre(); + telefono = ""; // Por ahora no hay en JSON + tipo = getString(R.string.action_food).replace("\n", " "); + descripcion = getString(R.string.msg_food_place); + latitud = r.getLatitud(); + longitud = r.getLongitud(); + } else if (object instanceof Iglesia) { + Iglesia i = (Iglesia) object; + nombre = i.getNombre(); + telefono = ""; + tipo = getString(R.string.msg_interest_place); // Usando un string descriptivo como tipo + descripcion = getString(R.string.msg_interest_place); + latitud = i.getLatitud(); + longitud = i.getLongitud(); + } + + // Vincular vistas + TextView tvNombre = findViewById(R.id.tvDetalleNombre); + TextView tvTipo = findViewById(R.id.tvDetalleTipo); + TextView tvDescripcion = findViewById(R.id.tvDetalleDescripcion); + TextView tvCocina = findViewById(R.id.tvDetalleCocina); + View layoutExtras = findViewById(R.id.layoutExtras); + MaterialButton btnLlamar = findViewById(R.id.btnLlamar); + MaterialButton btnComoLlegar = findViewById(R.id.btnComoLlegar); + + tvNombre.setText(nombre); + tvTipo.setText(tipo); + tvDescripcion.setText(descripcion != null && !descripcion.isEmpty() ? descripcion : getString(R.string.msg_no_description)); + + if (tieneCocina) { + layoutExtras.setVisibility(View.VISIBLE); + tvCocina.setVisibility(View.VISIBLE); + } + + if (telefono == null || telefono.isEmpty() || telefono.equals("Sin teléfono")) { + btnLlamar.setVisibility(View.GONE); + } else { + btnLlamar.setOnClickListener(v -> { + Intent intent = new Intent(Intent.ACTION_DIAL); + intent.setData(Uri.parse("tel:" + telefono)); + startActivity(intent); + }); + } + + btnComoLlegar.setOnClickListener(v -> { + String uri = String.format(java.util.Locale.US, "google.navigation:q=%f,%f&mode=w", latitud, longitud); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri)); + intent.setPackage("com.google.android.apps.maps"); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } else { + // Fallback a navegador si no hay Google Maps + String webUri = String.format(java.util.Locale.US, "https://www.google.com/maps/dir/?api=1&destination=%f,%f&travelmode=walking", latitud, longitud); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(webUri))); + } + }); + } +} diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/Etapa.java b/app/src/main/java/es/tatvil/elfarodelperegrino/Etapa.java index 90ed31f..651fa48 100644 --- a/app/src/main/java/es/tatvil/elfarodelperegrino/Etapa.java +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/Etapa.java @@ -1,6 +1,8 @@ package es.tatvil.elfarodelperegrino; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; public class Etapa implements Serializable { private String nombre; @@ -14,6 +16,9 @@ public class Etapa implements Serializable { private String hitoHistorico; private double latitud, longitud; private String imagenUrl; + private int desnivelPositivo; + private int desnivelNegativo; + private List perfilElevacion; public Etapa(String nombre, String origen, String destino, String ruta, String camino, double distancia, int dificultad, String reflexionEspiritual, String hitoHistorico, @@ -30,11 +35,29 @@ public class Etapa implements Serializable { this.latitud = latitud; this.longitud = longitud; this.imagenUrl = "https://picsum.photos/seed/" + nombre.replace(" ", "") + "/800/400"; + // Valores por defecto para el ejemplo + this.desnivelPositivo = (int) (distancia * 15); + this.desnivelNegativo = (int) (distancia * 12); + this.perfilElevacion = generarPerfilMock(distancia); + } + + private List generarPerfilMock(double dist) { + List perfil = new ArrayList<>(); + int base = 100 + (int)(Math.random() * 200); + for (int i = 0; i <= 10; i++) { + perfil.add(base + (int)(Math.random() * 150)); + } + return perfil; } // Getters - public String getImagenUrl() { return imagenUrl; } + public List getPerfilElevacion() { return perfilElevacion; } + public int getDesnivelPositivo() { return desnivelPositivo; } + public int getDesnivelNegativo() { return desnivelNegativo; } + public void setDesnivelPositivo(int desnivelPositivo) { this.desnivelPositivo = desnivelPositivo; } + public void setDesnivelNegativo(int desnivelNegativo) { this.desnivelNegativo = desnivelNegativo; } public void setImagenUrl(String imagenUrl) { this.imagenUrl = imagenUrl; } + public String getImagenUrl() { return imagenUrl; } public String getNombre() { return nombre; } public String getOrigen() { return origen; } public String getDestino() { return destino; } diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/EtapasActivity.java b/app/src/main/java/es/tatvil/elfarodelperegrino/EtapasActivity.java new file mode 100644 index 0000000..9b9fde9 --- /dev/null +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/EtapasActivity.java @@ -0,0 +1,115 @@ +package es.tatvil.elfarodelperegrino; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationServices; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class EtapasActivity extends AppCompatActivity { + + private FusedLocationProviderClient fusedLocationClient; + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 1. SIEMPRE debes llamar al super y establecer la vista + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_etapas); // Esto vincula el XML activity_etapas + + // 2. Ahora sí, inicializamos el RecyclerView después de que la vista exista + RecyclerView recyclerView = findViewById(R.id.recyclerViewEtapas); + + // 3. Configuración del LayoutManager + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + + // 4. Configuración del Spinner para seleccionar el camino + Spinner spinnerCaminos = findViewById(R.id.spinnerCaminos); + String[] opcionesCaminos = {"Norte", "Francés"}; + + ArrayAdapter adapterCaminos = new ArrayAdapter<>(this, + android.R.layout.simple_spinner_item, opcionesCaminos); + adapterCaminos.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerCaminos.setAdapter(adapterCaminos); + + // 5. Configuración del Spinner para distancia máxima + Spinner spinnerDistancia = findViewById(R.id.spinnerDistancia); + String[] opcionesDistancia = {"Sin límite", "15 km", "20 km", "25 km"}; + ArrayAdapter adapterDistancia = new ArrayAdapter<>(this, + android.R.layout.simple_spinner_item, opcionesDistancia); + adapterDistancia.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerDistancia.setAdapter(adapterDistancia); + + AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + String camino = spinnerCaminos.getSelectedItem().toString(); + String distStr = spinnerDistancia.getSelectedItem().toString(); + double maxDist = Double.MAX_VALUE; + if (!distStr.equals("Sin límite")) { + maxDist = Double.parseDouble(distStr.split(" ")[0]); + } + actualizarListaEtapas(camino, maxDist, recyclerView); + } + + @Override + public void onNothingSelected(AdapterView parent) {} + }; + + spinnerCaminos.setOnItemSelectedListener(listener); + spinnerDistancia.setOnItemSelectedListener(listener); + + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); + obtenerUbicacionActual(); + } + + private void actualizarListaEtapas(String camino, double maxDistancia, RecyclerView recyclerView) { + List todas = CaminoData.getEtapasPorCamino(this, camino); + List filtradas = new ArrayList<>(); + for (Etapa e : todas) { + if (e.getDistancia() <= maxDistancia) { + filtradas.add(e); + } + } + EtapaAdapter adapter = new EtapaAdapter(filtradas); + recyclerView.setAdapter(adapter); + } + + private void obtenerUbicacionActual() { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + fusedLocationClient.getLastLocation().addOnSuccessListener(this, location -> { + if (location != null) { + // Futura implementación: resaltar etapa más cercana + } + }); + } else { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == 1) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + obtenerUbicacionActual(); + } + } + } +} diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity.java b/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity.java index e13c801..4fd8036 100644 --- a/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity.java +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity.java @@ -1,174 +1,46 @@ + package es.tatvil.elfarodelperegrino; -import android.Manifest; -import android.content.pm.PackageManager; +import android.content.Intent; import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Spinner; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.gms.location.FusedLocationProviderClient; -import com.google.android.gms.location.LocationServices; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; +import android.view.View; +import android.widget.Toast; public class MainActivity extends AppCompatActivity { - private FusedLocationProviderClient fusedLocationClient; - @Override protected void onCreate(Bundle savedInstanceState) { - // 1. SIEMPRE debes llamar al super y establecer la vista super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); // Esto vincula el XML activity_main + setContentView(R.layout.activity_main); - // 2. Ahora sí, inicializamos el RecyclerView después de que la vista exista - RecyclerView recyclerView = findViewById(R.id.recyclerViewEtapas); - - // 3. Configuración del LayoutManager - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - - // 4. Configuración del Spinner para seleccionar el camino - Spinner spinner = findViewById(R.id.spinnerCaminos); - String[] opcionesCaminos = {"Norte", "Francés"}; - - ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, - android.R.layout.simple_spinner_item, opcionesCaminos); - spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(spinnerAdapter); - - spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - String caminoSeleccionado = opcionesCaminos[position]; - actualizarListaEtapas(caminoSeleccionado, recyclerView); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } + // --- SECCIÓN RUTA --- + findViewById(R.id.btnMap).setOnClickListener(v -> { + Intent intent = new Intent(MainActivity.this, EtapasActivity.class); + startActivity(intent); }); - fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); - obtenerUbicacionActual(); + // --- SECCIÓN SERVICIOS --- + findViewById(R.id.btnSleep).setOnClickListener(v -> { + Toast.makeText(this, "Funcionalidad de Alojamiento próximamente", Toast.LENGTH_SHORT).show(); + }); + + findViewById(R.id.btnFood).setOnClickListener(v -> { + Toast.makeText(this, "Funcionalidad de Restaurantes próximamente", Toast.LENGTH_SHORT).show(); + }); + + // --- SECCIÓN MI CAMINO --- + findViewById(R.id.btnDiary).setOnClickListener(v -> { + Toast.makeText(this, "Funcionalidad de Diario próximamente", Toast.LENGTH_SHORT).show(); + }); + + findViewById(R.id.btnRegister).setOnClickListener(v -> { + Toast.makeText(this, "Funcionalidad de Sellar Credencial próximamente", Toast.LENGTH_SHORT).show(); + }); + + findViewById(R.id.btnTraining).setOnClickListener(v -> { + Toast.makeText(this, "Funcionalidad de Entrenamiento próximamente", Toast.LENGTH_SHORT).show(); + }); } - - private void actualizarListaEtapas(String camino, RecyclerView recyclerView) { - List etapas = CaminoData.getEtapasPorCamino(this, camino); - EtapaAdapter adapter = new EtapaAdapter(etapas); - recyclerView.setAdapter(adapter); - } - - private void obtenerUbicacionActual() { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - fusedLocationClient.getLastLocation().addOnSuccessListener(this, location -> { - if (location != null) { - double miLat = location.getLatitude(); - double miLon = location.getLongitude(); - // Aquí se compararía con las etapas - } - }); - } else { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == 1) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - obtenerUbicacionActual(); - } - } - } - - private List cargarEtapasDesdeJSON() { - List lista = new ArrayList<>(); - try { - InputStream is = getResources().openRawResource(R.raw.etapas); - byte[] buffer = new byte[is.available()]; - is.read(buffer); - is.close(); - String json = new String(buffer, "UTF-8"); - - JSONArray jsonArray = new JSONArray(json); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject obj = jsonArray.getJSONObject(i); - lista.add(new Etapa( - obj.getString("nombre"), - obj.optString("origen", ""), - obj.optString("destino", ""), - obj.optString("ruta", obj.optString("origen") + " - " + obj.optString("destino")), - obj.optString("camino", "Norte"), - obj.getDouble("distancia"), - obj.optInt("dificultad", 0), - obj.optString("reflexionEspiritual", obj.optString("reflexion", "")), - obj.optString("hitoHistorico", obj.optString("historia", "")), - obj.getDouble("latitud"), - obj.getDouble("longitud") - )); - } - } catch (Exception e) { - e.printStackTrace(); - } - return lista; - } - - private List leerEtapasDesdeRaw() { - List lista = new ArrayList<>(); - try { - // Abrimos el archivo etapas.json que debes crear en res/raw - InputStream is = getResources().openRawResource(R.raw.etapas); - int size = is.available(); - byte[] buffer = new byte[size]; - is.read(buffer); - is.close(); - - String jsonString = new String(buffer, "UTF-8"); - JSONArray jsonArray = new JSONArray(jsonString); - - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject obj = jsonArray.getJSONObject(i); - lista.add(new Etapa( - obj.getString("nombre"), - obj.optString("origen", ""), - obj.optString("destino", ""), - obj.optString("ruta", obj.optString("origen") + " - " + obj.optString("destino")), - obj.optString("camino", "Norte"), - obj.getDouble("distancia"), - obj.optInt("dificultad", 0), - obj.optString("reflexionEspiritual", obj.optString("reflexion", "")), - obj.optString("hitoHistorico", obj.optString("historia", "")), - obj.getDouble("latitud"), - obj.getDouble("longitud") - )); - } - } catch (Exception e) { - e.printStackTrace(); - } - return lista; - } - - // Método para calcular distancia entre dos puntos (Fórmula de Haversine simplificada) - public double calcularDistancia(double lat1, double lon1, double lat2, double lon2) { - double theta = lon1 - lon2; - double dist = Math.sin(Math.toRadians(lat1)) * Math.sin(Math.toRadians(lat2)) + - Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * - Math.cos(Math.toRadians(theta)); - dist = Math.acos(dist); - dist = Math.toDegrees(dist); - return dist * 60 * 1.1515 * 1.609344; // Retorna Kilómetros - } -} \ No newline at end of file +} + \ No newline at end of file diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity2.class b/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity2.class deleted file mode 100644 index 131d15f..0000000 --- a/app/src/main/java/es/tatvil/elfarodelperegrino/MainActivity2.class +++ /dev/null @@ -1,22 +0,0 @@ -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - // Aquí conectas los botones - findViewById(R.id.btnTraining).setOnClickListener(v -> { - // TODO: abrir pantalla de entrenamiento - }); - - findViewById(R.id.btnDiary).setOnClickListener(v -> { - // TODO: abrir pantalla del diario - }); - - // ... y así con los demás - } -} - }) - }) - \ No newline at end of file diff --git a/app/src/main/java/es/tatvil/elfarodelperegrino/SimpleServicioAdapter.java b/app/src/main/java/es/tatvil/elfarodelperegrino/SimpleServicioAdapter.java new file mode 100644 index 0000000..5391b86 --- /dev/null +++ b/app/src/main/java/es/tatvil/elfarodelperegrino/SimpleServicioAdapter.java @@ -0,0 +1,85 @@ +package es.tatvil.elfarodelperegrino; + +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import java.io.Serializable; +import java.util.List; +import java.util.Locale; + +public class SimpleServicioAdapter extends RecyclerView.Adapter { + + private List items; + private double refLat, refLon; + + public SimpleServicioAdapter(List items, double refLat, double refLon) { + this.items = items; + this.refLat = refLat; + this.refLon = refLon; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_albergue, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Object item = items.get(position); + String nombre = ""; + double lat = 0, lon = 0; + String tipo = ""; + + if (item instanceof Iglesia) { + Iglesia i = (Iglesia) item; + nombre = i.getNombre(); + lat = i.getLatitud(); + lon = i.getLongitud(); + tipo = "Iglesia"; + } else if (item instanceof Restaurante) { + Restaurante r = (Restaurante) item; + nombre = r.getNombre(); + lat = r.getLatitud(); + lon = r.getLongitud(); + tipo = "Restaurante"; + } + + holder.tvNombre.setText(nombre); + holder.tvTipo.setText(tipo); + holder.tvTelefono.setVisibility(View.GONE); + + float[] results = new float[1]; + android.location.Location.distanceBetween(refLat, refLon, lat, lon, results); + float distanciaKm = results[0] / 1000; + holder.tvDistancia.setText(String.format(Locale.getDefault(), "A %.1f km", distanciaKm)); + + holder.itemView.setOnClickListener(v -> { + Intent intent = new Intent(v.getContext(), DetalleServicioActivity.class); + intent.putExtra("objeto", (Serializable) item); + v.getContext().startActivity(intent); + }); + } + + @Override + public int getItemCount() { + return items.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + TextView tvNombre, tvTipo, tvDistancia, tvTelefono; + public ViewHolder(@NonNull View itemView) { + super(itemView); + tvNombre = itemView.findViewById(R.id.tvAlbergueNombre); + tvTipo = itemView.findViewById(R.id.tvAlbergueTipo); + tvDistancia = itemView.findViewById(R.id.tvAlbergueDistancia); + tvTelefono = itemView.findViewById(R.id.tvAlbergueTelefono); + } + } +} diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9..c3ff9d1 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,25 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:pathData="M54,54m-40,0a40,40 0,1 1,80 0a40,40 0,1 1,-80 0"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 2b068d1..e3242a4 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -4,27 +4,62 @@ android:height="108dp" android:viewportWidth="108" android:viewportHeight="108"> - + + + + + + + + + + + - - + + + + + android:pathData="M60,30 L93,15 L93,45 Z"> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/Activity-main2.xml b/app/src/main/res/layout/Activity-main2.xml deleted file mode 100644 index d59d819..0000000 --- a/app/src/main/res/layout/Activity-main2.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -