Compare commits

...

2 Commits

Author SHA1 Message Date
Tatiana Villa Ema 7c048d7a9d Merge branch 'main' of https://git.tatvil.es/tatiana/el-faro-del-peregrino 2026-05-02 09:42:26 +02:00
Tatiana Villa Ema d96858d3c2 Estructura principal de la app 2026-05-02 02:00:58 +02:00
22 changed files with 1323 additions and 669 deletions

147
README.md
View File

@ -1,109 +1,104 @@
# El Faro del Peregrino # 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 ## ✨ Propósito
- Crear una app que acompañe el crecimiento espiritual personal.
- Integrar recursos como oraciones, meditaciones, lecturas y seguimiento del camino interior. 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.
- 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
--- ---
## Tecnologías utilizadas ## 🎯 Objetivos
Este proyecto está construido con:
- Java / Kotlin (según evolucione el código del módulo app/) - Acompañar el crecimiento espiritual personal y comunitario.
- Gradle como sistema de construcción - Integrar oraciones, meditaciones, lecturas y seguimiento del camino interior.
- Android Studio como entorno de desarrollo - Ofrecer una interfaz estética, accesible y acogedora.
- Estructura modular preparada para escalar - 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/ el-faro-del-peregrino/
├── app/ # Código principal de la app
├── app/ ├── gradle/ # Configuración de Gradle
├── gradle/ ├── build.gradle.kts # Build script raíz
├── build.gradle.kts ├── settings.gradle.kts # Configuración de módulos
├── settings.gradle.kts ├── gradle.properties # Propiedades globales
├── gradle.properties └── .idea/ # Configuración de Android Studio
└── .idea/ ```
`
--- ---
## Cómo ejecutar el proyecto ## 🚀 Cómo ejecutar el proyecto
1. Requisitos previos 1. **Requisitos previos**
- Android Studio Flamingo o superior - Android Studio Flamingo o superior
- JDK 17+ - JDK 17+
- Gradle (incluido en el wrapper) - Gradle (incluido en el wrapper)
2. Clonar el repositorio 2. **Clonar el repositorio**
`bash ```bash
git clone https://git.tatvil.es/tatiana/el-faro-del-peregrino.git git clone https://git.tatvil.es/tatiana/el-faro-del-peregrino.git
cd el-faro-del-peregrino cd el-faro-del-peregrino
` ```
3. Abrir en Android Studio 3. **Abrir en Android Studio**
- Selecciona Open - Selecciona *Open*
- Elige la carpeta del proyecto - Elige la carpeta del proyecto
- Android Studio descargará dependencias y sincronizará Gradle automáticamente - Android Studio descargará dependencias y sincronizará Gradle automáticamente
4. Ejecutar la app 4. **Ejecutar la app**
- Conecta un dispositivo o usa un emulador - Conecta un dispositivo o usa un emulador
- Pulsa ▶️ Run - Pulsa ▶️ *Run*
--- ---
🗺️ Roadmap inicial ## 🗺️ Roadmap
Este roadmap está alineado con el estilo profesional que ya usas en GitHub Issues.
Versión 0.1 — Estructura base **Versión 0.1 — Estructura base**
- [ ] Configuración inicial del proyecto - [ ] Configuración inicial del proyecto
- [ ] Pantalla de bienvenida - [ ] Pantalla de bienvenida
- [ ] Navegación básica - [ ] Navegación básica
- [ ] Tema visual inicial (colores, tipografías, iconografía) - [ ] Tema visual inicial (colores, tipografías, iconografía)
Versión 0.2 — Contenidos espirituales **Versión 0.2 — Contenidos espirituales**
- [ ] Módulo de oraciones - [ ] Módulo de oraciones
- [ ] Lecturas del día - [ ] Lecturas del día
- [ ] Modo oscuro contemplativo - [ ] Modo oscuro contemplativo
Versión 0.3 — Herramientas del peregrino **Versión 0.3 — Herramientas del peregrino**
- [ ] Diario espiritual - [ ] Diario espiritual
- [ ] Seguimiento del camino - [ ] Seguimiento del camino
- [ ] Notificaciones de ritmo diario - [ ] Notificaciones de ritmo diario
Versión 1.0 — Comunidad **Versión 1.0 — Comunidad**
- [ ] Perfil del peregrino - [ ] Perfil del peregrino
- [ ] Espacios de oración compartida - [ ] Espacios de oración compartida
- [ ] Mensajes y peticiones - [ ] Mensajes y peticiones
--- ---
🤝 Contribuciones ## 🌅 Inspiración
Este proyecto está en desarrollo activo.
Las ideas, mejoras y bugs se gestionan mediante GitHub Issues siguiendo un flujo profesional.
--- > “El faro no navega por ti, pero ilumina el camino.”
📜 Licencia Este proyecto busca ofrecer luz, calma y dirección en el viaje interior.
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.

View File

@ -46,5 +46,6 @@ dependencies {
implementation("com.google.android.gms:play-services-location:21.3.0") implementation("com.google.android.gms:play-services-location:21.3.0")
implementation(libs.volley) implementation(libs.volley)
implementation(libs.glide) implementation(libs.glide)
implementation(libs.mpandroidchart)
annotationProcessor(libs.glide.compiler) annotationProcessor(libs.glide.compiler)
} }

View File

@ -7,6 +7,13 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<queries>
<package android:name="com.google.android.apps.maps" />
<intent>
<action android:name="android.intent.action.DIAL" />
</intent>
</queries>
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
@ -31,6 +38,16 @@
android:label="Detalle de la Etapa" android:label="Detalle de la Etapa"
android:parentActivityName=".MainActivity"> android:parentActivityName=".MainActivity">
</activity> </activity>
<activity
android:name=".EtapasActivity"
android:label="Etapas del Camino"
android:parentActivityName=".MainActivity">
</activity>
<activity
android:name=".DetalleServicioActivity"
android:label="Detalle del Servicio"
android:parentActivityName=".DetalleEtapaActivity">
</activity>
</application> </application>
</manifest> </manifest>

View File

@ -1,5 +1,6 @@
package es.tatvil.elfarodelperegrino; package es.tatvil.elfarodelperegrino;
import android.content.Intent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -40,6 +41,12 @@ public class AlbergueAdapter extends RecyclerView.Adapter<AlbergueAdapter.Alberg
float distanciaKm = results[0] / 1000; float distanciaKm = results[0] / 1000;
holder.tvDistancia.setText(String.format(Locale.getDefault(), "A %.1f km del destino", distanciaKm)); holder.tvDistancia.setText(String.format(Locale.getDefault(), "A %.1f km del destino", distanciaKm));
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(v.getContext(), DetalleServicioActivity.class);
intent.putExtra("objeto", albergue);
v.getContext().startActivity(intent);
});
} }
@Override @Override

View File

@ -1,10 +1,13 @@
package es.tatvil.elfarodelperegrino; package es.tatvil.elfarodelperegrino;
import android.graphics.Color;
import android.location.Location; import android.location.Location;
import android.os.Bundle; import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; 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.JsonObjectRequest;
import com.android.volley.toolbox.Volley; import com.android.volley.toolbox.Volley;
import com.bumptech.glide.Glide; 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.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
public class DetalleEtapaActivity extends AppCompatActivity { public class DetalleEtapaActivity extends AppCompatActivity {
@ -39,24 +50,34 @@ public class DetalleEtapaActivity extends AppCompatActivity {
ImageView ivImagen = findViewById(R.id.ivDetalleImagen); ImageView ivImagen = findViewById(R.id.ivDetalleImagen);
TextView tvTitulo = findViewById(R.id.tvDetalleTitulo); TextView tvTitulo = findViewById(R.id.tvDetalleTitulo);
TextView tvDistancia = findViewById(R.id.tvDetalleDistancia); 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 tvHistoria = findViewById(R.id.tvDetalleHistoria);
TextView tvReflexion = findViewById(R.id.tvDetalleReflexion); TextView tvReflexion = findViewById(R.id.tvDetalleReflexion);
TextView tvClima = findViewById(R.id.tvDetalleClima); TextView tvClima = findViewById(R.id.tvDetalleClima);
tvTitulo.setText(etapa.getOrigen() + " - " + etapa.getDestino()); 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()); tvHistoria.setText(etapa.getHitoHistorico());
tvReflexion.setText(etapa.getReflexionEspiritual()); tvReflexion.setText(etapa.getReflexionEspiritual());
configurarGrafico(findViewById(R.id.elevationChart), etapa);
Glide.with(this) Glide.with(this)
.load(etapa.getImagenUrl()) .load(etapa.getImagenUrl())
.placeholder(android.R.drawable.ic_menu_gallery) .placeholder(android.R.drawable.ic_menu_gallery)
.into(ivImagen); .into(ivImagen);
obtenerClima(etapa.getLatitud(), etapa.getLongitud(), tvClima); obtenerClimaYPrevision(etapa.getLatitud(), etapa.getLongitud(), tvClima, findViewById(R.id.layoutForecast));
findViewById(R.id.btnVerClima).setOnClickListener(v -> // Botón Mapa
obtenerClima(etapa.getLatitud(), etapa.getLongitud(), tvClima)); 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 // Botón Albergues
findViewById(R.id.btnVerAlbergues).setOnClickListener(v -> { findViewById(R.id.btnVerAlbergues).setOnClickListener(v -> {
@ -76,7 +97,7 @@ public class DetalleEtapaActivity extends AppCompatActivity {
if (cercanas.isEmpty()) { if (cercanas.isEmpty()) {
Toast.makeText(this, "No hay iglesias registradas cerca", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "No hay iglesias registradas cerca", Toast.LENGTH_SHORT).show();
} else { } else {
mostrarDialogoSimple("Iglesias cercanas", cercanas); mostrarDialogoServicios("Iglesias cercanas", cercanas, etapa);
} }
}); });
@ -92,7 +113,7 @@ public class DetalleEtapaActivity extends AppCompatActivity {
if (cercanos.isEmpty()) { if (cercanos.isEmpty()) {
Toast.makeText(this, "No hay restaurantes registrados cerca", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "No hay restaurantes registrados cerca", Toast.LENGTH_SHORT).show();
} else { } else {
mostrarDialogoSimple("Restaurantes cercanos", cercanos); mostrarDialogoServicios("Restaurantes cercanos", cercanos, etapa);
} }
}); });
} }
@ -126,18 +147,17 @@ public class DetalleEtapaActivity extends AppCompatActivity {
.show(); .show();
} }
private void mostrarDialogoSimple(String titulo, List<?> items) { private void mostrarDialogoServicios(String titulo, List<?> items, Etapa etapa) {
StringBuilder sb = new StringBuilder(); AlertDialog.Builder builder = new AlertDialog.Builder(this);
for (Object item : items) { View view = LayoutInflater.from(this).inflate(R.layout.dialog_lista_albergues, null);
if (item instanceof Iglesia) sb.append("").append(((Iglesia) item).getNombre()).append("\n"); RecyclerView rv = view.findViewById(R.id.rvDialogo);
if (item instanceof Restaurante) sb.append("").append(((Restaurante) item).getNombre()).append("\n"); rv.setLayoutManager(new LinearLayoutManager(this));
} rv.setAdapter(new SimpleServicioAdapter(items, etapa.getLatitud(), etapa.getLongitud()));
new AlertDialog.Builder(this) builder.setTitle(titulo)
.setTitle(titulo) .setView(view)
.setMessage(sb.toString()) .setPositiveButton("Cerrar", null)
.setPositiveButton("OK", null) .show();
.show();
} }
private double calcularDistancia(double lat1, double lon1, double lat2, double lon2) { 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 return results[0] / 1000; // Retorna km
} }
private void obtenerClima(double lat, double lon, TextView tvClima) { private void configurarGrafico(LineChart chart, Etapa etapa) {
String url = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&appid=" + API_KEY + "&units=metric&lang=es"; List<Entry> entries = new ArrayList<>();
List<Integer> 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); 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 -> { response -> {
try { try {
JSONObject main = response.getJSONObject("main"); JSONObject main = response.getJSONObject("main");
double temp = main.getDouble("temp"); double temp = main.getDouble("temp");
String desc = response.getJSONArray("weather").getJSONObject(0).getString("description"); 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) { } catch (JSONException e) {
tvClima.setText("Error al procesar clima"); tvClima.setText("Error en clima actual");
} }
}, }, error -> tvClima.setText("Error al conectar con clima"));
error -> tvClima.setText("Error al obtener clima"));
queue.add(request); // 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);
} }
} }

View File

@ -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)));
}
});
}
}

View File

@ -1,6 +1,8 @@
package es.tatvil.elfarodelperegrino; package es.tatvil.elfarodelperegrino;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class Etapa implements Serializable { public class Etapa implements Serializable {
private String nombre; private String nombre;
@ -14,6 +16,9 @@ public class Etapa implements Serializable {
private String hitoHistorico; private String hitoHistorico;
private double latitud, longitud; private double latitud, longitud;
private String imagenUrl; private String imagenUrl;
private int desnivelPositivo;
private int desnivelNegativo;
private List<Integer> perfilElevacion;
public Etapa(String nombre, String origen, String destino, String ruta, String camino, double distancia, public Etapa(String nombre, String origen, String destino, String ruta, String camino, double distancia,
int dificultad, String reflexionEspiritual, String hitoHistorico, int dificultad, String reflexionEspiritual, String hitoHistorico,
@ -30,11 +35,29 @@ public class Etapa implements Serializable {
this.latitud = latitud; this.latitud = latitud;
this.longitud = longitud; this.longitud = longitud;
this.imagenUrl = "https://picsum.photos/seed/" + nombre.replace(" ", "") + "/800/400"; 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<Integer> generarPerfilMock(double dist) {
List<Integer> 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 // Getters
public String getImagenUrl() { return imagenUrl; } public List<Integer> 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 void setImagenUrl(String imagenUrl) { this.imagenUrl = imagenUrl; }
public String getImagenUrl() { return imagenUrl; }
public String getNombre() { return nombre; } public String getNombre() { return nombre; }
public String getOrigen() { return origen; } public String getOrigen() { return origen; }
public String getDestino() { return destino; } public String getDestino() { return destino; }

View File

@ -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 , 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<String> 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<String> 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<Etapa> todas = CaminoData.getEtapasPorCamino(this, camino);
List<Etapa> 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();
}
}
}
}

View File

@ -1,174 +1,46 @@
package es.tatvil.elfarodelperegrino; package es.tatvil.elfarodelperegrino;
import android.Manifest; import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle; 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.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat; import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager; import android.widget.Toast;
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 MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private FusedLocationProviderClient fusedLocationClient;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
// 1. SIEMPRE debes llamar al super y establecer la vista
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // Esto vincula el XML activity_main setContentView(R.layout.activity_main);
// 2. Ahora , inicializamos el RecyclerView después de que la vista exista // --- SECCIÓN RUTA ---
RecyclerView recyclerView = findViewById(R.id.recyclerViewEtapas); findViewById(R.id.btnMap).setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, EtapasActivity.class);
// 3. Configuración del LayoutManager startActivity(intent);
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<String> 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) {
}
}); });
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // --- SECCIÓN SERVICIOS ---
obtenerUbicacionActual(); 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<Etapa> 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<Etapa> cargarEtapasDesdeJSON() {
List<Etapa> 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<Etapa> leerEtapasDesdeRaw() {
List<Etapa> 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
}
}

View File

@ -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
}
}
})
})

View File

@ -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<SimpleServicioAdapter.ViewHolder> {
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);
}
}
}

View File

@ -1,170 +1,25 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<path <path
android:fillColor="#3DDC84" android:fillColor="#003366"
android:pathData="M0,0h108v108h-108z" /> android:pathData="M0,0h108v108h-108z" />
<!-- Subtle circular glow in the center -->
<path <path
android:fillColor="#00000000" android:pathData="M54,54m-40,0a40,40 0,1 1,80 0a40,40 0,1 1,-80 0">
android:pathData="M9,0L9,108" <aapt:attr name="android:fillColor">
android:strokeWidth="0.8" <gradient
android:strokeColor="#33FFFFFF" /> android:centerX="54"
<path android:centerY="54"
android:fillColor="#00000000" android:gradientRadius="45"
android:pathData="M19,0L19,108" android:type="radial">
android:strokeWidth="0.8" <item android:color="#1AFFFFFF" android:offset="0.0" />
android:strokeColor="#33FFFFFF" /> <item android:color="#00FFFFFF" android:offset="1.0" />
<path </gradient>
android:fillColor="#00000000" </aapt:attr>
android:pathData="M29,0L29,108" </path>
android:strokeWidth="0.8" </vector>
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -4,27 +4,62 @@
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<!-- Lighthouse Body -->
<path
android:pathData="M44,85 L64,85 L60,35 L48,35 Z"
android:fillColor="#E0E0E0" />
<path
android:pathData="M46,55 L62,55 L61,65 L47,65 Z"
android:fillColor="#B71C1C" /> <!-- Red stripe -->
<!-- Top Lantern Room -->
<path
android:pathData="M46,35 L62,35 L62,25 L46,25 Z"
android:fillColor="#333333" />
<path
android:pathData="M48,25 L60,25 L54,18 Z"
android:fillColor="#333333" />
<!-- Light Beam (Left) -->
<path
android:pathData="M48,30 L15,15 L15,45 Z">
<aapt:attr name="android:fillColor"> <aapt:attr name="android:fillColor">
<gradient <gradient
android:endX="85.84757" android:startX="48"
android:endY="92.4963" android:startY="30"
android:startX="42.9492" android:endX="15"
android:startY="49.59793" android:endY="30"
android:type="linear"> android:type="linear">
<item <item android:color="#B3FFD700" android:offset="0.0" />
android:color="#44000000" <item android:color="#00FFD700" android:offset="1.0" />
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient> </gradient>
</aapt:attr> </aapt:attr>
</path> </path>
<!-- Light Beam (Right) -->
<path <path
android:fillColor="#FFFFFF" android:pathData="M60,30 L93,15 L93,45 Z">
android:fillType="nonZero" <aapt:attr name="android:fillColor">
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" <gradient
android:strokeWidth="1" android:startX="60"
android:strokeColor="#00000000" /> android:startY="30"
android:endX="93"
android:endY="30"
android:type="linear">
<item android:color="#B3FFD700" android:offset="0.0" />
<item android:color="#00FFD700" android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<!-- Scallop Shell (Concha de Santiago) at the bottom -->
<path
android:pathData="M54,88 C48,88 44,92 44,96 C44,101 48,104 54,104 C60,104 64,101 64,96 C64,92 60,88 54,88 Z"
android:fillColor="#FFD700" />
<!-- Ridges on the shell -->
<path android:pathData="M54,90 L54,102" android:strokeColor="#B29600" android:strokeWidth="1" />
<path android:pathData="M50,91 L52,101" android:strokeColor="#B29600" android:strokeWidth="1" />
<path android:pathData="M58,91 L56,101" android:strokeColor="#B29600" android:strokeWidth="1" />
</vector> </vector>

View File

@ -1,145 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Saludo -->
<TextView
android:id="@+id/txtGreeting"
android:text="Buen Camino, Tatiana"
android:textSize="24sp"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- Fecha -->
<TextView
android:id="@+id/txtDate"
android:text="Sábado, 2 de mayo de 2026"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- Tarjeta del tiempo -->
<LinearLayout
android:orientation="vertical"
android:padding="16dp"
android:background="#1E88E5"
android:layout_marginTop="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtLocation"
android:text="📍 Las Rozas de Madrid"
android:textColor="@android:color/white"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txtWeatherToday"
android:text="Hoy: 11ºC - 19ºC · 60%"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txtWeatherTomorrow"
android:text="Mañana: 14ºC - 20ºC · 17%"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<!-- Grid de botones -->
<GridLayout
android:id="@+id/gridButtons"
android:columnCount="2"
android:rowCount="4"
android:layout_marginTop="24dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Cada botón -->
<Button
android:id="@+id/btnTraining"
android:text="Entrenamiento"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnMap"
android:text="Mapa y etapas"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnFood"
android:text="Dónde comer"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnSleep"
android:text="Dónde dormir"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnServices"
android:text="Servicios útiles"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnRegister"
android:text="Registrar paso"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnDiary"
android:text="Mi diario"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
<Button
android:id="@+id/btnMessages"
android:text="Mensajes del camino"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"/>
</GridLayout>
<!-- Frase final -->
<TextView
android:id="@+id/txtQuote"
android:text="El camino me enseñó que el verdadero lujo es el que nos dedicamos a nosotros mismos."
android:layout_marginTop="24dp"
android:textSize="14sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>

View File

@ -28,62 +28,155 @@
android:text="Nombre de la Etapa" android:text="Nombre de la Etapa"
android:layout_marginBottom="16dp"/> android:layout_marginBottom="16dp"/>
<TextView <LinearLayout
android:id="@+id/tvDetalleDistancia"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18sp" android:orientation="horizontal"
android:textColor="#BBBBBB" android:layout_marginBottom="16dp"
android:text="Distancia: 24 km" android:baselineAligned="false">
android:layout_marginBottom="24dp"/>
<TextView <LinearLayout
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Historia y Hitos" android:layout_weight="1"
android:textStyle="bold" android:orientation="vertical"
android:textColor="@color/text_primary" android:gravity="center">
android:layout_marginBottom="8dp"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Distancia"
android:textColor="#BBBBBB"
android:textSize="12sp"/>
<TextView
android:id="@+id/tvDetalleDistancia"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@color/text_primary"
android:text="24 km"/>
</LinearLayout>
<TextView <LinearLayout
android:id="@+id/tvDetalleHistoria" android:layout_width="0dp"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1"
android:textColor="@color/text_primary" android:orientation="vertical"
android:text="Cargando historia..." android:gravity="center">
android:layout_marginBottom="24dp"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Desnivel +"
android:textColor="#BBBBBB"
android:textSize="12sp"/>
<TextView
android:id="@+id/tvDetalleDesnivelPos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#4CAF50"
android:text="+450m"/>
</LinearLayout>
<TextView <LinearLayout
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Reflexión Espiritual" android:layout_weight="1"
android:textStyle="bold" android:orientation="vertical"
android:textColor="@color/text_primary" android:gravity="center">
android:layout_marginBottom="8dp"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Desnivel -"
android:textColor="#BBBBBB"
android:textSize="12sp"/>
<TextView
android:id="@+id/tvDetalleDesnivelNeg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#F44336"
android:text="-380m"/>
</LinearLayout>
</LinearLayout>
<TextView <androidx.cardview.widget.CardView
android:id="@+id/tvDetalleReflexion" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@color/text_primary" app:cardBackgroundColor="#222222"
android:text="Cargando reflexión..." app:cardCornerRadius="8dp"
android:layout_marginBottom="16dp"/> android:layout_marginBottom="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Perfil de Elevación"
android:textColor="@color/text_primary"
android:textStyle="bold"
android:layout_marginBottom="8dp"/>
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/elevationChart"
android:layout_width="match_parent"
android:layout_height="150dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView <androidx.cardview.widget.CardView
android:id="@+id/tvDetalleClima" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="16sp" app:cardBackgroundColor="#222222"
android:textColor="#FFEB3B" app:cardCornerRadius="8dp"
android:text="Clima: --" android:layout_marginBottom="24dp">
android:layout_marginBottom="24dp"/> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Previsión del Tiempo (Próximos días)"
android:textColor="@color/text_primary"
android:textStyle="bold"
android:layout_marginBottom="8dp"/>
<TextView
android:id="@+id/tvDetalleClima"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#FFEB3B"
android:text="Cargando clima actual..."/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#444444"
android:layout_marginVertical="8dp"/>
<LinearLayout
android:id="@+id/layoutForecast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<Button <Button
android:id="@+id/btnVerClima" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/btnVerMapa"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Ver Clima Actual" android:text="Ver en Mapa / GPS"
android:layout_marginBottom="8dp"/> android:backgroundTint="#1976D2"
android:layout_marginBottom="24dp"
app:icon="@android:drawable/ic_dialog_map"/>
<Button <Button
android:id="@+id/btnVerAlbergues" android:id="@+id/btnVerAlbergues"
@ -105,7 +198,45 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Restaurantes" android:text="Restaurantes"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/> style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_marginBottom="24dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Historia del lugar"
android:textColor="@color/text_primary"
android:textStyle="bold"
android:textSize="18sp"
android:layout_marginBottom="8dp"/>
<TextView
android:id="@+id/tvDetalleHistoria"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#CCCCCC"
android:textSize="14sp"
android:layout_marginBottom="16dp"
android:text="Cargando historia..."/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Reflexión Espiritual"
android:textColor="@color/text_primary"
android:textStyle="bold"
android:textSize="18sp"
android:layout_marginBottom="8dp"/>
<TextView
android:id="@+id/tvDetalleReflexion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#CCCCCC"
android:textSize="14sp"
android:fontStyle="italic"
android:layout_marginBottom="24dp"
android:text="Cargando reflexión..."/>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View File

@ -0,0 +1,117 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/spiritual_black">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="24dp"
app:strokeWidth="1dp"
app:strokeColor="@color/spiritual_gold">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:id="@+id/tvDetalleNombre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvDetalleTipo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/label_category"
android:textColor="@color/spiritual_gold"
android:textSize="16sp"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="@color/spiritual_text_dim"
android:alpha="0.3" />
<TextView
android:id="@+id/tvDetalleDescripcion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/msg_no_description"
android:textColor="@color/spiritual_text_bright"
android:textSize="16sp"
android:lineSpacingExtra="4dp" />
<LinearLayout
android:id="@+id/layoutExtras"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/tvDetalleCocina"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/msg_has_kitchen"
android:textColor="@color/accent_green"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnLlamar"
style="@style/Widget.MaterialComponents.Button.Icon"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="@string/action_call"
app:icon="@android:drawable/ic_menu_call"
app:cornerRadius="16dp"
android:backgroundTint="@color/maritime_blue_deep"
android:textColor="@color/white"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnComoLlegar"
style="@style/Widget.MaterialComponents.Button.Icon"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="@string/action_directions"
app:icon="@android:drawable/ic_menu_directions"
app:cornerRadius="16dp"
android:backgroundTint="@color/spiritual_surface"
app:strokeColor="@color/spiritual_gold"
app:strokeWidth="1dp"
android:textColor="@color/white" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,57 @@
<?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"
tools:context=".EtapasActivity">
<LinearLayout
android:id="@+id/headerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecciona el Camino:"
android:textColor="@color/text_primary"
android:textSize="14sp"
android:layout_marginStart="8dp"/>
<Spinner
android:id="@+id/spinnerCaminos"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="@android:drawable/btn_dropdown" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Distancia máxima por etapa:"
android:textColor="@color/text_primary"
android:textSize="14sp"
android:layout_marginStart="8dp"/>
<Spinner
android:id="@+id/spinnerDistancia"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="@android:drawable/btn_dropdown" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewEtapas"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/dark_background"
app:layout_constraintTop_toBottomOf="@id/headerLayout"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,28 +1,314 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> android:background="@color/spiritual_black"
android:fillViewport="true">
<Spinner <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/spinnerCaminos"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="@android:drawable/btn_dropdown"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewEtapas"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:background="@color/dark_background" android:paddingBottom="40dp">
app:layout_constraintTop_toBottomOf="@id/spinnerCaminos"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> <!-- Fondo de Cabecera -->
<View
android:id="@+id/headerBackground"
android:layout_width="match_parent"
android:layout_height="220dp"
android:background="@color/maritime_blue_deep"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txtGreeting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="56dp"
android:text="@string/welcome_message"
android:textColor="@color/white"
android:textSize="32sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- Tarjeta de Estado Flotante -->
<com.google.android.material.card.MaterialCardView
android:id="@+id/statusCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="-50dp"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="24dp"
app:cardElevation="10dp"
app:layout_constraintTop_toBottomOf="@id/headerBackground"
app:strokeWidth="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="20dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txtLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/location_sample"
android:textColor="@color/spiritual_gold"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txtWeatherToday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/weather_sample"
android:textColor="@color/white"
android:textSize="18sp" />
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="40dp"
android:layout_marginHorizontal="16dp"
android:background="@color/spiritual_text_dim" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/date_sample"
android:textColor="@color/spiritual_gold"
android:textSize="12sp"
android:textStyle="bold" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- SECCIÓN: RUTA (DESTACADA) -->
<TextView
android:id="@+id/labelRuta"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:text="@string/label_your_route"
android:letterSpacing="0.1"
android:textColor="@color/spiritual_text_dim"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/statusCard" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnMap"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="24dp"
app:layout_constraintTop_toBottomOf="@id/labelRuta"
app:strokeColor="@color/maritime_blue_mary"
app:strokeWidth="1dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/action_map"
android:textColor="@color/white"
android:textSize="22sp"
android:textStyle="bold" />
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentEnd="true"
android:alpha="0.1"
android:src="@android:drawable/ic_menu_mapmode"
app:tint="@color/white" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
<!-- SECCIÓN: SERVICIOS -->
<TextView
android:id="@+id/labelServicios"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:text="@string/label_services"
android:letterSpacing="0.1"
android:textColor="@color/spiritual_text_dim"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnMap" />
<LinearLayout
android:id="@+id/layoutServicios"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="18dp"
android:layout_marginTop="12dp"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/labelServicios">
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnSleep"
android:layout_width="0dp"
android:layout_height="110dp"
android:layout_margin="6dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/action_sleep"
android:textColor="@color/spiritual_text_bright"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnFood"
android:layout_width="0dp"
android:layout_height="110dp"
android:layout_margin="6dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/action_food"
android:textColor="@color/spiritual_text_bright"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<!-- SECCIÓN: MI CAMINO -->
<TextView
android:id="@+id/labelPersonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:text="@string/label_personal"
android:letterSpacing="0.1"
android:textColor="@color/spiritual_text_dim"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layoutServicios" />
<GridLayout
android:id="@+id/gridPersonal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="18dp"
android:layout_marginTop="8dp"
android:columnCount="3"
app:layout_constraintTop_toBottomOf="@id/labelPersonal">
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnDiary"
android:layout_width="0dp"
android:layout_height="90dp"
android:layout_columnWeight="1"
android:layout_margin="6dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/action_diary"
android:textColor="@color/spiritual_text_bright"
android:textSize="12sp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnRegister"
android:layout_width="0dp"
android:layout_height="90dp"
android:layout_columnWeight="1"
android:layout_margin="6dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/action_seal_short"
android:textColor="@color/spiritual_text_bright"
android:textSize="12sp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnTraining"
android:layout_width="0dp"
android:layout_height="90dp"
android:layout_columnWeight="1"
android:layout_margin="6dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/spiritual_surface"
app:cardCornerRadius="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/action_train_short"
android:textColor="@color/spiritual_text_bright"
android:textSize="12sp" />
</com.google.android.material.card.MaterialCardView>
</GridLayout>
<!-- FRASE FINAL -->
<TextView
android:id="@+id/txtQuote"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:layout_marginHorizontal="48dp"
android:gravity="center"
android:text="@string/quote_spiritual"
android:textColor="@color/spiritual_gold"
android:textSize="15sp"
android:alpha="0.8"
android:lineSpacingExtra="6dp"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gridPersonal" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -3,5 +3,11 @@
"nombre": "Pulperia A Garnacha", "nombre": "Pulperia A Garnacha",
"latitud": 42.91390034545274, "latitud": 42.91390034545274,
"longitud": -8.011660067243549 "longitud": -8.011660067243549
},
{
"nombre": "Bar Manuel",
"latitud": 42.931718288986296,
"longitud":-8.127124619996351
} }
] ]

View File

@ -1,3 +1,26 @@
<resources> <resources>
<string name="app_name">El Faro del Peregrino</string> <string name="app_name">El Faro del Peregrino</string>
<string name="welcome_message">Buen Camino,\nTatiana</string>
<string name="label_your_route">TU RUTA</string>
<string name="label_services">SERVICIOS</string>
<string name="label_personal">MI CAMINO</string>
<string name="action_map">Mapa interactivo y\nEtapas del Camino</string>
<string name="action_sleep">Dónde\nDormir</string>
<string name="action_food">Dónde\nComer</string>
<string name="action_diary">Mi\nDiario</string>
<string name="action_register">Sellar\nCredencial</string>
<string name="action_training">Preparación</string>
<string name="action_train_short">Entrenar</string>
<string name="action_seal_short">Sellar</string>
<string name="weather_sample">19ºC · Cielo despejado</string>
<string name="date_sample">2 Mayo\nSábado</string>
<string name="location_sample">📍 Las Rozas de Madrid</string>
<string name="quote_spiritual">\"El camino me enseñó que el verdadero lujo es el que nos dedicamos a nosotros mismos.\"</string>
<string name="label_category">Categoría</string>
<string name="action_call">Llamar</string>
<string name="action_directions">Cómo llegar</string>
<string name="msg_no_description">Sin descripción disponible.</string>
<string name="msg_has_kitchen">🍳 Dispone de cocina</string>
<string name="msg_interest_place">Lugar de interés espiritual y arquitectónico en el Camino.</string>
<string name="msg_food_place">Disfruta de la gastronomía local en este establecimiento.</string>
</resources> </resources>

View File

@ -9,6 +9,7 @@ activity = "1.13.0"
constraintlayout = "2.2.1" constraintlayout = "2.2.1"
volley = "1.2.1" volley = "1.2.1"
glide = "4.16.0" glide = "4.16.0"
mpandroidchart = "v3.1.0"
[libraries] [libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
@ -21,6 +22,7 @@ constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayo
volley = { group = "com.android.volley", name = "volley", version.ref = "volley" } volley = { group = "com.android.volley", name = "volley", version.ref = "volley" }
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" } glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
glide-compiler = { group = "com.github.bumptech.glide", name = "compiler", version.ref = "glide" } glide-compiler = { group = "com.github.bumptech.glide", name = "compiler", version.ref = "glide" }
mpandroidchart = { group = "com.github.PhilJay", name = "MPAndroidChart", version.ref = "mpandroidchart" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }

View File

@ -19,6 +19,7 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url = uri("https://jitpack.io") }
} }
} }