Commit del proyecto final DAM Elevate.
App para el desarrollo personal encargada de generarte un plan de ejercicios fisicos segun los datos introducidos por el usuatio y una dieta semanal.
This commit is contained in:
commit
7ca65a2ce3
|
|
@ -0,0 +1,15 @@
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Elevate
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2025-05-28T15:57:14.003490400Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=b1fdb6a8" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="SQLAndroidSettings">
|
||||||
|
<option name="storageVersion" value="2" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.borjabolufer.elevate'
|
||||||
|
compileSdk 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.borjabolufer.elevate"
|
||||||
|
minSdk 24
|
||||||
|
targetSdk 35
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation libs.appcompat
|
||||||
|
implementation libs.material
|
||||||
|
implementation libs.constraintlayout
|
||||||
|
implementation libs.lifecycle.livedata.ktx
|
||||||
|
implementation libs.lifecycle.viewmodel.ktx
|
||||||
|
implementation libs.navigation.fragment
|
||||||
|
implementation libs.navigation.ui
|
||||||
|
testImplementation libs.junit
|
||||||
|
androidTestImplementation libs.ext.junit
|
||||||
|
androidTestImplementation libs.espresso.core
|
||||||
|
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||||
|
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
|
||||||
|
implementation 'com.google.code.gson:gson:2.10.1'
|
||||||
|
implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:core:11.1.0'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
assertEquals("com.borjabolufer.elevate", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@drawable/logo"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.MyApp"
|
||||||
|
tools:targetApi="31">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".SplashActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/Theme.MyApp">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".PreguntasActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/Theme.MyApp" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/Theme.MyApp" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".WelcomeActivity"
|
||||||
|
android:theme="@style/Theme.MyApp"/>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".LoginActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/Theme.MyApp" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".RegisterActivity"
|
||||||
|
android:theme="@style/Theme.MyApp"/>
|
||||||
|
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioDbHelper;
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioEntity;
|
||||||
|
|
||||||
|
public class LoginActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "LoginActivity";
|
||||||
|
|
||||||
|
private EditText editEmail, editPassword;
|
||||||
|
private Button btnLogin;
|
||||||
|
|
||||||
|
private UsuarioDbHelper dbHelper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_loging);
|
||||||
|
|
||||||
|
editEmail = findViewById(R.id.editLoginEmail);
|
||||||
|
editPassword = findViewById(R.id.editLoginPassword);
|
||||||
|
btnLogin = findViewById(R.id.btnIniciarSesion);
|
||||||
|
TextView textIrRegistro = findViewById(R.id.textIrRegistro);
|
||||||
|
|
||||||
|
dbHelper = new UsuarioDbHelper(this);
|
||||||
|
|
||||||
|
btnLogin.setOnClickListener(v -> {
|
||||||
|
String email = editEmail.getText().toString().trim();
|
||||||
|
String password = editPassword.getText().toString();
|
||||||
|
|
||||||
|
if (email.isEmpty() || password.isEmpty()) {
|
||||||
|
Toast.makeText(LoginActivity.this, "Por favor, completa todos los campos", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Intentando iniciar sesión para el email: " + email);
|
||||||
|
|
||||||
|
UsuarioEntity usuarioVerificado = dbHelper.verificarUsuario(email, password);
|
||||||
|
|
||||||
|
if (usuarioVerificado != null) {
|
||||||
|
Log.i(TAG, "Inicio de sesión exitoso para: " + email);
|
||||||
|
Toast.makeText(LoginActivity.this, "Inicio de sesión exitoso", Toast.LENGTH_SHORT).show();
|
||||||
|
SharedPreferences prefs = getSharedPreferences("ElevatePrefs", MODE_PRIVATE);
|
||||||
|
prefs.edit().putString("user_email", email).apply();
|
||||||
|
|
||||||
|
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
|
||||||
|
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Fallo en el inicio de sesión para: " + email);
|
||||||
|
Toast.makeText(LoginActivity.this, "Email o contraseña incorrectos", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
textIrRegistro.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,480 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
import androidx.navigation.ui.AppBarConfiguration;
|
||||||
|
import androidx.navigation.ui.NavigationUI;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.databinding.ActivityMainBinding;
|
||||||
|
import com.borjabolufer.elevate.model.Ejercicio;
|
||||||
|
import com.borjabolufer.elevate.model.Usuario;
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioDbHelper;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.DetalleEjercicioFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.HomeFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.DietFragment;
|
||||||
|
import com.borjabolufer.elevate.utils.JsonParser;
|
||||||
|
import com.google.android.material.navigation.NavigationView;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "MainActivity";
|
||||||
|
private static final String PREFS_NAME = "ElevatePrefs";
|
||||||
|
private static final String KEY_USER_EMAIL = "user_email";
|
||||||
|
|
||||||
|
private AppBarConfiguration mAppBarConfiguration;
|
||||||
|
private ActivityMainBinding binding;
|
||||||
|
private String currentUserEmail;
|
||||||
|
private UsuarioDbHelper dbHelper;
|
||||||
|
private JsonParser jsonParser;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
|
||||||
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
|
||||||
|
inicializarComponentes();
|
||||||
|
|
||||||
|
|
||||||
|
setSupportActionBar(binding.appBarMain.toolbar);
|
||||||
|
|
||||||
|
|
||||||
|
configurarNavegacion();
|
||||||
|
|
||||||
|
|
||||||
|
limpiarNavegacionSiEsNecesario();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inicializarComponentes() {
|
||||||
|
dbHelper = new UsuarioDbHelper(this);
|
||||||
|
jsonParser = new JsonParser(this);
|
||||||
|
|
||||||
|
|
||||||
|
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
||||||
|
currentUserEmail = prefs.getString(KEY_USER_EMAIL, null);
|
||||||
|
|
||||||
|
Log.d(TAG, "Usuario actual: " + currentUserEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarNavegacion() {
|
||||||
|
DrawerLayout drawer = binding.drawerLayout;
|
||||||
|
NavigationView navigationView = binding.navView;
|
||||||
|
|
||||||
|
|
||||||
|
mAppBarConfiguration = new AppBarConfiguration.Builder(
|
||||||
|
R.id.nav_home,
|
||||||
|
R.id.nav_diet,
|
||||||
|
R.id.nav_profile
|
||||||
|
)
|
||||||
|
.setOpenableLayout(drawer)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
|
||||||
|
NavigationUI.setupWithNavController(navigationView, navController);
|
||||||
|
|
||||||
|
|
||||||
|
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
||||||
|
updateToolbarTitle(destination.getId());
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Navegando a: " + destination.getLabel() + " (ID: " + destination.getId() + ")");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
configurarNavigationHeader(navigationView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarNavigationHeader(NavigationView navigationView) {
|
||||||
|
UsuarioDbHelper usuarioDbHelper = new UsuarioDbHelper(getApplicationContext());
|
||||||
|
View headerView = navigationView.getHeaderView(0);
|
||||||
|
TextView headerTitle = headerView.findViewById(R.id.nav_header_title);
|
||||||
|
TextView headerSubtitle = headerView.findViewById(R.id.nav_header_subtitle);
|
||||||
|
|
||||||
|
SharedPreferences prefs = getSharedPreferences("ElevatePrefs", MODE_PRIVATE);
|
||||||
|
String email = prefs.getString("user_email", "usuario@desconocido.com");
|
||||||
|
String nombre = usuarioDbHelper.obtenerNombreUsuario(email);
|
||||||
|
|
||||||
|
headerTitle.setText(nombre != null ? nombre : "Usuario");
|
||||||
|
headerSubtitle.setText(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void limpiarNavegacionSiEsNecesario() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
|
||||||
|
|
||||||
|
if (fm.getBackStackEntryCount() > 3) {
|
||||||
|
Log.d(TAG, "Back stack muy grande (" + fm.getBackStackEntryCount() + "), limpiando...");
|
||||||
|
|
||||||
|
|
||||||
|
while (fm.getBackStackEntryCount() > 1) {
|
||||||
|
fm.popBackStackImmediate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
if (navController.getCurrentDestination() != null) {
|
||||||
|
int currentId = navController.getCurrentDestination().getId();
|
||||||
|
|
||||||
|
|
||||||
|
if (currentId != R.id.nav_home && currentId != R.id.nav_diet && currentId != R.id.nav_profile) {
|
||||||
|
Log.d(TAG, "Destino inválido detectado, navegando a home");
|
||||||
|
navController.navigate(R.id.nav_home);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error limpiando navegación", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
|
||||||
|
verificarEstadoNavegacion();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verificarEstadoNavegacion() {
|
||||||
|
try {
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Fragments en back stack: " + fm.getBackStackEntryCount());
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < fm.getBackStackEntryCount(); i++) {
|
||||||
|
FragmentManager.BackStackEntry entry = fm.getBackStackEntryAt(i);
|
||||||
|
if (entry.getName() != null && entry.getName().contains("Loading")) {
|
||||||
|
Log.d(TAG, "Detectado LoadingFragment problemático, limpiando...");
|
||||||
|
fm.popBackStackImmediate(entry.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
limpiarFragmentsHuerfanos();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error verificando navegación", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void limpiarFragmentsHuerfanos() {
|
||||||
|
try {
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
|
||||||
|
|
||||||
|
for (Fragment fragment : fm.getFragments()) {
|
||||||
|
if (fragment != null && fragment.getClass().getSimpleName().contains("Loading")) {
|
||||||
|
Log.d(TAG, "Removiendo fragment huérfano: " + fragment.getClass().getSimpleName());
|
||||||
|
fm.beginTransaction()
|
||||||
|
.remove(fragment)
|
||||||
|
.commitAllowingStateLoss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error limpiando fragments huérfanos", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarMensajeSinPlan() {
|
||||||
|
Snackbar.make(binding.getRoot(),
|
||||||
|
"Complete su perfil para generar su plan personalizado",
|
||||||
|
Snackbar.LENGTH_LONG)
|
||||||
|
.setAction("Completar", v -> {
|
||||||
|
Intent intent = new Intent(this, PreguntasActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void iniciarEntrenamiento() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
HomeFragment homeFragment = getCurrentHomeFragment();
|
||||||
|
if (homeFragment != null) {
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Iniciando entrenamiento desde HomeFragment");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al iniciar entrenamiento", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verRecetaCompleta() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
DietFragment dietFragment = getCurrentDietFragment();
|
||||||
|
if (dietFragment != null) {
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Mostrando receta completa desde DietFragment");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al mostrar receta", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HomeFragment getCurrentHomeFragment() {
|
||||||
|
try {
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
if (navController.getCurrentDestination() != null &&
|
||||||
|
navController.getCurrentDestination().getId() == R.id.nav_home) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al obtener HomeFragment", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DietFragment getCurrentDietFragment() {
|
||||||
|
try {
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
if (navController.getCurrentDestination() != null &&
|
||||||
|
navController.getCurrentDestination().getId() == R.id.nav_diet) {
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al obtener DietFragment", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateToolbarTitle(int destinationId) {
|
||||||
|
String title;
|
||||||
|
|
||||||
|
if (destinationId == R.id.nav_home) {
|
||||||
|
title = "Entrenamientos";
|
||||||
|
} else if (destinationId == R.id.nav_diet) {
|
||||||
|
title = "Plan Nutricional";
|
||||||
|
} else if (destinationId == R.id.nav_profile) {
|
||||||
|
title = "Mi Perfil";
|
||||||
|
} else {
|
||||||
|
title = "Elevate Fitness";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setTitle(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.main, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void abrirConfiguracion() {
|
||||||
|
Snackbar.make(binding.getRoot(), "Abriendo configuración...", Snackbar.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sincronizarDatos() {
|
||||||
|
Snackbar.make(binding.getRoot(), "Sincronizando datos...", Snackbar.LENGTH_LONG)
|
||||||
|
.setAction("Cancelar", v -> {
|
||||||
|
Log.d(TAG, "Sincronización cancelada");
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
Snackbar.make(binding.getRoot(), "Datos sincronizados correctamente",
|
||||||
|
Snackbar.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Error en sincronización", e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refrescarDatos() {
|
||||||
|
Log.d(TAG, "Refrescando datos de ejercicios y dietas");
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
String planJson = dbHelper.obtenerPlanJson(currentUserEmail);
|
||||||
|
|
||||||
|
if (planJson != null && !planJson.trim().isEmpty()) {
|
||||||
|
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
|
||||||
|
|
||||||
|
if (navController.getCurrentDestination() != null) {
|
||||||
|
int currentId = navController.getCurrentDestination().getId();
|
||||||
|
|
||||||
|
if (currentId == R.id.nav_home || currentId == R.id.nav_diet) {
|
||||||
|
Snackbar.make(binding.getRoot(), "Datos actualizados", Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mostrarMensajeSinPlan();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al refrescar datos", e);
|
||||||
|
Snackbar.make(binding.getRoot(), "Error al actualizar datos", Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp() {
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|
||||||
|
|| super.onSupportNavigateUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (binding.drawerLayout.isDrawerOpen(binding.navView)) {
|
||||||
|
binding.drawerLayout.closeDrawer(binding.navView);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
if (fm.getBackStackEntryCount() > 0) {
|
||||||
|
|
||||||
|
super.onBackPressed();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
if (navController.getCurrentDestination() != null &&
|
||||||
|
navController.getCurrentDestination().getId() != R.id.nav_home) {
|
||||||
|
|
||||||
|
navController.navigate(R.id.nav_home);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error en onBackPressed", e);
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getCurrentUserEmail() {
|
||||||
|
return currentUserEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UsuarioDbHelper getDbHelper() {
|
||||||
|
return dbHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void navegarA(int destinationId) {
|
||||||
|
try {
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
|
||||||
|
|
||||||
|
if (getSupportFragmentManager().getBackStackEntryCount() > 2) {
|
||||||
|
limpiarNavegacionSiEsNecesario();
|
||||||
|
}
|
||||||
|
|
||||||
|
navController.navigate(destinationId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error navegando a destino: " + destinationId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
|
||||||
|
if (dbHelper != null) {
|
||||||
|
dbHelper.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
binding = null;
|
||||||
|
|
||||||
|
Log.d(TAG, "MainActivity destruida correctamente");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void resetNavigation() {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Reseteando navegación de emergencia");
|
||||||
|
|
||||||
|
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
|
||||||
|
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
navController.navigate(R.id.nav_home);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error en reset de navegación", e);
|
||||||
|
|
||||||
|
recreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.model.Altura;
|
||||||
|
import com.borjabolufer.elevate.model.DiasEntrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Edad;
|
||||||
|
import com.borjabolufer.elevate.model.Intensidad;
|
||||||
|
import com.borjabolufer.elevate.model.Objetivo;
|
||||||
|
import com.borjabolufer.elevate.model.Peso;
|
||||||
|
import com.borjabolufer.elevate.model.Sexo;
|
||||||
|
import com.borjabolufer.elevate.model.Usuario;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.AlturaFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.DiasEntrenamientoFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.EdadFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.IntensidadFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.LoadingPlanFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.ObjetivoFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.PesoFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.ResumenFragment;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.SexoFragment;
|
||||||
|
|
||||||
|
public class PreguntasActivity extends AppCompatActivity
|
||||||
|
implements AlturaFragment.IOnAlturaListener,
|
||||||
|
PesoFragment.IOnPesoListener,
|
||||||
|
EdadFragment.IOnEdadListener,
|
||||||
|
SexoFragment.IOnSexoListener,
|
||||||
|
IntensidadFragment.IOnIntensidadListener,
|
||||||
|
ObjetivoFragment.IOnObjetivoListener,
|
||||||
|
DiasEntrenamientoFragment.IOnDiasEntrenamientoListener {
|
||||||
|
|
||||||
|
private static final String TAG = "PreguntasActivity";
|
||||||
|
|
||||||
|
private Usuario usuario = new Usuario();
|
||||||
|
private String nombreCompleto;
|
||||||
|
private String emailUsuario;
|
||||||
|
private String passwordUsuario;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_preguntas);
|
||||||
|
|
||||||
|
|
||||||
|
if (!obtenerDatosDelIntent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
mostrarFragment(new SexoFragment());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean obtenerDatosDelIntent() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent == null) {
|
||||||
|
Log.e(TAG, "Intent nulo");
|
||||||
|
mostrarErrorYCerrar("Error: No se recibieron datos para configurar tu perfil.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nombreCompleto = intent.getStringExtra(RegisterActivity.EXTRA_NOMBRE);
|
||||||
|
emailUsuario = intent.getStringExtra(RegisterActivity.EXTRA_EMAIL);
|
||||||
|
passwordUsuario = intent.getStringExtra(RegisterActivity.EXTRA_PASSWORD);
|
||||||
|
|
||||||
|
|
||||||
|
if (nombreCompleto == null || nombreCompleto.trim().isEmpty()) {
|
||||||
|
Log.e(TAG, "Nombre completo no recibido");
|
||||||
|
mostrarErrorYCerrar("Error: Falta el nombre completo.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailUsuario == null || emailUsuario.trim().isEmpty()) {
|
||||||
|
Log.e(TAG, "Email no recibido");
|
||||||
|
mostrarErrorYCerrar("Error: Falta el email del usuario.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordUsuario == null || passwordUsuario.trim().isEmpty()) {
|
||||||
|
Log.e(TAG, "Contraseña no recibida");
|
||||||
|
mostrarErrorYCerrar("Error: Falta la contraseña del usuario.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Datos recibidos correctamente:");
|
||||||
|
Log.d(TAG, "- Nombre: " + nombreCompleto);
|
||||||
|
Log.d(TAG, "- Email: " + emailUsuario);
|
||||||
|
Log.d(TAG, "- Password: " + (passwordUsuario != null ? "[PRESENTE]" : "[AUSENTE]"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarErrorYCerrar(String mensaje) {
|
||||||
|
Toast.makeText(this, mensaje, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarFragment(Fragment fragment) {
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.fragmentContainerViewPreguntas, fragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSexoSelected(Sexo sexo) {
|
||||||
|
usuario.setSexo(sexo);
|
||||||
|
Log.d(TAG, "Sexo seleccionado: " + sexo.getSexo());
|
||||||
|
mostrarFragment(new EdadFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEdadSelected(Edad edad) {
|
||||||
|
usuario.setEdad(edad);
|
||||||
|
Log.d(TAG, "Edad seleccionada: " + edad.getEdad());
|
||||||
|
mostrarFragment(new PesoFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPesoSelected(Peso peso) {
|
||||||
|
usuario.setPeso(peso);
|
||||||
|
Log.d(TAG, "Peso seleccionado: " + peso.getPeso());
|
||||||
|
mostrarFragment(new AlturaFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAlturaSelected(Altura altura) {
|
||||||
|
usuario.setAltura(altura);
|
||||||
|
Log.d(TAG, "Altura seleccionada: " + altura.getAltura());
|
||||||
|
mostrarFragment(new IntensidadFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIntensidadSelected(Intensidad intensidad) {
|
||||||
|
usuario.setIntensidad(intensidad);
|
||||||
|
Log.d(TAG, "Intensidad seleccionada: " + intensidad.getIntensidad());
|
||||||
|
mostrarFragment(new ObjetivoFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onObjetivoSelected(Objetivo objetivo) {
|
||||||
|
usuario.setObjetivo(objetivo);
|
||||||
|
Log.d(TAG, "Objetivo seleccionado: " + objetivo.getObjetivo());
|
||||||
|
mostrarFragment(new DiasEntrenamientoFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDiasEntrenamientoSelected(DiasEntrenamiento diasEntrenamiento) {
|
||||||
|
usuario.setDias(diasEntrenamiento);
|
||||||
|
Log.d(TAG, "Días de entrenamiento seleccionados: " + diasEntrenamiento.getCantidad());
|
||||||
|
|
||||||
|
|
||||||
|
if (!validarUsuarioCompleto()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Perfil de usuario completado. Mostrando resumen antes de generar plan.");
|
||||||
|
mostrarResumen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validarUsuarioCompleto() {
|
||||||
|
StringBuilder errores = new StringBuilder();
|
||||||
|
|
||||||
|
if (usuario.getSexo() == null) errores.append("- Sexo\n");
|
||||||
|
if (usuario.getEdad() == null) errores.append("- Edad\n");
|
||||||
|
if (usuario.getPeso() == null) errores.append("- Peso\n");
|
||||||
|
if (usuario.getAltura() == null) errores.append("- Altura\n");
|
||||||
|
if (usuario.getIntensidad() == null) errores.append("- Intensidad\n");
|
||||||
|
if (usuario.getObjetivo() == null) errores.append("- Objetivo\n");
|
||||||
|
if (usuario.getDias() == null) errores.append("- Días de entrenamiento\n");
|
||||||
|
|
||||||
|
if (errores.length() > 0) {
|
||||||
|
Log.e(TAG, "Datos faltantes en el usuario:\n" + errores.toString());
|
||||||
|
Toast.makeText(this, "Error: Faltan datos del perfil:\n" + errores.toString(),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarResumen() {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "=== INICIANDO MOSTRAR RESUMEN ===");
|
||||||
|
|
||||||
|
|
||||||
|
ResumenFragment resumenFragment = ResumenFragment.newInstance(
|
||||||
|
usuario,
|
||||||
|
emailUsuario,
|
||||||
|
passwordUsuario
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Creando ResumenFragment con:");
|
||||||
|
Log.d(TAG, "- Usuario completo: " + (usuario != null ? "✓" : "✗"));
|
||||||
|
Log.d(TAG, "- Email: " + emailUsuario);
|
||||||
|
Log.d(TAG, "- Nombre: " + nombreCompleto);
|
||||||
|
Log.d(TAG, "- Datos del usuario:");
|
||||||
|
Log.d(TAG, " * Sexo: " + (usuario.getSexo() != null ? usuario.getSexo().getSexo() : "null"));
|
||||||
|
Log.d(TAG, " * Edad: " + (usuario.getEdad() != null ? usuario.getEdad().getEdad() : "null"));
|
||||||
|
Log.d(TAG, " * Peso: " + (usuario.getPeso() != null ? usuario.getPeso().getPeso() : "null"));
|
||||||
|
Log.d(TAG, " * Altura: " + (usuario.getAltura() != null ? usuario.getAltura().getAltura() : "null"));
|
||||||
|
Log.d(TAG, " * Objetivo: " + (usuario.getObjetivo() != null ? usuario.getObjetivo().getObjetivo() : "null"));
|
||||||
|
Log.d(TAG, " * Intensidad: " + (usuario.getIntensidad() != null ? usuario.getIntensidad().getIntensidad() : "null"));
|
||||||
|
Log.d(TAG, " * Días: " + (usuario.getDias() != null ? usuario.getDias().getCantidad() : "null"));
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, "Mostrando ResumenFragment...");
|
||||||
|
mostrarFragment(resumenFragment);
|
||||||
|
|
||||||
|
Toast.makeText(this, "¡Perfil completo! Revisa tu información antes de continuar.",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
Log.d(TAG, "=== RESUMEN MOSTRADO EXITOSAMENTE ===");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al crear ResumenFragment", e);
|
||||||
|
Toast.makeText(this, "Error al mostrar resumen. Inténtalo de nuevo.",
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void procederAGeneracionDePlan() {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "=== PROCEDIENDO A GENERACIÓN DE PLAN ===");
|
||||||
|
Log.d(TAG, "Método llamado desde ResumenFragment");
|
||||||
|
|
||||||
|
|
||||||
|
LoadingPlanFragment loadingFragment = LoadingPlanFragment.newInstance(
|
||||||
|
usuario,
|
||||||
|
emailUsuario,
|
||||||
|
passwordUsuario
|
||||||
|
);
|
||||||
|
|
||||||
|
Log.d(TAG, "LoadingPlanFragment creado correctamente");
|
||||||
|
|
||||||
|
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.fragmentContainerViewPreguntas, loadingFragment)
|
||||||
|
.commit();
|
||||||
|
|
||||||
|
Toast.makeText(this, "Generando tu plan personalizado...",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
Log.d(TAG, "=== NAVEGACIÓN A LOADING COMPLETADA ===");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al crear LoadingPlanFragment", e);
|
||||||
|
Toast.makeText(this, "Error al generar el plan. Inténtalo de nuevo.",
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
|
||||||
|
|
||||||
|
if (backStackCount > 0) {
|
||||||
|
|
||||||
|
super.onBackPressed();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Toast.makeText(this, "¿Seguro que quieres cancelar la configuración?",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getNombreCompleto() {
|
||||||
|
return nombreCompleto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getEmailUsuario() {
|
||||||
|
return emailUsuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Usuario getUsuario() {
|
||||||
|
return usuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getPasswordUsuario() {
|
||||||
|
return passwordUsuario;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Patterns;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioDbHelper;
|
||||||
|
|
||||||
|
public class RegisterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private EditText editNombre, editEmail, editPassword, editConfirmPassword;
|
||||||
|
private Button btnRegister;
|
||||||
|
private TextView textIrLogin;
|
||||||
|
|
||||||
|
private UsuarioDbHelper dbHelper;
|
||||||
|
|
||||||
|
public static final String EXTRA_NOMBRE = "com.borjabolufer.elevate.EXTRA_NOMBRE";
|
||||||
|
public static final String EXTRA_EMAIL = "com.borjabolufer.elevate.EXTRA_EMAIL";
|
||||||
|
public static final String EXTRA_PASSWORD = "com.borjabolufer.elevate.EXTRA_PASSWORD";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_register);
|
||||||
|
|
||||||
|
initializeViews();
|
||||||
|
dbHelper = new UsuarioDbHelper(this);
|
||||||
|
setupListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeViews() {
|
||||||
|
editNombre = findViewById(R.id.editNombre);
|
||||||
|
editEmail = findViewById(R.id.editEmail);
|
||||||
|
editPassword = findViewById(R.id.editPassword);
|
||||||
|
editConfirmPassword = findViewById(R.id.editConfirmPassword);
|
||||||
|
btnRegister = findViewById(R.id.btnCrearCuenta);
|
||||||
|
textIrLogin = findViewById(R.id.textIrLogin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupListeners() {
|
||||||
|
btnRegister.setOnClickListener(v -> attemptRegistration());
|
||||||
|
|
||||||
|
textIrLogin.setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(this, LoginActivity.class));
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attemptRegistration() {
|
||||||
|
clearErrors();
|
||||||
|
|
||||||
|
String nombreCompleto = editNombre.getText().toString().trim();
|
||||||
|
String email = editEmail.getText().toString().trim();
|
||||||
|
String password = editPassword.getText().toString();
|
||||||
|
String confirmPassword = editConfirmPassword.getText().toString();
|
||||||
|
|
||||||
|
if (!validateInputs(nombreCompleto, email, password, confirmPassword)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(this, "Datos correctos, configurando tu perfil...", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, PreguntasActivity.class);
|
||||||
|
intent.putExtra(EXTRA_NOMBRE, nombreCompleto);
|
||||||
|
intent.putExtra(EXTRA_EMAIL, email);
|
||||||
|
intent.putExtra(EXTRA_PASSWORD, password);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearErrors() {
|
||||||
|
editNombre.setError(null);
|
||||||
|
editEmail.setError(null);
|
||||||
|
editPassword.setError(null);
|
||||||
|
editConfirmPassword.setError(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateInputs(String nombre, String email, String password, String confirmPassword) {
|
||||||
|
boolean isValid = true;
|
||||||
|
EditText focusView = null;
|
||||||
|
|
||||||
|
if (!password.equals(confirmPassword)) {
|
||||||
|
editConfirmPassword.setError("Las contraseñas no coinciden");
|
||||||
|
focusView = editConfirmPassword;
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(password)) {
|
||||||
|
editPassword.setError("Este campo es requerido");
|
||||||
|
focusView = editPassword;
|
||||||
|
isValid = false;
|
||||||
|
} else if (password.length() < 6) {
|
||||||
|
editPassword.setError("La contraseña debe tener al menos 6 caracteres");
|
||||||
|
focusView = editPassword;
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(email)) {
|
||||||
|
editEmail.setError("Este campo es requerido");
|
||||||
|
focusView = editEmail;
|
||||||
|
isValid = false;
|
||||||
|
} else if (!isValidEmail(email)) {
|
||||||
|
editEmail.setError("Este email no es válido");
|
||||||
|
focusView = editEmail;
|
||||||
|
isValid = false;
|
||||||
|
} else if (dbHelper.checkUserEmailExists(email)) {
|
||||||
|
editEmail.setError("Este email ya está registrado");
|
||||||
|
focusView = editEmail;
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(nombre)) {
|
||||||
|
editNombre.setError("Este campo es requerido");
|
||||||
|
focusView = editNombre;
|
||||||
|
isValid = false;
|
||||||
|
} else if (nombre.length() < 2) {
|
||||||
|
editNombre.setError("El nombre debe tener al menos 2 caracteres");
|
||||||
|
focusView = editNombre;
|
||||||
|
isValid = false;
|
||||||
|
} else if (!isValidName(nombre)) {
|
||||||
|
editNombre.setError("El nombre solo puede contener letras y espacios");
|
||||||
|
focusView = editNombre;
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focusView != null) {
|
||||||
|
focusView.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidEmail(CharSequence target) {
|
||||||
|
return (!TextUtils.isEmpty(target) && Patterns.EMAIL_ADDRESS.matcher(target).matches());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidName(String name) {
|
||||||
|
return name.matches("^[a-zA-ZáéíóúüñÁÉÍÓÚÜÑ\\s'-]+$");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
if (dbHelper != null) {
|
||||||
|
dbHelper.close();
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
public class SplashActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final int DURACION_SPLASH = 2000;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.splash);
|
||||||
|
|
||||||
|
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Intent intent = new Intent(SplashActivity.this, WelcomeActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, DURACION_SPLASH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.borjabolufer.elevate;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
public class WelcomeActivity extends AppCompatActivity {
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_welcome);
|
||||||
|
|
||||||
|
Button btnIniciar = findViewById(R.id.btnIniciar);
|
||||||
|
Button btnRegistrar = findViewById(R.id.btnRegistrar);
|
||||||
|
|
||||||
|
btnIniciar.setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(this, LoginActivity.class));
|
||||||
|
});
|
||||||
|
|
||||||
|
btnRegistrar.setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(this, RegisterActivity.class));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
package com.borjabolufer.elevate.adapters;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Comida;
|
||||||
|
import com.borjabolufer.elevate.model.ValorNutricional;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.DietFragment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ComidaAdapter extends RecyclerView.Adapter<ComidaAdapter.ComidaViewHolder> {
|
||||||
|
|
||||||
|
private static final String TAG = "ComidaAdapter";
|
||||||
|
private List<DietFragment.ComidaItem> comidas;
|
||||||
|
private Fragment parentFragment;
|
||||||
|
|
||||||
|
public ComidaAdapter(List<DietFragment.ComidaItem> comidas) {
|
||||||
|
this.comidas = comidas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComidaAdapter(List<DietFragment.ComidaItem> comidas, Fragment parentFragment) {
|
||||||
|
this.comidas = comidas;
|
||||||
|
this.parentFragment = parentFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ComidaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_comida, parent, false);
|
||||||
|
return new ComidaViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ComidaViewHolder holder, int position) {
|
||||||
|
DietFragment.ComidaItem item = comidas.get(position);
|
||||||
|
holder.bind(item, parentFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return comidas.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ComidaViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView textNombreComida, textDescripcion, textCalorias, textProteinas, textCarbos, textGrasas;
|
||||||
|
ImageView iconComida;
|
||||||
|
|
||||||
|
ComidaViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
textNombreComida = itemView.findViewById(R.id.text_nombre_comida);
|
||||||
|
textDescripcion = itemView.findViewById(R.id.text_descripcion_comida);
|
||||||
|
textCalorias = itemView.findViewById(R.id.text_calorias_comida);
|
||||||
|
textProteinas = itemView.findViewById(R.id.text_proteinas_comida);
|
||||||
|
textCarbos = itemView.findViewById(R.id.text_carbos_comida);
|
||||||
|
textGrasas = itemView.findViewById(R.id.text_grasas_comida);
|
||||||
|
iconComida = itemView.findViewById(R.id.icon_comida);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(DietFragment.ComidaItem item, Fragment parentFragment) {
|
||||||
|
Comida comida = item.getComida();
|
||||||
|
|
||||||
|
textNombreComida.setText(item.getNombre());
|
||||||
|
textDescripcion.setText(comida.getNombre());
|
||||||
|
|
||||||
|
ValorNutricional vn = comida.getValor_nutricional();
|
||||||
|
if (vn != null) {
|
||||||
|
textCalorias.setText(vn.getCalorías() + " kcal");
|
||||||
|
textProteinas.setText(vn.getProteínas() + "g");
|
||||||
|
textCarbos.setText(vn.getCarbohidratos() + "g");
|
||||||
|
textGrasas.setText(vn.getGrasas() + "g");
|
||||||
|
} else {
|
||||||
|
textCalorias.setText("0 kcal");
|
||||||
|
textProteinas.setText("0g");
|
||||||
|
textCarbos.setText("0g");
|
||||||
|
textGrasas.setText("0g");
|
||||||
|
}
|
||||||
|
|
||||||
|
int iconResource;
|
||||||
|
int tintColor;
|
||||||
|
|
||||||
|
switch (item.getTipo()) {
|
||||||
|
case "desayuno":
|
||||||
|
iconResource = R.drawable.ic_desayuno;
|
||||||
|
tintColor = R.color.warning;
|
||||||
|
break;
|
||||||
|
case "comida":
|
||||||
|
iconResource = R.drawable.ic_comida;
|
||||||
|
tintColor = R.color.primary;
|
||||||
|
break;
|
||||||
|
case "cena":
|
||||||
|
iconResource = R.drawable.ic_cena;
|
||||||
|
tintColor = R.color.info;
|
||||||
|
break;
|
||||||
|
case "snacks":
|
||||||
|
iconResource = R.drawable.ic_snack;
|
||||||
|
tintColor = R.color.success;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iconResource = R.drawable.ic_restaurante;
|
||||||
|
tintColor = R.color.info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconComida.setImageResource(iconResource);
|
||||||
|
iconComida.setColorFilter(itemView.getContext().getColor(tintColor));
|
||||||
|
|
||||||
|
itemView.setOnClickListener(v -> {
|
||||||
|
try {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable("comida", comida);
|
||||||
|
args.putString("tipo_comida", item.getTipo());
|
||||||
|
|
||||||
|
Log.d(TAG, "Navegando al detalle de comida: " + comida.getNombre());
|
||||||
|
|
||||||
|
Navigation.findNavController(v).navigate(R.id.nav_detalle_comida, args);
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.e(TAG, "Error de navegación - ID no encontrado en navigation.xml: " + e.getMessage());
|
||||||
|
if (itemView.getContext() instanceof android.app.Activity) {
|
||||||
|
((android.app.Activity) itemView.getContext()).runOnUiThread(() -> {
|
||||||
|
android.widget.Toast.makeText(itemView.getContext(),
|
||||||
|
"Error: Destino de navegación no configurado",
|
||||||
|
android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error general de navegación: " + e.getMessage());
|
||||||
|
if (itemView.getContext() instanceof android.app.Activity) {
|
||||||
|
((android.app.Activity) itemView.getContext()).runOnUiThread(() -> {
|
||||||
|
android.widget.Toast.makeText(itemView.getContext(),
|
||||||
|
"Error al navegar al detalle",
|
||||||
|
android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
itemView.setClickable(true);
|
||||||
|
itemView.setFocusable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.borjabolufer.elevate.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.HomeFragment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EjercicioAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
public interface OnItemClickListener {
|
||||||
|
void onItemClick(HomeFragment.EjercicioItem item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int TYPE_HEADER = 0;
|
||||||
|
private static final int TYPE_EJERCICIO = 1;
|
||||||
|
|
||||||
|
private List<HomeFragment.EjercicioItem> ejercicios;
|
||||||
|
private OnItemClickListener listener;
|
||||||
|
|
||||||
|
public EjercicioAdapter(List<HomeFragment.EjercicioItem> ejercicios, OnItemClickListener listener) {
|
||||||
|
this.ejercicios = ejercicios;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return ejercicios.get(position).isEsHeader() ? TYPE_HEADER : TYPE_EJERCICIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
if (viewType == TYPE_HEADER) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_ejercicio_header, parent, false);
|
||||||
|
return new HeaderViewHolder(view);
|
||||||
|
} else {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_ejercicio, parent, false);
|
||||||
|
return new EjercicioViewHolder(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
HomeFragment.EjercicioItem item = ejercicios.get(position);
|
||||||
|
if (holder instanceof HeaderViewHolder) {
|
||||||
|
((HeaderViewHolder) holder).bind(item);
|
||||||
|
} else if (holder instanceof EjercicioViewHolder) {
|
||||||
|
((EjercicioViewHolder) holder).bind(item, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return ejercicios.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView textHeader;
|
||||||
|
|
||||||
|
HeaderViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
textHeader = itemView.findViewById(R.id.text_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(HomeFragment.EjercicioItem item) {
|
||||||
|
textHeader.setText(item.getNombre());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EjercicioViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView textNombre, textSeries, textRepeticiones;
|
||||||
|
ImageView iconEjercicio;
|
||||||
|
|
||||||
|
EjercicioViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
textNombre = itemView.findViewById(R.id.text_nombre_ejercicio);
|
||||||
|
textSeries = itemView.findViewById(R.id.text_series);
|
||||||
|
textRepeticiones = itemView.findViewById(R.id.text_repeticiones);
|
||||||
|
iconEjercicio = itemView.findViewById(R.id.icon_ejercicio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(HomeFragment.EjercicioItem item, OnItemClickListener listener) {
|
||||||
|
textNombre.setText(item.getNombre());
|
||||||
|
|
||||||
|
if ("calentamiento".equals(item.getTipo()) || "enfriamiento".equals(item.getTipo())) {
|
||||||
|
textSeries.setText("");
|
||||||
|
textRepeticiones.setText("");
|
||||||
|
} else {
|
||||||
|
textSeries.setText(item.getSeries() > 0 ? item.getSeries() + " series" : "");
|
||||||
|
textRepeticiones.setText(item.getRepeticiones() > 0 ? item.getRepeticiones() + " reps" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
int iconResource;
|
||||||
|
int tintColor;
|
||||||
|
switch (item.getTipo()) {
|
||||||
|
case "calentamiento":
|
||||||
|
iconResource = R.drawable.ic_calentamiento;
|
||||||
|
tintColor = R.color.warning;
|
||||||
|
break;
|
||||||
|
case "principal":
|
||||||
|
iconResource = R.drawable.ic_principal;
|
||||||
|
tintColor = R.color.primary;
|
||||||
|
break;
|
||||||
|
case "enfriamiento":
|
||||||
|
iconResource = R.drawable.ic_enfriamiento;
|
||||||
|
tintColor = R.color.success;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iconResource = R.drawable.ic_calentamiento;
|
||||||
|
tintColor = R.color.warning;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconEjercicio.setImageResource(iconResource);
|
||||||
|
iconEjercicio.setColorFilter(itemView.getContext().getColor(tintColor));
|
||||||
|
|
||||||
|
itemView.setOnClickListener(v -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onItemClick(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Altura {
|
||||||
|
private int altura;
|
||||||
|
|
||||||
|
public Altura(int altura) {
|
||||||
|
this.altura = altura;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAltura() {
|
||||||
|
return altura;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAltura(int altura) {
|
||||||
|
this.altura = altura;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Altura: " + altura + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Comida implements Serializable {
|
||||||
|
public String nombre;
|
||||||
|
public List<String> ingredientes;
|
||||||
|
public String receta;
|
||||||
|
public ValorNutricional valor_nutricional;
|
||||||
|
public String video_url;
|
||||||
|
|
||||||
|
public Comida(String nombre, List<String> ingredientes, String receta, ValorNutricional valor_nutricional, String video_url) {
|
||||||
|
this.nombre = nombre;
|
||||||
|
this.ingredientes = ingredientes;
|
||||||
|
this.receta = receta;
|
||||||
|
this.valor_nutricional = valor_nutricional;
|
||||||
|
this.video_url = video_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombre() {
|
||||||
|
return nombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNombre(String nombre) {
|
||||||
|
this.nombre = nombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getIngredientes() {
|
||||||
|
return ingredientes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIngredientes(List<String> ingredientes) {
|
||||||
|
this.ingredientes = ingredientes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceta() {
|
||||||
|
return receta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceta(String receta) {
|
||||||
|
this.receta = receta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValorNutricional getValor_nutricional() {
|
||||||
|
return valor_nutricional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValor_nutricional(ValorNutricional valor_nutricional) {
|
||||||
|
this.valor_nutricional = valor_nutricional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVideo_url() {
|
||||||
|
return video_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideo_url(String video_url) {
|
||||||
|
this.video_url = video_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class DiasEntrenamiento {
|
||||||
|
private int cantidad;
|
||||||
|
|
||||||
|
public String dia;
|
||||||
|
public Entrenamiento entrenamiento;
|
||||||
|
public Dieta dieta;
|
||||||
|
|
||||||
|
public DiasEntrenamiento(int cantidad) {
|
||||||
|
this.cantidad = cantidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiasEntrenamiento(int cantidad, String dia, Entrenamiento entrenamiento, Dieta dieta) {
|
||||||
|
this.cantidad = cantidad;
|
||||||
|
this.dia = dia;
|
||||||
|
this.entrenamiento = entrenamiento;
|
||||||
|
this.dieta = dieta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCantidad() {
|
||||||
|
return cantidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCantidad(int cantidad) {
|
||||||
|
this.cantidad = cantidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDia() {
|
||||||
|
return dia;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDia(String dia) {
|
||||||
|
this.dia = dia;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entrenamiento getEntrenamiento() {
|
||||||
|
return entrenamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntrenamiento(Entrenamiento entrenamiento) {
|
||||||
|
this.entrenamiento = entrenamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dieta getDieta() {
|
||||||
|
return dieta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDieta(Dieta dieta) {
|
||||||
|
this.dieta = dieta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Dieta {
|
||||||
|
public Comida desayuno;
|
||||||
|
public Comida comida;
|
||||||
|
public Comida cena;
|
||||||
|
public Comida snacks;
|
||||||
|
|
||||||
|
public Dieta(Comida desayuno, Comida comida, Comida cena, Comida snacks) {
|
||||||
|
this.desayuno = desayuno;
|
||||||
|
this.comida = comida;
|
||||||
|
this.cena = cena;
|
||||||
|
this.snacks = snacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comida getDesayuno() {
|
||||||
|
return desayuno;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDesayuno(Comida desayuno) {
|
||||||
|
this.desayuno = desayuno;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comida getComida() {
|
||||||
|
return comida;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComida(Comida comida) {
|
||||||
|
this.comida = comida;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comida getCena() {
|
||||||
|
return cena;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCena(Comida cena) {
|
||||||
|
this.cena = cena;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comida getSnacks() {
|
||||||
|
return snacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnacks(Comida snacks) {
|
||||||
|
this.snacks = snacks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Edad {
|
||||||
|
private int edad;
|
||||||
|
|
||||||
|
public Edad(int edad) {
|
||||||
|
this.edad = edad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEdad() {
|
||||||
|
return edad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEdad(int edad) {
|
||||||
|
this.edad = edad;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Edad: " + edad + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Ejercicio implements Serializable {
|
||||||
|
private String nombre;
|
||||||
|
private String descripcion;
|
||||||
|
private int series;
|
||||||
|
private int repeticiones;
|
||||||
|
private String descanso;
|
||||||
|
private String videoUrl;
|
||||||
|
|
||||||
|
public Ejercicio(String nombre, String descripcion, int series, int repeticiones, String descanso, String videoUrl) {
|
||||||
|
this.nombre = nombre;
|
||||||
|
this.descripcion = descripcion;
|
||||||
|
this.series = series;
|
||||||
|
this.repeticiones = repeticiones;
|
||||||
|
this.descanso = descanso;
|
||||||
|
this.videoUrl = videoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombre() {
|
||||||
|
return nombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescripcion() {
|
||||||
|
return descripcion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSeries() {
|
||||||
|
return series;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRepeticiones() {
|
||||||
|
return repeticiones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescanso() {
|
||||||
|
return descanso;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVideoUrl() {
|
||||||
|
return videoUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Entrenamiento {
|
||||||
|
public List<Ejercicio> calentamiento;
|
||||||
|
public List<Ejercicio> ejercicios_principales;
|
||||||
|
public List<Ejercicio> enfriamiento;
|
||||||
|
public Entrenamiento(List<Ejercicio> calentamiento, List<Ejercicio> ejercicios_principales, List<Ejercicio> enfriamiento) {
|
||||||
|
this.calentamiento = calentamiento;
|
||||||
|
this.ejercicios_principales = ejercicios_principales;
|
||||||
|
this.enfriamiento = enfriamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public List<Ejercicio> getCalentamiento() {
|
||||||
|
return calentamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCalentamiento(List<Ejercicio> calentamiento) {
|
||||||
|
this.calentamiento = calentamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Ejercicio> getEjercicios_principales() {
|
||||||
|
return ejercicios_principales;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEjercicios_principales(List<Ejercicio> ejercicios_principales) {
|
||||||
|
this.ejercicios_principales = ejercicios_principales;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Ejercicio> getEnfriamiento() {
|
||||||
|
return enfriamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnfriamiento(List<Ejercicio> enfriamiento) {
|
||||||
|
this.enfriamiento = enfriamiento;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Intensidad {
|
||||||
|
private String intensidad;
|
||||||
|
|
||||||
|
public Intensidad(String intensidad) {
|
||||||
|
this.intensidad = intensidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIntensidad() {
|
||||||
|
return intensidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntensidad(String intensidad) {
|
||||||
|
this.intensidad = intensidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Intensidad: " + intensidad + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Objetivo {
|
||||||
|
private String objetivo;
|
||||||
|
|
||||||
|
public Objetivo(String objetivo) {
|
||||||
|
this.objetivo = objetivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getObjetivo() {
|
||||||
|
return objetivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObjetivo(String objetivo) {
|
||||||
|
this.objetivo = objetivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Objetivo: " + objetivo + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Peso {
|
||||||
|
private float peso;
|
||||||
|
|
||||||
|
|
||||||
|
public Peso(float peso) {
|
||||||
|
this.peso = peso;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPeso() {
|
||||||
|
return peso;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeso(float peso) {
|
||||||
|
this.peso = peso;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Peso: " + peso + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Semana {
|
||||||
|
public List<DiasEntrenamiento> semana;
|
||||||
|
|
||||||
|
public Semana(List<DiasEntrenamiento> semana) {
|
||||||
|
this.semana = semana;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DiasEntrenamiento> getSemana() {
|
||||||
|
return semana;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSemana(List<DiasEntrenamiento> semana) {
|
||||||
|
this.semana = semana;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class Sexo {
|
||||||
|
private String sexo;
|
||||||
|
|
||||||
|
public Sexo(String sexo) {
|
||||||
|
this.sexo = sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSexo() {
|
||||||
|
return sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSexo(String sexo) {
|
||||||
|
this.sexo = sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Sexo:" + sexo + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Usuario implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Sexo sexo;
|
||||||
|
private Objetivo objetivo;
|
||||||
|
private Intensidad intensidad;
|
||||||
|
private Edad edad;
|
||||||
|
private Peso peso;
|
||||||
|
private Altura altura;
|
||||||
|
private DiasEntrenamiento dias;
|
||||||
|
|
||||||
|
public Usuario(Sexo sexo, Objetivo objetivo, Intensidad intensidad,
|
||||||
|
Edad edad, Peso peso, Altura altura, DiasEntrenamiento dias) {
|
||||||
|
this.sexo = sexo;
|
||||||
|
this.objetivo = objetivo;
|
||||||
|
this.intensidad = intensidad;
|
||||||
|
this.edad = edad;
|
||||||
|
this.peso = peso;
|
||||||
|
this.altura = altura;
|
||||||
|
this.dias = dias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Usuario() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sexo getSexo() { return sexo; }
|
||||||
|
public Objetivo getObjetivo() { return objetivo; }
|
||||||
|
public Intensidad getIntensidad() { return intensidad; }
|
||||||
|
public Edad getEdad() { return edad; }
|
||||||
|
public Peso getPeso() { return peso; }
|
||||||
|
public Altura getAltura() { return altura; }
|
||||||
|
public DiasEntrenamiento getDias() { return dias; }
|
||||||
|
|
||||||
|
public void setSexo(Sexo sexo) {
|
||||||
|
this.sexo = sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObjetivo(Objetivo objetivo) {
|
||||||
|
this.objetivo = objetivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntensidad(Intensidad intensidad) {
|
||||||
|
this.intensidad = intensidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEdad(Edad edad) {
|
||||||
|
this.edad = edad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeso(Peso peso) {
|
||||||
|
this.peso = peso;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAltura(Altura altura) {
|
||||||
|
this.altura = altura;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDias(DiasEntrenamiento dias) {
|
||||||
|
this.dias = dias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("UsuarioFitness {\n");
|
||||||
|
sb.append(" Sexo = '").append(sexo != null ? sexo.getSexo() : "N/A").append("',\n");
|
||||||
|
sb.append(" Objetivo = '").append(objetivo != null ? objetivo.getObjetivo() : "N/A").append("',\n");
|
||||||
|
sb.append(" Intensidad = '").append(intensidad != null ? intensidad.getIntensidad() : "N/A").append("',\n");
|
||||||
|
sb.append(" Edad = ").append(edad != null ? edad.getEdad() : "N/A").append(",\n");
|
||||||
|
sb.append(" Peso = ").append(peso != null ? peso.getPeso() : "N/A").append(" kg,\n");
|
||||||
|
sb.append(" Altura = ").append(altura != null ? altura.getAltura() : "N/A").append(" cm,\n");
|
||||||
|
sb.append(" Días de entrenamiento = ").append(dias != null ? dias.getCantidad() : "N/A").append("\n}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,556 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.utils.PasswordUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UsuarioDbHelper extends SQLiteOpenHelper {
|
||||||
|
private static final String TAG = "UsuarioDbHelper";
|
||||||
|
|
||||||
|
private static final String DB_NAME = "elevate_fitness.db";
|
||||||
|
private static final int DB_VERSION = 2;
|
||||||
|
|
||||||
|
public static final String TABLE_USUARIOS = "usuarios";
|
||||||
|
|
||||||
|
public static final String COLUMN_ID = "id";
|
||||||
|
public static final String COLUMN_EMAIL = "email";
|
||||||
|
public static final String COLUMN_NOMBRE_COMPLETO = "nombre_completo";
|
||||||
|
public static final String COLUMN_PASSWORD_HASH = "password_hash";
|
||||||
|
public static final String COLUMN_SEXO = "sexo";
|
||||||
|
public static final String COLUMN_EDAD = "edad";
|
||||||
|
public static final String COLUMN_OBJETIVO = "objetivo";
|
||||||
|
public static final String COLUMN_DIAS_ENTRENAMIENTO = "dias_entrenamiento";
|
||||||
|
public static final String COLUMN_INTENSIDAD = "intensidad";
|
||||||
|
public static final String COLUMN_PESO = "peso";
|
||||||
|
public static final String COLUMN_ALTURA = "altura";
|
||||||
|
public static final String COLUMN_PLAN_JSON = "plan_json";
|
||||||
|
public static final String COLUMN_FECHA_REGISTRO = "fecha_registro";
|
||||||
|
|
||||||
|
public UsuarioDbHelper(Context context) {
|
||||||
|
super(context, DB_NAME, null, DB_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
String CREATE_USUARIOS_TABLE = "CREATE TABLE " + TABLE_USUARIOS + "("
|
||||||
|
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
|
+ COLUMN_EMAIL + " TEXT UNIQUE NOT NULL,"
|
||||||
|
+ COLUMN_NOMBRE_COMPLETO + " TEXT NOT NULL,"
|
||||||
|
+ COLUMN_PASSWORD_HASH + " TEXT NOT NULL,"
|
||||||
|
+ COLUMN_SEXO + " TEXT,"
|
||||||
|
+ COLUMN_EDAD + " INTEGER,"
|
||||||
|
+ COLUMN_OBJETIVO + " TEXT,"
|
||||||
|
+ COLUMN_DIAS_ENTRENAMIENTO + " INTEGER,"
|
||||||
|
+ COLUMN_INTENSIDAD + " TEXT,"
|
||||||
|
+ COLUMN_PESO + " REAL,"
|
||||||
|
+ COLUMN_ALTURA + " REAL,"
|
||||||
|
+ COLUMN_PLAN_JSON + " TEXT,"
|
||||||
|
+ COLUMN_FECHA_REGISTRO + " TEXT DEFAULT CURRENT_TIMESTAMP"
|
||||||
|
+ ")";
|
||||||
|
db.execSQL(CREATE_USUARIOS_TABLE);
|
||||||
|
Log.i(TAG, "Tabla usuarios creada.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(SQLiteDatabase db) {
|
||||||
|
String DROP_USUARIOS_TABLE = "DROP TABLE " + TABLE_USUARIOS;
|
||||||
|
db.execSQL(DROP_USUARIOS_TABLE);
|
||||||
|
Log.i(TAG, "Tabla usuarios eliminada.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
Log.w(TAG, "Actualizando base de datos de la versión " + oldVersion + " a " + newVersion + ", se perderán los datos antiguos.");
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_USUARIOS);
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long insertarUsuarioCompleto(UsuarioEntity usuario) {
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
|
values.put(COLUMN_EMAIL, usuario.getEmail());
|
||||||
|
values.put(COLUMN_NOMBRE_COMPLETO, usuario.getNombreCompleto());
|
||||||
|
values.put(COLUMN_PASSWORD_HASH, usuario.getPasswordHash());
|
||||||
|
values.put(COLUMN_SEXO, usuario.getSexo());
|
||||||
|
values.put(COLUMN_EDAD, usuario.getEdad());
|
||||||
|
values.put(COLUMN_OBJETIVO, usuario.getObjetivo());
|
||||||
|
values.put(COLUMN_DIAS_ENTRENAMIENTO, usuario.getDiasEntrenamiento());
|
||||||
|
values.put(COLUMN_INTENSIDAD, usuario.getIntensidad());
|
||||||
|
values.put(COLUMN_PESO, usuario.getPeso());
|
||||||
|
values.put(COLUMN_ALTURA, usuario.getAltura());
|
||||||
|
values.put(COLUMN_PLAN_JSON, usuario.getPlanJson());
|
||||||
|
|
||||||
|
long id = -1;
|
||||||
|
try {
|
||||||
|
id = db.insertOrThrow(TABLE_USUARIOS, null, values);
|
||||||
|
Log.i(TAG, "Nuevo usuario insertado con ID: " + id + " y email: " + usuario.getEmail());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al insertar usuario: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkUserEmailExists(String email) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
String[] columns = {COLUMN_ID};
|
||||||
|
String selection = COLUMN_EMAIL + " = ?";
|
||||||
|
String[] selectionArgs = {email};
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_USUARIOS, columns, selection, selectionArgs, null, null, null);
|
||||||
|
int count = cursor.getCount();
|
||||||
|
return count > 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al verificar email: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UsuarioEntity verificarUsuario(String email, String plainPassword) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
UsuarioEntity usuario = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_USUARIOS,
|
||||||
|
new String[]{COLUMN_ID, COLUMN_EMAIL, COLUMN_NOMBRE_COMPLETO, COLUMN_PASSWORD_HASH, COLUMN_SEXO, COLUMN_EDAD, COLUMN_OBJETIVO, COLUMN_DIAS_ENTRENAMIENTO, COLUMN_INTENSIDAD, COLUMN_PESO, COLUMN_ALTURA, COLUMN_PLAN_JSON, COLUMN_FECHA_REGISTRO},
|
||||||
|
COLUMN_EMAIL + " = ?",
|
||||||
|
new String[]{email},
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
String storedPasswordHash = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PASSWORD_HASH));
|
||||||
|
|
||||||
|
Log.d(TAG, "plainPassword: " + plainPassword);
|
||||||
|
Log.d(TAG, "storedPasswordHash: " + storedPasswordHash);
|
||||||
|
|
||||||
|
if (PasswordUtils.verifyPassword(plainPassword, storedPasswordHash)) {
|
||||||
|
usuario = new UsuarioEntity();
|
||||||
|
usuario.setId(cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_ID)));
|
||||||
|
usuario.setEmail(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_EMAIL)));
|
||||||
|
usuario.setNombreCompleto(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NOMBRE_COMPLETO)));
|
||||||
|
usuario.setPasswordHash(storedPasswordHash);
|
||||||
|
usuario.setSexo(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_SEXO)));
|
||||||
|
usuario.setEdad(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_EDAD)));
|
||||||
|
usuario.setObjetivo(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_OBJETIVO)));
|
||||||
|
usuario.setDiasEntrenamiento(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_DIAS_ENTRENAMIENTO)));
|
||||||
|
usuario.setIntentensidad(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_INTENSIDAD)));
|
||||||
|
usuario.setPeso(cursor.getDouble(cursor.getColumnIndexOrThrow(COLUMN_PESO)));
|
||||||
|
usuario.setAltura(cursor.getDouble(cursor.getColumnIndexOrThrow(COLUMN_ALTURA)));
|
||||||
|
usuario.setPlanJson(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PLAN_JSON)));
|
||||||
|
usuario.setFechaRegistro(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_FECHA_REGISTRO)));
|
||||||
|
Log.i(TAG, "Usuario verificado: " + email);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Contraseña incorrecta para el usuario: " + email);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Usuario no encontrado con email: " + email);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al verificar usuario: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return usuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UsuarioEntity getUsuarioPorEmail(String email) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
UsuarioEntity usuario = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_USUARIOS,
|
||||||
|
new String[]{COLUMN_ID, COLUMN_EMAIL, COLUMN_NOMBRE_COMPLETO, COLUMN_PASSWORD_HASH, COLUMN_SEXO, COLUMN_EDAD, COLUMN_OBJETIVO, COLUMN_DIAS_ENTRENAMIENTO, COLUMN_INTENSIDAD, COLUMN_PESO, COLUMN_ALTURA, COLUMN_PLAN_JSON, COLUMN_FECHA_REGISTRO},
|
||||||
|
COLUMN_EMAIL + " = ?",
|
||||||
|
new String[]{email},
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
usuario = new UsuarioEntity();
|
||||||
|
usuario.setId(cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_ID)));
|
||||||
|
usuario.setEmail(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_EMAIL)));
|
||||||
|
usuario.setNombreCompleto(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NOMBRE_COMPLETO)));
|
||||||
|
usuario.setPasswordHash(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PASSWORD_HASH)));
|
||||||
|
usuario.setSexo(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_SEXO)));
|
||||||
|
usuario.setEdad(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_EDAD)));
|
||||||
|
usuario.setObjetivo(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_OBJETIVO)));
|
||||||
|
usuario.setDiasEntrenamiento(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_DIAS_ENTRENAMIENTO)));
|
||||||
|
usuario.setIntentensidad(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_INTENSIDAD)));
|
||||||
|
usuario.setPeso(cursor.getDouble(cursor.getColumnIndexOrThrow(COLUMN_PESO)));
|
||||||
|
usuario.setAltura(cursor.getDouble(cursor.getColumnIndexOrThrow(COLUMN_ALTURA)));
|
||||||
|
usuario.setPlanJson(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PLAN_JSON)));
|
||||||
|
usuario.setFechaRegistro(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_FECHA_REGISTRO)));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al obtener usuario por email: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return usuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int actualizarUsuario(UsuarioEntity usuario) {
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
|
values.put(COLUMN_NOMBRE_COMPLETO, usuario.getNombreCompleto());
|
||||||
|
values.put(COLUMN_EMAIL, usuario.getEmail());
|
||||||
|
|
||||||
|
if (usuario.getPasswordHash() != null && !usuario.getPasswordHash().isEmpty()) {
|
||||||
|
values.put(COLUMN_PASSWORD_HASH, usuario.getPasswordHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
values.put(COLUMN_SEXO, usuario.getSexo());
|
||||||
|
values.put(COLUMN_EDAD, usuario.getEdad());
|
||||||
|
values.put(COLUMN_OBJETIVO, usuario.getObjetivo());
|
||||||
|
values.put(COLUMN_DIAS_ENTRENAMIENTO, usuario.getDiasEntrenamiento());
|
||||||
|
values.put(COLUMN_INTENSIDAD, usuario.getIntensidad());
|
||||||
|
values.put(COLUMN_PESO, usuario.getPeso());
|
||||||
|
values.put(COLUMN_ALTURA, usuario.getAltura());
|
||||||
|
|
||||||
|
int rowsAffected = 0;
|
||||||
|
try {
|
||||||
|
rowsAffected = db.update(TABLE_USUARIOS, values, COLUMN_ID + " = ?",
|
||||||
|
new String[]{String.valueOf(usuario.getId())});
|
||||||
|
|
||||||
|
if (rowsAffected > 0) {
|
||||||
|
Log.i(TAG, "Usuario actualizado correctamente: " + usuario.getEmail());
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "No se pudo actualizar el usuario: " + usuario.getEmail());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al actualizar usuario: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return rowsAffected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean migrarPlanJson(String emailAnterior, String emailNuevo) {
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
|
try {
|
||||||
|
String planJson = obtenerPlanJson(emailAnterior);
|
||||||
|
|
||||||
|
if (planJson != null && !planJson.trim().isEmpty()) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_PLAN_JSON, planJson);
|
||||||
|
|
||||||
|
int updated = db.update(TABLE_USUARIOS, values, COLUMN_EMAIL + " = ?",
|
||||||
|
new String[]{emailNuevo});
|
||||||
|
|
||||||
|
Log.d(TAG, "Plan JSON migrado de " + emailAnterior + " a " + emailNuevo);
|
||||||
|
return updated > 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al migrar plan JSON: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int updatePlanJson(String email, String nuevoPlanJson) {
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_PLAN_JSON, nuevoPlanJson);
|
||||||
|
|
||||||
|
int rowsAffected = 0;
|
||||||
|
try {
|
||||||
|
rowsAffected = db.update(TABLE_USUARIOS, values, COLUMN_EMAIL + " = ?", new String[]{email});
|
||||||
|
if (rowsAffected > 0) {
|
||||||
|
Log.i(TAG, "Plan JSON actualizado para el usuario: " + email);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "No se pudo actualizar el plan JSON para el usuario (no encontrado o sin cambios): " + email);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al actualizar plan JSON: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return rowsAffected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void actualizarPlanJson(String email, String planJson) {
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_PLAN_JSON, planJson);
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.update(TABLE_USUARIOS, values, COLUMN_EMAIL + " = ?", new String[]{email});
|
||||||
|
Log.i(TAG, "Plan JSON actualizado (método legacy) para: " + email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al actualizar plan JSON (método legacy): " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String obtenerPlanJson(String email) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
String planJson = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_USUARIOS,
|
||||||
|
new String[]{COLUMN_PLAN_JSON},
|
||||||
|
COLUMN_EMAIL + "=?",
|
||||||
|
new String[]{email},
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
planJson = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PLAN_JSON));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al obtener plan JSON: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return planJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean guardarPlanJsonEnArchivo(Context context, String email) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_USUARIOS,
|
||||||
|
new String[]{COLUMN_PLAN_JSON},
|
||||||
|
COLUMN_EMAIL + "=?",
|
||||||
|
new String[]{email},
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
String planJson = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PLAN_JSON));
|
||||||
|
|
||||||
|
if (planJson != null && !planJson.trim().isEmpty()) {
|
||||||
|
planJson = planJson.replace("```json", "").replace("```", "").trim();
|
||||||
|
|
||||||
|
String fileName = "plan_" + email.replace("@", "_").replace(".", "_") + ".json";
|
||||||
|
|
||||||
|
File jsonFile = new File(context.getFilesDir(), fileName);
|
||||||
|
FileWriter writer = new FileWriter(jsonFile);
|
||||||
|
writer.write(planJson);
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
Log.i(TAG, "Plan guardado en archivo: " + fileName);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error al guardar JSON en archivo interno: ", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error general al guardar JSON: ", e);
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String cargarPlanJsonDesdeArchivo(Context context, String email) {
|
||||||
|
try {
|
||||||
|
String fileName = "plan_" + email.replace("@", "_").replace(".", "_") + ".json";
|
||||||
|
File jsonFile = new File(context.getFilesDir(), fileName);
|
||||||
|
|
||||||
|
if (!jsonFile.exists()) {
|
||||||
|
Log.w(TAG, "Archivo no encontrado: " + fileName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder jsonContent = new StringBuilder();
|
||||||
|
java.io.FileInputStream fis = new java.io.FileInputStream(jsonFile);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||||
|
jsonContent.append(new String(buffer, 0, bytesRead, "UTF-8"));
|
||||||
|
}
|
||||||
|
fis.close();
|
||||||
|
|
||||||
|
Log.i(TAG, "Plan cargado desde archivo: " + fileName);
|
||||||
|
return jsonContent.toString();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error al cargar JSON desde archivo: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existePlanJsonArchivo(Context context, String email) {
|
||||||
|
String fileName = "plan_" + email.replace("@", "_").replace(".", "_") + ".json";
|
||||||
|
File jsonFile = new File(context.getFilesDir(), fileName);
|
||||||
|
return jsonFile.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean eliminarPlanJsonArchivo(Context context, String email) {
|
||||||
|
try {
|
||||||
|
String fileName = "plan_" + email.replace("@", "_").replace(".", "_") + ".json";
|
||||||
|
File jsonFile = new File(context.getFilesDir(), fileName);
|
||||||
|
|
||||||
|
if (jsonFile.exists()) {
|
||||||
|
boolean deleted = jsonFile.delete();
|
||||||
|
Log.i(TAG, "Archivo eliminado: " + fileName + " - Éxito: " + deleted);
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al eliminar archivo JSON: ", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> listarPlanesExistentes(Context context) {
|
||||||
|
List<String> planes = new ArrayList<>();
|
||||||
|
File filesDir = context.getFilesDir();
|
||||||
|
File[] files = filesDir.listFiles();
|
||||||
|
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.getName().startsWith("plan_") && file.getName().endsWith(".json")) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
String email = fileName.replace("plan_", "").replace(".json", "")
|
||||||
|
.replace("_", "@");
|
||||||
|
planes.add(email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return planes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String obtenerNombreUsuario(String email) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
String nombre = "Usuario";
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.rawQuery("SELECT nombre_completo FROM usuarios WHERE email = ?", new String[]{email});
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
nombre = cursor.getString(0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al obtener nombre de usuario: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return nombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean eliminarUsuario(String email) {
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
|
try {
|
||||||
|
int deleted = db.delete(TABLE_USUARIOS, COLUMN_EMAIL + " = ?", new String[]{email});
|
||||||
|
if (deleted > 0) {
|
||||||
|
Log.i(TAG, "Usuario eliminado: " + email);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "No se encontró usuario para eliminar: " + email);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al eliminar usuario: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UsuarioEntity> obtenerTodosLosUsuarios() {
|
||||||
|
List<UsuarioEntity> usuarios = new ArrayList<>();
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_USUARIOS, null, null, null, null, null, COLUMN_FECHA_REGISTRO + " DESC");
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
UsuarioEntity usuario = new UsuarioEntity();
|
||||||
|
usuario.setId(cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_ID)));
|
||||||
|
usuario.setEmail(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_EMAIL)));
|
||||||
|
usuario.setNombreCompleto(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NOMBRE_COMPLETO)));
|
||||||
|
usuario.setPasswordHash(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PASSWORD_HASH)));
|
||||||
|
usuario.setSexo(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_SEXO)));
|
||||||
|
usuario.setEdad(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_EDAD)));
|
||||||
|
usuario.setObjetivo(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_OBJETIVO)));
|
||||||
|
usuario.setDiasEntrenamiento(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_DIAS_ENTRENAMIENTO)));
|
||||||
|
usuario.setIntentensidad(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_INTENSIDAD)));
|
||||||
|
usuario.setPeso(cursor.getDouble(cursor.getColumnIndexOrThrow(COLUMN_PESO)));
|
||||||
|
usuario.setAltura(cursor.getDouble(cursor.getColumnIndexOrThrow(COLUMN_ALTURA)));
|
||||||
|
usuario.setPlanJson(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PLAN_JSON)));
|
||||||
|
usuario.setFechaRegistro(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_FECHA_REGISTRO)));
|
||||||
|
usuarios.add(usuario);
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al obtener todos los usuarios: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return usuarios;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int contarUsuarios() {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.rawQuery("SELECT COUNT(*) FROM " + TABLE_USUARIOS, null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
count = cursor.getInt(0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al contar usuarios: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class UsuarioEntity {
|
||||||
|
private long id;
|
||||||
|
private String email;
|
||||||
|
private String nombreCompleto;
|
||||||
|
private String passwordHash;
|
||||||
|
private String sexo;
|
||||||
|
private int edad;
|
||||||
|
private String objetivo;
|
||||||
|
private int diasEntrenamiento;
|
||||||
|
private String intensidad;
|
||||||
|
private double peso;
|
||||||
|
private double altura;
|
||||||
|
private String planJson;
|
||||||
|
private String fechaRegistro;
|
||||||
|
|
||||||
|
public UsuarioEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsuarioEntity(String email, String nombreCompleto, String passwordHash,
|
||||||
|
String sexo, int edad, String objetivo, int diasEntrenamiento,
|
||||||
|
String intensidad, double peso, double altura, String planJson) {
|
||||||
|
this.email = email;
|
||||||
|
this.nombreCompleto = nombreCompleto;
|
||||||
|
this.passwordHash = passwordHash;
|
||||||
|
this.sexo = sexo;
|
||||||
|
this.edad = edad;
|
||||||
|
this.objetivo = objetivo;
|
||||||
|
this.diasEntrenamiento = diasEntrenamiento;
|
||||||
|
this.intensidad = intensidad;
|
||||||
|
this.peso = peso;
|
||||||
|
this.altura = altura;
|
||||||
|
this.planJson = planJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombreCompleto() {
|
||||||
|
return nombreCompleto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNombreCompleto(String nombreCompleto) {
|
||||||
|
this.nombreCompleto = nombreCompleto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPasswordHash() {
|
||||||
|
return passwordHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPasswordHash(String passwordHash) {
|
||||||
|
this.passwordHash = passwordHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSexo() {
|
||||||
|
return sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSexo(String sexo) {
|
||||||
|
this.sexo = sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEdad() {
|
||||||
|
return edad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEdad(int edad) {
|
||||||
|
this.edad = edad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getObjetivo() {
|
||||||
|
return objetivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObjetivo(String objetivo) {
|
||||||
|
this.objetivo = objetivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDiasEntrenamiento() {
|
||||||
|
return diasEntrenamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiasEntrenamiento(int diasEntrenamiento) {
|
||||||
|
this.diasEntrenamiento = diasEntrenamiento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIntensidad() {
|
||||||
|
return intensidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntentensidad(String intensidad) {
|
||||||
|
this.intensidad = intensidad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPeso() {
|
||||||
|
return peso;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeso(double peso) {
|
||||||
|
this.peso = peso;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAltura() {
|
||||||
|
return altura;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAltura(double altura) {
|
||||||
|
this.altura = altura;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlanJson() {
|
||||||
|
return planJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlanJson(String planJson) {
|
||||||
|
this.planJson = planJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFechaRegistro() {
|
||||||
|
return fechaRegistro;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFechaRegistro(String fechaRegistro) {
|
||||||
|
this.fechaRegistro = fechaRegistro;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UsuarioEntity{" +
|
||||||
|
"id=" + id +
|
||||||
|
", email='" + email + '\'' +
|
||||||
|
", nombreCompleto='" + nombreCompleto + '\'' +
|
||||||
|
", sexo='" + sexo + '\'' +
|
||||||
|
", edad=" + edad +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.borjabolufer.elevate.model;
|
||||||
|
|
||||||
|
public class ValorNutricional {
|
||||||
|
public int calorías;
|
||||||
|
public int proteínas;
|
||||||
|
public int carbohidratos;
|
||||||
|
public int grasas;
|
||||||
|
|
||||||
|
public ValorNutricional(int calorías, int proteínas, int carbohidratos, int grasas) {
|
||||||
|
this.calorías = calorías;
|
||||||
|
this.proteínas = proteínas;
|
||||||
|
this.carbohidratos = carbohidratos;
|
||||||
|
this.grasas = grasas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCalorías() {
|
||||||
|
return calorías;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCalorías(int calorías) {
|
||||||
|
this.calorías = calorías;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProteínas() {
|
||||||
|
return proteínas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProteínas(int proteínas) {
|
||||||
|
this.proteínas = proteínas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCarbohidratos() {
|
||||||
|
return carbohidratos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCarbohidratos(int carbohidratos) {
|
||||||
|
this.carbohidratos = carbohidratos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGrasas() {
|
||||||
|
return grasas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrasas(int grasas) {
|
||||||
|
this.grasas = grasas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Altura;
|
||||||
|
|
||||||
|
public class AlturaFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnAlturaListener {
|
||||||
|
void onAlturaSelected(Altura altura);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnAlturaListener alturaListener;
|
||||||
|
private EditText editAltura;
|
||||||
|
private Button btnContinuar;
|
||||||
|
|
||||||
|
public AlturaFragment() {
|
||||||
|
super(R.layout.fragment_altura);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
initializeViews(view);
|
||||||
|
setupTextWatcher();
|
||||||
|
setupClickListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeViews(View view) {
|
||||||
|
editAltura = view.findViewById(R.id.editAltura);
|
||||||
|
btnContinuar = view.findViewById(R.id.btnContinuarAltura);
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
btnContinuar.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTextWatcher() {
|
||||||
|
editAltura.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
validateInput(s.toString().trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateInput(String input) {
|
||||||
|
boolean isValid = false;
|
||||||
|
|
||||||
|
if (!input.isEmpty()) {
|
||||||
|
try {
|
||||||
|
int altura = Integer.parseInt(input);
|
||||||
|
|
||||||
|
if (altura >= 100 && altura <= 250) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(isValid);
|
||||||
|
btnContinuar.setAlpha(isValid ? 1.0f : 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupClickListener() {
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
String alturaTexto = editAltura.getText().toString().trim();
|
||||||
|
|
||||||
|
if (alturaTexto.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, ingresa tu altura", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int alturaValor = Integer.parseInt(alturaTexto);
|
||||||
|
|
||||||
|
if (alturaValor < 100 || alturaValor > 250) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, ingresa una altura entre 100 y 250 cm",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Altura altura = new Altura(alturaValor);
|
||||||
|
|
||||||
|
if (alturaListener != null) {
|
||||||
|
alturaListener.onAlturaSelected(altura);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Error: No se pudo procesar la altura",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, ingresa un número válido",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnAlturaListener) {
|
||||||
|
alturaListener = (IOnAlturaListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement IOnAlturaListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
alturaListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.databinding.FragmentComidaDetalleBinding;
|
||||||
|
import com.borjabolufer.elevate.model.Comida;
|
||||||
|
import com.borjabolufer.elevate.model.ValorNutricional;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DetalleComidaFragment extends Fragment {
|
||||||
|
|
||||||
|
private static final String TAG = "DetalleComidaFragment";
|
||||||
|
private FragmentComidaDetalleBinding binding;
|
||||||
|
private Comida comida;
|
||||||
|
private String tipoComida;
|
||||||
|
private String nombreComida;
|
||||||
|
|
||||||
|
public static DetalleComidaFragment newInstance(Comida comida, String tipo, String nombre) {
|
||||||
|
DetalleComidaFragment fragment = new DetalleComidaFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable("comida", comida);
|
||||||
|
args.putString("tipo_comida", tipo);
|
||||||
|
args.putString("nombre_comida", nombre);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
comida = (Comida) getArguments().getSerializable("comida");
|
||||||
|
tipoComida = getArguments().getString("tipo_comida", "");
|
||||||
|
nombreComida = getArguments().getString("nombre_comida", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
binding = FragmentComidaDetalleBinding.inflate(inflater, container, false);
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
if (comida != null) {
|
||||||
|
configurarUI();
|
||||||
|
} else {
|
||||||
|
mostrarError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarUI() {
|
||||||
|
String titulo = nombreComida != null && !nombreComida.isEmpty() ? nombreComida : comida.getNombre();
|
||||||
|
binding.textComidaTitle.setText(titulo);
|
||||||
|
|
||||||
|
configurarIconoComida();
|
||||||
|
|
||||||
|
|
||||||
|
configurarInformacionNutricional();
|
||||||
|
|
||||||
|
|
||||||
|
configurarIngredientes();
|
||||||
|
|
||||||
|
|
||||||
|
configurarReceta();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarIconoComida() {
|
||||||
|
int iconResource;
|
||||||
|
int tintColor;
|
||||||
|
|
||||||
|
switch (tipoComida.toLowerCase()) {
|
||||||
|
case "desayuno":
|
||||||
|
iconResource = R.drawable.ic_desayuno;
|
||||||
|
tintColor = R.color.warning;
|
||||||
|
break;
|
||||||
|
case "almuerzo":
|
||||||
|
case "comida":
|
||||||
|
iconResource = R.drawable.ic_comida;
|
||||||
|
tintColor = R.color.primary;
|
||||||
|
break;
|
||||||
|
case "cena":
|
||||||
|
iconResource = R.drawable.ic_cena;
|
||||||
|
tintColor = R.color.info;
|
||||||
|
break;
|
||||||
|
case "snack":
|
||||||
|
case "snacks":
|
||||||
|
iconResource = R.drawable.ic_snack;
|
||||||
|
tintColor = R.color.success;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iconResource = R.drawable.ic_restaurante;
|
||||||
|
tintColor = R.color.primary;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.iconComidaDetalle.setImageResource(iconResource);
|
||||||
|
binding.iconComidaDetalle.setColorFilter(requireContext().getColor(tintColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarInformacionNutricional() {
|
||||||
|
ValorNutricional vn = comida.getValor_nutricional();
|
||||||
|
|
||||||
|
if (vn != null) {
|
||||||
|
binding.textCaloriesValue.setText(vn.getCalorías() + " kcal");
|
||||||
|
binding.textProteinsValue.setText(vn.getProteínas() + "g");
|
||||||
|
binding.textCarbsValue.setText(vn.getCarbohidratos() + "g");
|
||||||
|
binding.textFatsValue.setText(vn.getGrasas() + "g");
|
||||||
|
} else {
|
||||||
|
binding.textCaloriesValue.setText("-- kcal");
|
||||||
|
binding.textProteinsValue.setText("--g");
|
||||||
|
binding.textCarbsValue.setText("--g");
|
||||||
|
binding.textFatsValue.setText("--g");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarIngredientes() {
|
||||||
|
List<String> ingredientes = comida.getIngredientes();
|
||||||
|
|
||||||
|
if (ingredientes != null && !ingredientes.isEmpty()) {
|
||||||
|
StringBuilder ingredientesTexto = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < ingredientes.size(); i++) {
|
||||||
|
ingredientesTexto.append("• ").append(ingredientes.get(i));
|
||||||
|
if (i < ingredientes.size() - 1) {
|
||||||
|
ingredientesTexto.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.textIngredientsContent.setText(ingredientesTexto.toString());
|
||||||
|
} else {
|
||||||
|
binding.textIngredientsContent.setText("No se especificaron ingredientes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarReceta() {
|
||||||
|
String receta = comida.getReceta();
|
||||||
|
|
||||||
|
if (receta != null && !receta.trim().isEmpty()) {
|
||||||
|
|
||||||
|
String recetaFormateada = formatearReceta(receta);
|
||||||
|
binding.textRecipeContent.setText(recetaFormateada);
|
||||||
|
} else {
|
||||||
|
binding.textRecipeContent.setText("No se proporcionaron instrucciones de preparación");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatearReceta(String receta) {
|
||||||
|
|
||||||
|
if (receta == null) return "";
|
||||||
|
|
||||||
|
return receta
|
||||||
|
.trim()
|
||||||
|
.replaceAll("\\s*\\n\\s*", "\n")
|
||||||
|
.replaceAll("(\\d+\\.)\\s*", "\n$1 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void mostrarError() {
|
||||||
|
Toast.makeText(getContext(), "Error al cargar la información de la comida", Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
|
||||||
|
if (getParentFragmentManager().getBackStackEntryCount() > 0) {
|
||||||
|
getParentFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Comida getComida() {
|
||||||
|
return comida;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTipoComida() {
|
||||||
|
return tipoComida;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombreComida() {
|
||||||
|
return nombreComida;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getCaloriasComida() {
|
||||||
|
if (comida != null && comida.getValor_nutricional() != null) {
|
||||||
|
return comida.getValor_nutricional().getCalorías();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,656 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.widget.VideoView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Ejercicio;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.chip.Chip;
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants;
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer;
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener;
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView;
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.ui.DefaultPlayerUiController;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class DetalleEjercicioFragment extends Fragment implements MediaPlayer.OnPreparedListener,
|
||||||
|
MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
|
||||||
|
|
||||||
|
private static final String TAG = "DetalleEjercicioFragment";
|
||||||
|
private static final String ARG_EJERCICIO = "ejercicio";
|
||||||
|
private static final String ARG_TIPO = "tipo";
|
||||||
|
|
||||||
|
private Ejercicio ejercicio;
|
||||||
|
private String tipoEjercicio;
|
||||||
|
|
||||||
|
|
||||||
|
private TextView nombreTextView;
|
||||||
|
private TextView descripcionTextView;
|
||||||
|
private TextView grupoMuscularTextView;
|
||||||
|
private TextView nivelDificultadTextView;
|
||||||
|
private TextView seriesTextView;
|
||||||
|
private TextView repeticionesTextView;
|
||||||
|
private TextView descansoTextView;
|
||||||
|
private Chip chipTipoEjercicio;
|
||||||
|
|
||||||
|
|
||||||
|
private ImageButton btnVolver;
|
||||||
|
|
||||||
|
|
||||||
|
private VideoView videoView;
|
||||||
|
private ImageButton btnPlayPause;
|
||||||
|
private SeekBar seekBar;
|
||||||
|
private TextView durationTextView;
|
||||||
|
private MaterialButton externalVideoButton;
|
||||||
|
|
||||||
|
|
||||||
|
private View descripcionContainer;
|
||||||
|
private View grupoMuscularContainer;
|
||||||
|
private View nivelDificultadContainer;
|
||||||
|
private View parametersContainer;
|
||||||
|
private View videoContainer;
|
||||||
|
private View noVideoOverlay;
|
||||||
|
private View duracionContainer;
|
||||||
|
|
||||||
|
|
||||||
|
private Handler handler = new Handler();
|
||||||
|
private boolean isVideoReady = false;
|
||||||
|
private boolean isPlaying = false;
|
||||||
|
|
||||||
|
private YouTubePlayerView youTubePlayerView;
|
||||||
|
|
||||||
|
public static DetalleEjercicioFragment newInstance(Ejercicio ejercicio) {
|
||||||
|
return newInstance(ejercicio, "principal");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DetalleEjercicioFragment newInstance(Ejercicio ejercicio, String tipo) {
|
||||||
|
DetalleEjercicioFragment fragment = new DetalleEjercicioFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_EJERCICIO, ejercicio);
|
||||||
|
args.putString(ARG_TIPO, tipo);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
ejercicio = (Ejercicio) getArguments().getSerializable(ARG_EJERCICIO);
|
||||||
|
tipoEjercicio = getArguments().getString(ARG_TIPO, "principal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_ejercicio_detalle, container, false);
|
||||||
|
|
||||||
|
inicializarViews(view);
|
||||||
|
configurarBotonVolver();
|
||||||
|
configurarVistasSegunTipo();
|
||||||
|
configurarVideo();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inicializarViews(View view) {
|
||||||
|
|
||||||
|
nombreTextView = view.findViewById(R.id.text_exercise_title);
|
||||||
|
descripcionTextView = view.findViewById(R.id.text_description_content);
|
||||||
|
grupoMuscularTextView = view.findViewById(R.id.text_grupo_muscular);
|
||||||
|
nivelDificultadTextView = view.findViewById(R.id.text_nivel_dificultad);
|
||||||
|
seriesTextView = view.findViewById(R.id.text_series_value);
|
||||||
|
repeticionesTextView = view.findViewById(R.id.text_repetitions_value);
|
||||||
|
descansoTextView = view.findViewById(R.id.text_rest_value);
|
||||||
|
chipTipoEjercicio = view.findViewById(R.id.chip_tipo_ejercicio);
|
||||||
|
youTubePlayerView = view.findViewById(R.id.youtube_player_view);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
externalVideoButton = view.findViewById(R.id.button_external_video);
|
||||||
|
|
||||||
|
|
||||||
|
descripcionContainer = view.findViewById(R.id.description_container);
|
||||||
|
grupoMuscularContainer = view.findViewById(R.id.grupo_muscular_container);
|
||||||
|
nivelDificultadContainer = view.findViewById(R.id.nivel_dificultad_container);
|
||||||
|
parametersContainer = view.findViewById(R.id.parameters_container);
|
||||||
|
videoContainer = view.findViewById(R.id.video_container);
|
||||||
|
noVideoOverlay = view.findViewById(R.id.no_video_overlay);
|
||||||
|
duracionContainer = view.findViewById(R.id.duracion_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void configurarBotonVolver() {
|
||||||
|
if (btnVolver != null) {
|
||||||
|
btnVolver.setOnClickListener(v -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
androidx.navigation.NavController navController =
|
||||||
|
androidx.navigation.Navigation.findNavController(requireView());
|
||||||
|
navController.popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
requireActivity().getSupportFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarVistasSegunTipo() {
|
||||||
|
|
||||||
|
nombreTextView.setText(ejercicio.getNombre());
|
||||||
|
|
||||||
|
|
||||||
|
configurarChipTipo();
|
||||||
|
|
||||||
|
switch (tipoEjercicio.toLowerCase()) {
|
||||||
|
case "calentamiento":
|
||||||
|
configurarParaCalentamiento();
|
||||||
|
break;
|
||||||
|
case "enfriamiento":
|
||||||
|
configurarParaEnfriamiento();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
configurarParaEjercicioPrincipal();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarChipTipo() {
|
||||||
|
if (chipTipoEjercicio != null) {
|
||||||
|
switch (tipoEjercicio.toLowerCase()) {
|
||||||
|
case "calentamiento":
|
||||||
|
chipTipoEjercicio.setText("🔥 Calentamiento");
|
||||||
|
chipTipoEjercicio.setChipBackgroundColorResource(R.color.warning);
|
||||||
|
break;
|
||||||
|
case "enfriamiento":
|
||||||
|
chipTipoEjercicio.setText("🧘 Enfriamiento");
|
||||||
|
chipTipoEjercicio.setChipBackgroundColorResource(R.color.success);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
chipTipoEjercicio.setText("💪 Principal");
|
||||||
|
chipTipoEjercicio.setChipBackgroundColorResource(R.color.primary);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarParaCalentamiento() {
|
||||||
|
Log.d(TAG, "Configurando vista para calentamiento");
|
||||||
|
|
||||||
|
|
||||||
|
mostrarVista(descripcionContainer);
|
||||||
|
descripcionTextView.setText(extraerDescripcionLimpia());
|
||||||
|
descansoTextView.setText(ejercicio.getDescanso());
|
||||||
|
mostrarVista(parametersContainer);
|
||||||
|
|
||||||
|
configurarVideoParaCalentamientoEnfriamiento();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarParaEnfriamiento() {
|
||||||
|
Log.d(TAG, "Configurando vista para enfriamiento");
|
||||||
|
|
||||||
|
|
||||||
|
mostrarVista(descripcionContainer);
|
||||||
|
mostrarVista(parametersContainer);
|
||||||
|
|
||||||
|
|
||||||
|
descripcionTextView.setText(extraerDescripcionLimpia());
|
||||||
|
|
||||||
|
descansoTextView.setText(ejercicio.getDescanso() != null ? ejercicio.getDescanso() : "No especificado");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarParaEjercicioPrincipal() {
|
||||||
|
Log.d(TAG, "Configurando vista para ejercicio principal");
|
||||||
|
|
||||||
|
|
||||||
|
mostrarVista(descripcionContainer);
|
||||||
|
mostrarVista(parametersContainer);
|
||||||
|
|
||||||
|
|
||||||
|
descripcionTextView.setText(extraerDescripcionLimpia());
|
||||||
|
seriesTextView.setText(String.valueOf(ejercicio.getSeries()));
|
||||||
|
repeticionesTextView.setText(String.valueOf(ejercicio.getRepeticiones()));
|
||||||
|
descansoTextView.setText(ejercicio.getDescanso() != null ? ejercicio.getDescanso() : "No especificado");
|
||||||
|
|
||||||
|
|
||||||
|
String grupoMuscular = extraerGrupoMuscular();
|
||||||
|
String nivelDificultad = extraerNivelDificultad();
|
||||||
|
|
||||||
|
if (!grupoMuscular.isEmpty()) {
|
||||||
|
mostrarVista(grupoMuscularContainer);
|
||||||
|
grupoMuscularTextView.setText(grupoMuscular);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nivelDificultad.isEmpty()) {
|
||||||
|
mostrarVista(nivelDificultadContainer);
|
||||||
|
nivelDificultadTextView.setText(nivelDificultad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String extraerDescripcionLimpia() {
|
||||||
|
String descripcion = ejercicio.getDescripcion();
|
||||||
|
if (descripcion == null || descripcion.trim().isEmpty()) {
|
||||||
|
return "Sin descripción disponible";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return descripcion
|
||||||
|
.replaceAll("\\s*\\|\\s*Grupo muscular:.*?(?=\\||$)", "")
|
||||||
|
.replaceAll("\\s*\\|\\s*Nivel:.*?(?=\\||$)", "")
|
||||||
|
.replaceAll("\\s*\\|\\s*Duración:.*?(?=\\||$)", "")
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extraerGrupoMuscular() {
|
||||||
|
String descripcion = ejercicio.getDescripcion();
|
||||||
|
if (descripcion != null && descripcion.contains("Grupo muscular:")) {
|
||||||
|
String[] partes = descripcion.split("\\|");
|
||||||
|
for (String parte : partes) {
|
||||||
|
if (parte.trim().startsWith("Grupo muscular:")) {
|
||||||
|
return parte.replace("Grupo muscular:", "").trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extraerNivelDificultad() {
|
||||||
|
String descripcion = ejercicio.getDescripcion();
|
||||||
|
if (descripcion != null && descripcion.contains("Nivel:")) {
|
||||||
|
String[] partes = descripcion.split("\\|");
|
||||||
|
for (String parte : partes) {
|
||||||
|
if (parte.trim().startsWith("Nivel:")) {
|
||||||
|
return parte.replace("Nivel:", "").trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extraerDuracion() {
|
||||||
|
String descripcion = ejercicio.getDescripcion();
|
||||||
|
if (descripcion != null && descripcion.contains("Duración:")) {
|
||||||
|
String[] partes = descripcion.split("\\|");
|
||||||
|
for (String parte : partes) {
|
||||||
|
if (parte.trim().startsWith("Duración:")) {
|
||||||
|
return parte.replace("Duración:", "").trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarVideoParaCalentamientoEnfriamiento() {
|
||||||
|
String videoUrl = ejercicio.getVideoUrl();
|
||||||
|
|
||||||
|
if (videoUrl == null || videoUrl.trim().isEmpty()) {
|
||||||
|
|
||||||
|
ocultarVista(videoContainer);
|
||||||
|
ocultarVista(youTubePlayerView);
|
||||||
|
ocultarVista(externalVideoButton);
|
||||||
|
ocultarVista(noVideoOverlay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ocultarVista(videoContainer);
|
||||||
|
ocultarVista(youTubePlayerView);
|
||||||
|
ocultarVista(noVideoOverlay);
|
||||||
|
mostrarVista(externalVideoButton);
|
||||||
|
|
||||||
|
|
||||||
|
if (esYouTubeUrl(videoUrl)) {
|
||||||
|
externalVideoButton.setText("📹 Ver Video de " +
|
||||||
|
(tipoEjercicio.equals("calentamiento") ? "Calentamiento" : "Enfriamiento"));
|
||||||
|
} else {
|
||||||
|
externalVideoButton.setText("📹 Ver Video Demostrativo");
|
||||||
|
}
|
||||||
|
|
||||||
|
externalVideoButton.setOnClickListener(v -> {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(videoUrl));
|
||||||
|
|
||||||
|
|
||||||
|
if (esYouTubeUrl(videoUrl)) {
|
||||||
|
intent.setPackage("com.google.android.youtube");
|
||||||
|
if (intent.resolveActivity(requireContext().getPackageManager()) != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
intent.setPackage(null);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error abriendo video externo", e);
|
||||||
|
Toast.makeText(getContext(), "No se pudo abrir el video", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private void configurarVideo() {
|
||||||
|
String videoUrl = ejercicio.getVideoUrl();
|
||||||
|
|
||||||
|
if (videoUrl == null || videoUrl.trim().isEmpty()) {
|
||||||
|
mostrarVistaNoVideo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esYouTubeUrl(videoUrl)) {
|
||||||
|
|
||||||
|
String videoId = extraerIdVideoYouTube(videoUrl);
|
||||||
|
configurarVideoYouTube(videoId);
|
||||||
|
} else if (esVideoLocal(videoUrl)) {
|
||||||
|
configurarVideoLocal(videoUrl);
|
||||||
|
} else {
|
||||||
|
configurarVideoDirecto(videoUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extraerIdVideoYouTube(String url) {
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile("(?<=watch\\?v=|/videos/|embed\\/|youtu.be\\/|\\/v\\/|\\/e\\/|watch\\?feature=player_embedded&v=)([^#\\&\\?]*)");
|
||||||
|
Matcher matcher = pattern.matcher(url);
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarVideoYouTube(String videoId) {
|
||||||
|
if (videoId == null) {
|
||||||
|
mostrarVistaNoVideo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ocultarVista(noVideoOverlay);
|
||||||
|
ocultarVista(externalVideoButton);
|
||||||
|
mostrarVista(youTubePlayerView);
|
||||||
|
|
||||||
|
getLifecycle().addObserver(youTubePlayerView);
|
||||||
|
|
||||||
|
youTubePlayerView.addYouTubePlayerListener(new AbstractYouTubePlayerListener() {
|
||||||
|
@Override
|
||||||
|
public void onReady(@NonNull YouTubePlayer youTubePlayer) {
|
||||||
|
youTubePlayer.loadVideo(videoId, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull YouTubePlayer youTubePlayer, @NonNull PlayerConstants.PlayerError error) {
|
||||||
|
Log.e(TAG, "Error en YouTube Player: " + error.name());
|
||||||
|
configurarVideoExterno(ejercicio.getVideoUrl());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean esVideoLocal(String url) {
|
||||||
|
return url.startsWith("android.resource://") ||
|
||||||
|
url.startsWith("file://") ||
|
||||||
|
(!url.startsWith("http://") && !url.startsWith("https://"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean esYouTubeUrl(String url) {
|
||||||
|
return url.contains("youtube.com") || url.contains("youtu.be");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarVideoLocal(String videoUrl) {
|
||||||
|
try {
|
||||||
|
mostrarVista(videoContainer);
|
||||||
|
ocultarVista(noVideoOverlay);
|
||||||
|
ocultarVista(externalVideoButton);
|
||||||
|
|
||||||
|
Uri videoUri = Uri.parse(videoUrl);
|
||||||
|
videoView.setVideoURI(videoUri);
|
||||||
|
videoView.setOnPreparedListener(this);
|
||||||
|
videoView.setOnCompletionListener(this);
|
||||||
|
videoView.setOnErrorListener(this);
|
||||||
|
|
||||||
|
configurarControlesVideo();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error configurando video local", e);
|
||||||
|
mostrarVistaNoVideo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarVideoDirecto(String videoUrl) {
|
||||||
|
try {
|
||||||
|
mostrarVista(videoContainer);
|
||||||
|
ocultarVista(noVideoOverlay);
|
||||||
|
ocultarVista(externalVideoButton);
|
||||||
|
|
||||||
|
Uri videoUri = Uri.parse(videoUrl);
|
||||||
|
videoView.setVideoURI(videoUri);
|
||||||
|
videoView.setOnPreparedListener(this);
|
||||||
|
videoView.setOnCompletionListener(this);
|
||||||
|
videoView.setOnErrorListener(this);
|
||||||
|
|
||||||
|
configurarControlesVideo();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error configurando video directo", e);
|
||||||
|
configurarVideoExterno(videoUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarVideoExterno(String videoUrl) {
|
||||||
|
ocultarVista(videoContainer);
|
||||||
|
ocultarVista(noVideoOverlay);
|
||||||
|
mostrarVista(externalVideoButton);
|
||||||
|
|
||||||
|
externalVideoButton.setText("▶️ Ver en YouTube");
|
||||||
|
externalVideoButton.setOnClickListener(v -> {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(videoUrl));
|
||||||
|
|
||||||
|
|
||||||
|
intent.setPackage("com.google.android.youtube");
|
||||||
|
if (intent.resolveActivity(requireContext().getPackageManager()) != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
intent.setPackage(null);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error abriendo video externo", e);
|
||||||
|
Toast.makeText(getContext(), "No se pudo abrir el video", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarVistaNoVideo() {
|
||||||
|
ocultarVista(videoContainer);
|
||||||
|
ocultarVista(externalVideoButton);
|
||||||
|
mostrarVista(noVideoOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarControlesVideo() {
|
||||||
|
btnPlayPause.setOnClickListener(v -> {
|
||||||
|
if (isVideoReady) {
|
||||||
|
if (isPlaying) {
|
||||||
|
pausarVideo();
|
||||||
|
} else {
|
||||||
|
reproducirVideo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
if (fromUser && isVideoReady) {
|
||||||
|
videoView.seekTo(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reproducirVideo() {
|
||||||
|
if (videoView != null && isVideoReady) {
|
||||||
|
videoView.start();
|
||||||
|
isPlaying = true;
|
||||||
|
btnPlayPause.setImageResource(R.drawable.baseline_pause_24);
|
||||||
|
actualizarSeekBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pausarVideo() {
|
||||||
|
if (videoView != null && videoView.isPlaying()) {
|
||||||
|
videoView.pause();
|
||||||
|
isPlaying = false;
|
||||||
|
btnPlayPause.setImageResource(R.drawable.baseline_play_arrow_24);
|
||||||
|
handler.removeCallbacks(updateSeekBarTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actualizarSeekBar() {
|
||||||
|
if (isPlaying && videoView != null) {
|
||||||
|
handler.post(updateSeekBarTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable updateSeekBarTask = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (videoView != null && isPlaying) {
|
||||||
|
int currentPosition = videoView.getCurrentPosition();
|
||||||
|
seekBar.setProgress(currentPosition);
|
||||||
|
durationTextView.setText(formatearTiempo(currentPosition));
|
||||||
|
handler.postDelayed(this, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private String formatearTiempo(int milliseconds) {
|
||||||
|
return String.format("%02d:%02d",
|
||||||
|
TimeUnit.MILLISECONDS.toMinutes(milliseconds),
|
||||||
|
TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarVista(View view) {
|
||||||
|
if (view != null) {
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ocultarVista(View view) {
|
||||||
|
if (view != null) {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepared(MediaPlayer mp) {
|
||||||
|
Log.d(TAG, "Video preparado para reproducción");
|
||||||
|
isVideoReady = true;
|
||||||
|
|
||||||
|
int duration = videoView.getDuration();
|
||||||
|
seekBar.setMax(duration);
|
||||||
|
durationTextView.setText(formatearTiempo(duration));
|
||||||
|
|
||||||
|
|
||||||
|
reproducirVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCompletion(MediaPlayer mp) {
|
||||||
|
Log.d(TAG, "Video completado");
|
||||||
|
isPlaying = false;
|
||||||
|
btnPlayPause.setImageResource(R.drawable.baseline_play_arrow_24);
|
||||||
|
handler.removeCallbacks(updateSeekBarTask);
|
||||||
|
|
||||||
|
|
||||||
|
seekBar.setProgress(0);
|
||||||
|
durationTextView.setText("00:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||||
|
Log.e(TAG, "Error en video: what=" + what + ", extra=" + extra);
|
||||||
|
|
||||||
|
|
||||||
|
String videoUrl = ejercicio.getVideoUrl();
|
||||||
|
if (esYouTubeUrl(videoUrl)) {
|
||||||
|
configurarVideoExterno(videoUrl);
|
||||||
|
} else {
|
||||||
|
mostrarVistaNoVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (videoView != null && videoView.isPlaying()) {
|
||||||
|
pausarVideo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (handler != null) {
|
||||||
|
handler.removeCallbacks(updateSeekBarTask);
|
||||||
|
}
|
||||||
|
if (videoView != null) {
|
||||||
|
videoView.stopPlayback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Ejercicio getEjercicio() {
|
||||||
|
return ejercicio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTipoEjercicio() {
|
||||||
|
return tipoEjercicio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tieneVideo() {
|
||||||
|
String videoUrl = ejercicio.getVideoUrl();
|
||||||
|
return videoUrl != null && !videoUrl.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean esEjercicioPrincipal() {
|
||||||
|
return "principal".equals(tipoEjercicio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.DiasEntrenamiento;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DiasEntrenamientoFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnDiasEntrenamientoListener {
|
||||||
|
void onDiasEntrenamientoSelected(DiasEntrenamiento diasEntrenamiento);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnDiasEntrenamientoListener diasEntrenamientoListener;
|
||||||
|
private Spinner spinnerDias;
|
||||||
|
private Button btnContinuar;
|
||||||
|
private int diasSeleccionados = -1;
|
||||||
|
|
||||||
|
public DiasEntrenamientoFragment() {
|
||||||
|
super(R.layout.fragment_dias_entrenamiento);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
initializeViews(view);
|
||||||
|
setupSpinner();
|
||||||
|
setupClickListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeViews(View view) {
|
||||||
|
spinnerDias = view.findViewById(R.id.spinnerDias);
|
||||||
|
btnContinuar = view.findViewById(R.id.btnContinuarDias);
|
||||||
|
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
btnContinuar.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSpinner() {
|
||||||
|
List<String> diasOpciones = Arrays.asList(
|
||||||
|
"Selecciona días por semana",
|
||||||
|
"1 días por semana",
|
||||||
|
"2 días por semana",
|
||||||
|
"3 días por semana",
|
||||||
|
"4 días por semana",
|
||||||
|
"5 días por semana",
|
||||||
|
"6 días por semana",
|
||||||
|
"7 días por semana"
|
||||||
|
);
|
||||||
|
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(
|
||||||
|
requireContext(),
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
diasOpciones
|
||||||
|
);
|
||||||
|
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerDias.setAdapter(adapter);
|
||||||
|
|
||||||
|
|
||||||
|
spinnerDias.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (position == 0) {
|
||||||
|
|
||||||
|
diasSeleccionados = -1;
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
btnContinuar.setAlpha(0.5f);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
diasSeleccionados = position;
|
||||||
|
btnContinuar.setEnabled(true);
|
||||||
|
btnContinuar.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
diasSeleccionados = -1;
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
btnContinuar.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupClickListener() {
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
if (diasSeleccionados == -1) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, selecciona los días de entrenamiento",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (diasSeleccionados < 3 || diasSeleccionados > 6) {
|
||||||
|
Toast.makeText(getContext(), "Selecciona entre 3 y 6 días por semana",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diasEntrenamientoListener != null) {
|
||||||
|
DiasEntrenamiento seleccionado = new DiasEntrenamiento(diasSeleccionados);
|
||||||
|
diasEntrenamientoListener.onDiasEntrenamientoSelected(seleccionado);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Error: No se pudo procesar la selección",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnDiasEntrenamientoListener) {
|
||||||
|
diasEntrenamientoListener = (IOnDiasEntrenamientoListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context + " must implement IOnDiasEntrenamientoListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
diasEntrenamientoListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,242 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.adapters.ComidaAdapter;
|
||||||
|
import com.borjabolufer.elevate.databinding.FragmentDietBinding;
|
||||||
|
import com.borjabolufer.elevate.model.DiasEntrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Dieta;
|
||||||
|
import com.borjabolufer.elevate.model.Comida;
|
||||||
|
import com.borjabolufer.elevate.model.ValorNutricional;
|
||||||
|
import com.borjabolufer.elevate.model.Semana;
|
||||||
|
import com.borjabolufer.elevate.utils.JsonParser;
|
||||||
|
import com.borjabolufer.elevate.utils.JsonUtils;
|
||||||
|
import com.google.android.material.chip.Chip;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DietFragment extends Fragment {
|
||||||
|
|
||||||
|
private FragmentDietBinding binding;
|
||||||
|
private JsonParser jsonParser;
|
||||||
|
private Semana semanaActual;
|
||||||
|
private ComidaAdapter comidaAdapter;
|
||||||
|
private List<ComidaItem> comidasList;
|
||||||
|
private String diaSeleccionado = obtenerDiaActual();
|
||||||
|
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
binding = FragmentDietBinding.inflate(inflater, container, false);
|
||||||
|
View root = binding.getRoot();
|
||||||
|
|
||||||
|
inicializarComponentes();
|
||||||
|
configurarRecyclerView();
|
||||||
|
cargarDatos();
|
||||||
|
configurarChipsDias();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String obtenerDiaActual() {
|
||||||
|
String[] diasSemana = {"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"};
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
int dia = calendar.get(Calendar.DAY_OF_WEEK);
|
||||||
|
return diasSemana[dia - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inicializarComponentes() {
|
||||||
|
jsonParser = new JsonParser(getContext());
|
||||||
|
comidasList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cargarDatos() {
|
||||||
|
String email = obtenerEmailUsuarioActual();
|
||||||
|
if (email == null) {
|
||||||
|
Toast.makeText(getContext(), "Usuario no identificado", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String contenidoJson = JsonUtils.cargarPlanDelUsuario(getContext(), email);
|
||||||
|
if (contenidoJson == null) {
|
||||||
|
Toast.makeText(getContext(), "No se encontró el plan del usuario", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject rawJson;
|
||||||
|
try {
|
||||||
|
rawJson = new JSONObject(contenidoJson);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(getContext(), "Error al procesar el JSON", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
semanaActual = jsonParser.convertirJsonASemana(rawJson);
|
||||||
|
|
||||||
|
if (jsonParser.validarSemana(semanaActual)) {
|
||||||
|
cargarDietaDelDia(diaSeleccionado);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Error al cargar los datos", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void configurarRecyclerView() {
|
||||||
|
|
||||||
|
comidaAdapter = new ComidaAdapter(comidasList, this);
|
||||||
|
binding.recyclerComidas.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
binding.recyclerComidas.setAdapter(comidaAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarChipsDias() {
|
||||||
|
String[] diasSemana = {"Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"};
|
||||||
|
|
||||||
|
for (String dia : diasSemana) {
|
||||||
|
Chip chip = new Chip(getContext());
|
||||||
|
chip.setText(dia);
|
||||||
|
chip.setCheckable(true);
|
||||||
|
chip.setChecked(dia.equals(diaSeleccionado));
|
||||||
|
|
||||||
|
chip.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
||||||
|
if (isChecked) {
|
||||||
|
diaSeleccionado = dia;
|
||||||
|
cargarDietaDelDia(dia);
|
||||||
|
|
||||||
|
for (int i = 0; i < binding.chipGroupDiasDieta.getChildCount(); i++) {
|
||||||
|
Chip otherChip = (Chip) binding.chipGroupDiasDieta.getChildAt(i);
|
||||||
|
if (!otherChip.getText().equals(dia)) {
|
||||||
|
otherChip.setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
binding.chipGroupDiasDieta.addView(chip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cargarDietaDelDia(String dia) {
|
||||||
|
DiasEntrenamiento diaEntrenamiento = jsonParser.obtenerDia(semanaActual, dia);
|
||||||
|
|
||||||
|
if (diaEntrenamiento == null || diaEntrenamiento.getDieta() == null) {
|
||||||
|
mostrarSinDieta();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dieta dieta = diaEntrenamiento.getDieta();
|
||||||
|
comidasList.clear();
|
||||||
|
|
||||||
|
|
||||||
|
int totalCalorias = jsonParser.calcularCaloriasTotales(diaEntrenamiento);
|
||||||
|
actualizarResumenNutricional(dieta, totalCalorias);
|
||||||
|
|
||||||
|
|
||||||
|
if (dieta.getDesayuno() != null) {
|
||||||
|
comidasList.add(new ComidaItem("Desayuno", dieta.getDesayuno(), "desayuno"));
|
||||||
|
}
|
||||||
|
if (dieta.getComida() != null) {
|
||||||
|
comidasList.add(new ComidaItem("Comida", dieta.getComida(), "comida"));
|
||||||
|
}
|
||||||
|
if (dieta.getCena() != null) {
|
||||||
|
comidasList.add(new ComidaItem("Cena", dieta.getCena(), "cena"));
|
||||||
|
}
|
||||||
|
if (dieta.getSnacks() != null) {
|
||||||
|
comidasList.add(new ComidaItem("Snacks", dieta.getSnacks(), "snacks"));
|
||||||
|
}
|
||||||
|
|
||||||
|
comidaAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarSinDieta() {
|
||||||
|
binding.textCaloriasTotales.setText("0");
|
||||||
|
binding.textProteinas.setText("0g");
|
||||||
|
binding.textCarbohidratos.setText("0g");
|
||||||
|
binding.textGrasas.setText("0g");
|
||||||
|
|
||||||
|
comidasList.clear();
|
||||||
|
comidaAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actualizarResumenNutricional(Dieta dieta, int totalCalorias) {
|
||||||
|
int totalProteinas = 0, totalCarbohidratos = 0, totalGrasas = 0;
|
||||||
|
|
||||||
|
ValorNutricional[] valores = {
|
||||||
|
dieta.getDesayuno() != null ? dieta.getDesayuno().getValor_nutricional() : null,
|
||||||
|
dieta.getComida() != null ? dieta.getComida().getValor_nutricional() : null,
|
||||||
|
dieta.getCena() != null ? dieta.getCena().getValor_nutricional() : null,
|
||||||
|
dieta.getSnacks() != null ? dieta.getSnacks().getValor_nutricional() : null
|
||||||
|
};
|
||||||
|
|
||||||
|
for (ValorNutricional valor : valores) {
|
||||||
|
if (valor != null) {
|
||||||
|
totalProteinas += valor.getProteínas();
|
||||||
|
totalCarbohidratos += valor.getCarbohidratos();
|
||||||
|
totalGrasas += valor.getGrasas();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
binding.textCaloriasTotales.setText(String.valueOf(totalCalorias));
|
||||||
|
binding.textProteinas.setText(totalProteinas + "g");
|
||||||
|
binding.textCarbohidratos.setText(totalCarbohidratos + "g");
|
||||||
|
binding.textGrasas.setText(totalGrasas + "g");
|
||||||
|
|
||||||
|
|
||||||
|
actualizarBarrasProgreso(totalProteinas, totalCarbohidratos, totalGrasas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actualizarBarrasProgreso(int proteinas, int carbos, int grasas) {
|
||||||
|
|
||||||
|
int objetivoProteinas = 150;
|
||||||
|
int objetivoCarbos = 200;
|
||||||
|
int objetivoGrasas = 80;
|
||||||
|
|
||||||
|
binding.progressProteinas.setProgress((proteinas * 100) / objetivoProteinas);
|
||||||
|
binding.progressCarbohidratos.setProgress((carbos * 100) / objetivoCarbos);
|
||||||
|
binding.progressGrasas.setProgress((grasas * 100) / objetivoGrasas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String obtenerEmailUsuarioActual() {
|
||||||
|
SharedPreferences prefs = getActivity().getSharedPreferences("ElevatePrefs", Context.MODE_PRIVATE);
|
||||||
|
String email = prefs.getString("user_email", null);
|
||||||
|
Log.d("UsuarioActual", "Email cargado: " + email);
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ComidaItem {
|
||||||
|
private String nombre;
|
||||||
|
private Comida comida;
|
||||||
|
private String tipo;
|
||||||
|
|
||||||
|
public ComidaItem(String nombre, Comida comida, String tipo) {
|
||||||
|
this.nombre = nombre;
|
||||||
|
this.comida = comida;
|
||||||
|
this.tipo = tipo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombre() { return nombre; }
|
||||||
|
public Comida getComida() { return comida; }
|
||||||
|
public String getTipo() { return tipo; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Edad;
|
||||||
|
|
||||||
|
public class EdadFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnEdadListener {
|
||||||
|
void onEdadSelected(Edad edad);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnEdadListener edadListener;
|
||||||
|
|
||||||
|
public EdadFragment() {
|
||||||
|
super(R.layout.fragment_edad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
EditText editEdad = view.findViewById(R.id.editEdad);
|
||||||
|
Button btnContinuar = view.findViewById(R.id.btnContinuarEdad);
|
||||||
|
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
String edadTexto = editEdad.getText().toString().trim();
|
||||||
|
if (!edadTexto.isEmpty()) {
|
||||||
|
int edadValor = Integer.parseInt(edadTexto);
|
||||||
|
Edad edad = new Edad(edadValor);
|
||||||
|
edadListener.onEdadSelected(edad);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnEdadListener) {
|
||||||
|
edadListener = (IOnEdadListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement IOnEdadListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,527 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.MainActivity;
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.adapters.EjercicioAdapter;
|
||||||
|
import com.borjabolufer.elevate.databinding.FragmentHomeBinding;
|
||||||
|
import com.borjabolufer.elevate.model.DiasEntrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Entrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Ejercicio;
|
||||||
|
import com.borjabolufer.elevate.model.Semana;
|
||||||
|
import com.borjabolufer.elevate.ui.fragments.DetalleEjercicioFragment;
|
||||||
|
import com.borjabolufer.elevate.utils.JsonParser;
|
||||||
|
import com.borjabolufer.elevate.utils.JsonUtils;
|
||||||
|
import com.google.android.material.chip.Chip;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class HomeFragment extends Fragment implements EjercicioAdapter.OnItemClickListener {
|
||||||
|
|
||||||
|
private static final String TAG = "HomeFragment";
|
||||||
|
|
||||||
|
private FragmentHomeBinding binding;
|
||||||
|
private JsonParser jsonParser;
|
||||||
|
private Semana semanaActual;
|
||||||
|
private EjercicioAdapter ejercicioAdapter;
|
||||||
|
private List<EjercicioItem> ejerciciosList;
|
||||||
|
private String diaSeleccionado = obtenerDiaActual();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
binding = FragmentHomeBinding.inflate(inflater, container, false);
|
||||||
|
View root = binding.getRoot();
|
||||||
|
|
||||||
|
Log.d(TAG, "HomeFragment creado");
|
||||||
|
|
||||||
|
inicializarComponentes();
|
||||||
|
configurarRecyclerView();
|
||||||
|
configurarChipsDias();
|
||||||
|
cargarDatos();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inicializarComponentes() {
|
||||||
|
jsonParser = new JsonParser(getContext());
|
||||||
|
ejerciciosList = new ArrayList<>();
|
||||||
|
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("EEEE, dd MMM yyyy", new Locale("es", "ES"));
|
||||||
|
sdf.setTimeZone(calendar.getTimeZone());
|
||||||
|
binding.textFechaActual.setText(sdf.format(calendar.getTime()));
|
||||||
|
|
||||||
|
Log.d(TAG, "Componentes inicializados, día seleccionado: " + diaSeleccionado);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cargarDatos() {
|
||||||
|
String email = obtenerEmailUsuarioActual();
|
||||||
|
if (email == null) {
|
||||||
|
mostrarError("Usuario no identificado");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Cargando datos para usuario: " + email);
|
||||||
|
|
||||||
|
String contenidoJson = JsonUtils.cargarPlanDelUsuario(getContext(), email);
|
||||||
|
if (contenidoJson == null) {
|
||||||
|
mostrarError("No se encontró el plan del usuario");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject rawJson;
|
||||||
|
try {
|
||||||
|
rawJson = new JSONObject(contenidoJson);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al procesar JSON", e);
|
||||||
|
mostrarError("Error al procesar el JSON");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
semanaActual = jsonParser.convertirJsonASemana(rawJson);
|
||||||
|
|
||||||
|
if (jsonParser.validarSemana(semanaActual)) {
|
||||||
|
Log.d(TAG, "Semana validada correctamente");
|
||||||
|
cargarEjerciciosDelDia(diaSeleccionado);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Error validando semana");
|
||||||
|
mostrarError("Error al cargar los datos");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarRecyclerView() {
|
||||||
|
ejercicioAdapter = new EjercicioAdapter(ejerciciosList, this);
|
||||||
|
binding.recyclerEjercicios.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
binding.recyclerEjercicios.setAdapter(ejercicioAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarChipsDias() {
|
||||||
|
String[] diasSemana = {"Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"};
|
||||||
|
|
||||||
|
for (String dia : diasSemana) {
|
||||||
|
Chip chip = new Chip(getContext());
|
||||||
|
chip.setText(dia);
|
||||||
|
chip.setCheckable(true);
|
||||||
|
chip.setChecked(dia.equals(diaSeleccionado));
|
||||||
|
|
||||||
|
chip.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
||||||
|
if (isChecked) {
|
||||||
|
diaSeleccionado = dia;
|
||||||
|
Log.d(TAG, "Día seleccionado cambiado a: " + dia);
|
||||||
|
cargarEjerciciosDelDia(dia);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < binding.chipGroupDias.getChildCount(); i++) {
|
||||||
|
Chip otherChip = (Chip) binding.chipGroupDias.getChildAt(i);
|
||||||
|
if (!otherChip.getText().equals(dia)) {
|
||||||
|
otherChip.setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
binding.chipGroupDias.addView(chip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cargarEjerciciosDelDia(String dia) {
|
||||||
|
if (semanaActual == null) {
|
||||||
|
Log.w(TAG, "semanaActual es null, no se pueden cargar ejercicios");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiasEntrenamiento diaEntrenamiento = jsonParser.obtenerDia(semanaActual, dia);
|
||||||
|
|
||||||
|
if (diaEntrenamiento == null || diaEntrenamiento.getEntrenamiento() == null) {
|
||||||
|
Log.d(TAG, "Día de descanso: " + dia);
|
||||||
|
mostrarDiaDescanso(dia);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entrenamiento entrenamiento = diaEntrenamiento.getEntrenamiento();
|
||||||
|
mostrarDiaEntrenamiento(dia, entrenamiento);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarDiaDescanso(String dia) {
|
||||||
|
binding.textDiaActual.setText("Día de descanso");
|
||||||
|
binding.textDescripcionDia.setText("Hoy es tu día de recuperación. ¡Disfrútalo!");
|
||||||
|
ejerciciosList.clear();
|
||||||
|
|
||||||
|
if (ejercicioAdapter != null) {
|
||||||
|
ejercicioAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarDiaEntrenamiento(String dia, Entrenamiento entrenamiento) {
|
||||||
|
binding.textDiaActual.setText(dia);
|
||||||
|
binding.textDescripcionDia.setText("Entrenamiento de " + dia.toLowerCase());
|
||||||
|
|
||||||
|
ejerciciosList.clear();
|
||||||
|
|
||||||
|
|
||||||
|
if (entrenamiento.getCalentamiento() != null && !entrenamiento.getCalentamiento().isEmpty()) {
|
||||||
|
ejerciciosList.add(new EjercicioItem("CALENTAMIENTO", 0, 0, "calentamiento", true));
|
||||||
|
for (Ejercicio ejercicio : entrenamiento.getCalentamiento()) {
|
||||||
|
ejerciciosList.add(new EjercicioItem(
|
||||||
|
ejercicio.getNombre(),
|
||||||
|
ejercicio.getSeries(),
|
||||||
|
ejercicio.getDescripcion(),
|
||||||
|
ejercicio.getRepeticiones(),
|
||||||
|
ejercicio.getDescanso(),
|
||||||
|
ejercicio.getVideoUrl(),
|
||||||
|
"calentamiento"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (entrenamiento.getEjercicios_principales() != null && !entrenamiento.getEjercicios_principales().isEmpty()) {
|
||||||
|
ejerciciosList.add(new EjercicioItem("EJERCICIOS PRINCIPALES", 0, 0, "principal", true));
|
||||||
|
for (Ejercicio ejercicio : entrenamiento.getEjercicios_principales()) {
|
||||||
|
ejerciciosList.add(new EjercicioItem(
|
||||||
|
ejercicio.getNombre(),
|
||||||
|
ejercicio.getSeries(),
|
||||||
|
ejercicio.getDescripcion(),
|
||||||
|
ejercicio.getRepeticiones(),
|
||||||
|
ejercicio.getDescanso(),
|
||||||
|
ejercicio.getVideoUrl(),
|
||||||
|
"principal"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (entrenamiento.getEnfriamiento() != null && !entrenamiento.getEnfriamiento().isEmpty()) {
|
||||||
|
ejerciciosList.add(new EjercicioItem("ENFRIAMIENTO", 0, 0, "enfriamiento", true));
|
||||||
|
for (Ejercicio ejercicio : entrenamiento.getEnfriamiento()) {
|
||||||
|
ejerciciosList.add(new EjercicioItem(
|
||||||
|
ejercicio.getNombre(),
|
||||||
|
ejercicio.getSeries(),
|
||||||
|
ejercicio.getDescripcion(),
|
||||||
|
ejercicio.getRepeticiones(),
|
||||||
|
ejercicio.getDescanso(),
|
||||||
|
ejercicio.getVideoUrl(),
|
||||||
|
"enfriamiento"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ejercicioAdapter != null) {
|
||||||
|
ejercicioAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Ejercicios cargados para " + dia + ": " + ejerciciosList.size() + " items");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String obtenerEmailUsuarioActual() {
|
||||||
|
try {
|
||||||
|
SharedPreferences prefs = getActivity().getSharedPreferences("ElevatePrefs", Context.MODE_PRIVATE);
|
||||||
|
String email = prefs.getString("user_email", null);
|
||||||
|
Log.d(TAG, "Email cargado: " + email);
|
||||||
|
return email;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error obteniendo email usuario", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String obtenerDiaActual() {
|
||||||
|
try {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("EEEE", new Locale("es", "ES"));
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
String diaFormateado = sdf.format(calendar.getTime());
|
||||||
|
|
||||||
|
|
||||||
|
return diaFormateado.substring(0, 1).toUpperCase() + diaFormateado.substring(1).toLowerCase();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error obteniendo día actual", e);
|
||||||
|
return "Lunes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarError(String mensaje) {
|
||||||
|
Log.e(TAG, "Error: " + mensaje);
|
||||||
|
if (getContext() != null) {
|
||||||
|
Toast.makeText(getContext(), mensaje, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(EjercicioItem item) {
|
||||||
|
if (item.isEsHeader()) {
|
||||||
|
Log.d(TAG, "Click en header ignorado: " + item.getNombre());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Click en ejercicio: " + item.getNombre());
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (item.getNombre() == null || item.getTipo() == null) {
|
||||||
|
mostrarError("Datos del ejercicio incompletos");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ejercicio ejercicio = new Ejercicio(
|
||||||
|
item.getNombre(),
|
||||||
|
item.getDescripcion() != null ? item.getDescripcion() : "",
|
||||||
|
item.getSeries(),
|
||||||
|
item.getRepeticiones(),
|
||||||
|
item.getDescanso() != null ? item.getDescanso() : "",
|
||||||
|
item.getVideoURL() != null ? item.getVideoURL() : ""
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (!isNavigationSafe()) {
|
||||||
|
Log.w(TAG, "Navegación no segura, reintentando...");
|
||||||
|
mostrarError("Espera un momento e inténtalo de nuevo");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable("ejercicio", ejercicio);
|
||||||
|
args.putString("tipo_ejercicio", item.getTipo());
|
||||||
|
|
||||||
|
|
||||||
|
navegarConNavController(args);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error navegando a detalle ejercicio", e);
|
||||||
|
mostrarError("Error al abrir detalle del ejercicio");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isNavigationSafe() {
|
||||||
|
try {
|
||||||
|
return isAdded() &&
|
||||||
|
getContext() != null &&
|
||||||
|
getActivity() != null &&
|
||||||
|
!getActivity().isFinishing() &&
|
||||||
|
!getActivity().isDestroyed() &&
|
||||||
|
getView() != null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error verificando seguridad navegación", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void navegarConNavController(Bundle args) {
|
||||||
|
try {
|
||||||
|
NavController navController = Navigation.findNavController(requireView());
|
||||||
|
|
||||||
|
|
||||||
|
if (navController.getCurrentDestination() != null) {
|
||||||
|
int currentDestId = navController.getCurrentDestination().getId();
|
||||||
|
|
||||||
|
if (currentDestId == R.id.nav_home) {
|
||||||
|
|
||||||
|
navController.navigate(R.id.action_home_to_detalle_ejercicio, args);
|
||||||
|
Log.d(TAG, "Navegación exitosa con NavController");
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "No estamos en home fragment, usando fallback");
|
||||||
|
navegarConFragmentManager(args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Destino actual es null, usando fallback");
|
||||||
|
navegarConFragmentManager(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception navException) {
|
||||||
|
Log.w(TAG, "NavController falló, usando FragmentManager", navException);
|
||||||
|
navegarConFragmentManager(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void navegarConFragmentManager(Bundle args) {
|
||||||
|
try {
|
||||||
|
Ejercicio ejercicio = (Ejercicio) args.getSerializable("ejercicio");
|
||||||
|
String tipo = args.getString("tipo_ejercicio");
|
||||||
|
|
||||||
|
if (ejercicio == null || tipo == null) {
|
||||||
|
mostrarError("Error en datos del ejercicio");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DetalleEjercicioFragment detalleFragment = DetalleEjercicioFragment.newInstance(ejercicio, tipo);
|
||||||
|
|
||||||
|
requireActivity().getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.nav_host_fragment_content_main, detalleFragment)
|
||||||
|
.addToBackStack("detalle_ejercicio")
|
||||||
|
.commitAllowingStateLoss();
|
||||||
|
|
||||||
|
Log.d(TAG, "Navegación exitosa con FragmentManager");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error en navegación con FragmentManager", e);
|
||||||
|
mostrarError("No se pudo abrir el detalle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
Log.d(TAG, "HomeFragment onResume");
|
||||||
|
|
||||||
|
|
||||||
|
verificarEstadoFragment();
|
||||||
|
|
||||||
|
|
||||||
|
if (semanaActual == null) {
|
||||||
|
Log.d(TAG, "Recargando datos en onResume");
|
||||||
|
cargarDatos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verificarEstadoFragment() {
|
||||||
|
try {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
FragmentManager fm = getActivity().getSupportFragmentManager();
|
||||||
|
|
||||||
|
|
||||||
|
if (fm.getBackStackEntryCount() > 5) {
|
||||||
|
Log.w(TAG, "Demasiados fragments en back stack: " + fm.getBackStackEntryCount());
|
||||||
|
|
||||||
|
|
||||||
|
if (getActivity() instanceof MainActivity) {
|
||||||
|
((MainActivity) getActivity()).resetNavigation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error verificando estado fragment", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void refrescarDatos() {
|
||||||
|
Log.d(TAG, "Refrescando datos del HomeFragment");
|
||||||
|
|
||||||
|
if (isAdded() && getContext() != null) {
|
||||||
|
cargarDatos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void navegarADia(String dia) {
|
||||||
|
Log.d(TAG, "Navegando a día: " + dia);
|
||||||
|
|
||||||
|
if (dia != null && !dia.isEmpty()) {
|
||||||
|
diaSeleccionado = dia;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < binding.chipGroupDias.getChildCount(); i++) {
|
||||||
|
Chip chip = (Chip) binding.chipGroupDias.getChildAt(i);
|
||||||
|
chip.setChecked(chip.getText().equals(dia));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cargarEjerciciosDelDia(dia);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
|
||||||
|
Log.d(TAG, "HomeFragment onDestroyView");
|
||||||
|
|
||||||
|
|
||||||
|
ejercicioAdapter = null;
|
||||||
|
ejerciciosList = null;
|
||||||
|
semanaActual = null;
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class EjercicioItem implements Serializable {
|
||||||
|
private String nombre;
|
||||||
|
private String descripcion;
|
||||||
|
private int series;
|
||||||
|
private int repeticiones;
|
||||||
|
private String descanso;
|
||||||
|
private String tipo;
|
||||||
|
private String videoURL;
|
||||||
|
private boolean esHeader;
|
||||||
|
|
||||||
|
|
||||||
|
public EjercicioItem(String nombre, int series, int repeticiones, String tipo, boolean esHeader) {
|
||||||
|
this.nombre = nombre != null ? nombre : "";
|
||||||
|
this.series = series;
|
||||||
|
this.repeticiones = repeticiones;
|
||||||
|
this.tipo = tipo != null ? tipo : "";
|
||||||
|
this.esHeader = esHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public EjercicioItem(String nombre, int series, String descripcion, int repeticiones, String descanso, String videoURL, String tipo) {
|
||||||
|
this.nombre = nombre != null ? nombre : "";
|
||||||
|
this.series = series;
|
||||||
|
this.descripcion = descripcion != null ? descripcion : "";
|
||||||
|
this.repeticiones = repeticiones;
|
||||||
|
this.descanso = descanso != null ? descanso : "";
|
||||||
|
this.videoURL = videoURL != null ? videoURL : "";
|
||||||
|
this.tipo = tipo != null ? tipo : "";
|
||||||
|
this.esHeader = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getNombre() { return nombre != null ? nombre : ""; }
|
||||||
|
public void setNombre(String nombre) { this.nombre = nombre != null ? nombre : ""; }
|
||||||
|
|
||||||
|
public String getDescripcion() { return descripcion != null ? descripcion : ""; }
|
||||||
|
public void setDescripcion(String descripcion) { this.descripcion = descripcion != null ? descripcion : ""; }
|
||||||
|
|
||||||
|
public int getSeries() { return series; }
|
||||||
|
public void setSeries(int series) { this.series = series; }
|
||||||
|
|
||||||
|
public int getRepeticiones() { return repeticiones; }
|
||||||
|
public void setRepeticiones(int repeticiones) { this.repeticiones = repeticiones; }
|
||||||
|
|
||||||
|
public String getDescanso() { return descanso != null ? descanso : ""; }
|
||||||
|
public void setDescanso(String descanso) { this.descanso = descanso != null ? descanso : ""; }
|
||||||
|
|
||||||
|
public String getTipo() { return tipo != null ? tipo : ""; }
|
||||||
|
public void setTipo(String tipo) { this.tipo = tipo != null ? tipo : ""; }
|
||||||
|
|
||||||
|
public String getVideoURL() { return videoURL != null ? videoURL : ""; }
|
||||||
|
public void setVideoURL(String videoURL) { this.videoURL = videoURL != null ? videoURL : ""; }
|
||||||
|
|
||||||
|
public boolean isEsHeader() { return esHeader; }
|
||||||
|
public void setEsHeader(boolean esHeader) { this.esHeader = esHeader; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Intensidad;
|
||||||
|
import com.borjabolufer.elevate.utils.ColorUtils;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class IntensidadFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnIntensidadListener {
|
||||||
|
void onIntensidadSelected(Intensidad intensidad);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnIntensidadListener intensidadListener;
|
||||||
|
private Intensidad intensidadSeleccionada;
|
||||||
|
private MaterialButton selectedButton;
|
||||||
|
|
||||||
|
public IntensidadFragment() {
|
||||||
|
super(R.layout.fragment_intensidad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnIntensidadListener) {
|
||||||
|
intensidadListener = (IOnIntensidadListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement IOnIntensidadListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
MaterialButton btnBaja = view.findViewById(R.id.intensidad_baja);
|
||||||
|
MaterialButton btnMedia = view.findViewById(R.id.intensidad_media);
|
||||||
|
MaterialButton btnAlta = view.findViewById(R.id.intensidad_alta);
|
||||||
|
Button btnContinuar = view.findViewById(R.id.btnContinuarIntensidad);
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
|
||||||
|
View.OnClickListener listener = v -> {
|
||||||
|
if (selectedButton != null) {
|
||||||
|
ColorUtils.limpiarSeleccion(selectedButton);
|
||||||
|
}
|
||||||
|
selectedButton = (MaterialButton) v;
|
||||||
|
ColorUtils.marcarSeleccion(selectedButton);
|
||||||
|
|
||||||
|
if (v.getId() == R.id.intensidad_baja) {
|
||||||
|
intensidadSeleccionada = new Intensidad("Baja");
|
||||||
|
} else if (v.getId() == R.id.intensidad_media) {
|
||||||
|
intensidadSeleccionada = new Intensidad("Media");
|
||||||
|
} else if (v.getId() == R.id.intensidad_alta) {
|
||||||
|
intensidadSeleccionada = new Intensidad("Alta");
|
||||||
|
}
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
btnBaja.setOnClickListener(listener);
|
||||||
|
btnMedia.setOnClickListener(listener);
|
||||||
|
btnAlta.setOnClickListener(listener);
|
||||||
|
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
if (intensidadSeleccionada != null && intensidadListener != null) {
|
||||||
|
intensidadListener.onIntensidadSelected(intensidadSeleccionada);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,890 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.LinearInterpolator;
|
||||||
|
import android.view.animation.RotateAnimation;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.LoginActivity;
|
||||||
|
import com.borjabolufer.elevate.MainActivity;
|
||||||
|
import com.borjabolufer.elevate.PreguntasActivity;
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Usuario;
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioDbHelper;
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioEntity;
|
||||||
|
import com.borjabolufer.elevate.utils.ChatGptApi;
|
||||||
|
import com.borjabolufer.elevate.utils.PasswordUtils;
|
||||||
|
import com.borjabolufer.elevate.utils.PlanActualizadoDialog;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LoadingPlanFragment extends Fragment {
|
||||||
|
|
||||||
|
private static final String ARG_USUARIO = "usuario";
|
||||||
|
private static final String ARG_NOMBRE_USUARIO = "nombre_usuario";
|
||||||
|
private static final String ARG_CONTRASENA_USUARIO = "contrasena_usuario";
|
||||||
|
private static final String ARG_ES_REGENERACION = "es_regeneracion";
|
||||||
|
private static final String TAG = "LoadingPlanFragment";
|
||||||
|
|
||||||
|
|
||||||
|
private ImageView loadingIcon;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private TextView progressText;
|
||||||
|
private TextView statusText;
|
||||||
|
private RecyclerView logRecyclerView;
|
||||||
|
private LogAdapter logAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
private Usuario usuario;
|
||||||
|
private String nombreUsuario;
|
||||||
|
private String contrasenaUsuario;
|
||||||
|
private boolean esRegeneracion = false;
|
||||||
|
private List<LogItem> logItems;
|
||||||
|
private Handler handler;
|
||||||
|
private int currentProgress = 0;
|
||||||
|
private int currentLogStep = 0;
|
||||||
|
|
||||||
|
|
||||||
|
private final String[] logMessages = {
|
||||||
|
"🔍 Analizando tu perfil fitness...",
|
||||||
|
"📊 Calculando tu tasa metabólica basal (TMB)...",
|
||||||
|
"🎯 Definiendo objetivos personalizados...",
|
||||||
|
"💪 Seleccionando ejercicios para tu nivel...",
|
||||||
|
"🏋️ Creando rutina de calentamiento dinámico...",
|
||||||
|
"⚡ Diseñando ejercicios principales...",
|
||||||
|
"🧘 Configurando rutina de enfriamiento...",
|
||||||
|
"🥗 Planificando tu menú nutricional...",
|
||||||
|
"🍎 Calculando macronutrientes necesarios...",
|
||||||
|
"📱 Seleccionando recetas saludables...",
|
||||||
|
"⏰ Optimizando horarios de comida...",
|
||||||
|
"💧 Configurando hidratación diaria...",
|
||||||
|
"😴 Estableciendo recomendaciones de descanso...",
|
||||||
|
"📦 Evaluando compatibilidad de alimentos...",
|
||||||
|
"🔄 Ajustando proporciones de entrenamiento...",
|
||||||
|
"📆 Distribuyendo días de ejercicio y descanso...",
|
||||||
|
"🧠 Analizando patrones de comportamiento previos...",
|
||||||
|
"📈 Comparando métricas con otros usuarios similares...",
|
||||||
|
"📚 Revisando literatura científica reciente...",
|
||||||
|
"🧮 Recalculando parámetros por coherencia...",
|
||||||
|
"⚙️ Verificando integridad del plan...",
|
||||||
|
"💡 Incorporando sugerencias inteligentes...",
|
||||||
|
"🎯 Refinando los objetivos establecidos...",
|
||||||
|
"🔗 Integrando plan de ejercicios y nutrición...",
|
||||||
|
"✨ Aplicando algoritmos de personalización avanzada...",
|
||||||
|
"🔐 Encriptando tus datos de forma segura...",
|
||||||
|
"📤 Subiendo tu configuración a la nube...",
|
||||||
|
"📥 Sincronizando con tu perfil...",
|
||||||
|
"📂 Generando informe detallado...",
|
||||||
|
"✅ Finalizando y guardando tu plan personalizado..."
|
||||||
|
};
|
||||||
|
|
||||||
|
private final int[] logDurations = {
|
||||||
|
1500, 2000, 1800, 2200, 1600, 2500, 1400, 2000,
|
||||||
|
1700, 1900, 1300, 1200, 1400, 1800, 1600, 2100,
|
||||||
|
1900, 2300, 1800, 1400, 1500, 1700, 1600, 2400,
|
||||||
|
1100, 1200, 1400, 1300, 2000, 1800
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static LoadingPlanFragment newInstance(Usuario usuario, String nombreUsuario, String contrasenaUsuario) {
|
||||||
|
LoadingPlanFragment fragment = new LoadingPlanFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_USUARIO, usuario);
|
||||||
|
args.putString(ARG_NOMBRE_USUARIO, nombreUsuario);
|
||||||
|
args.putString(ARG_CONTRASENA_USUARIO, contrasenaUsuario);
|
||||||
|
args.putBoolean(ARG_ES_REGENERACION, false);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static LoadingPlanFragment newInstanceRegeneration(Usuario usuario, String nombreUsuario) {
|
||||||
|
LoadingPlanFragment fragment = new LoadingPlanFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_USUARIO, usuario);
|
||||||
|
args.putString(ARG_NOMBRE_USUARIO, nombreUsuario);
|
||||||
|
args.putString(ARG_CONTRASENA_USUARIO, "regeneration");
|
||||||
|
args.putBoolean(ARG_ES_REGENERACION, true);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
usuario = (Usuario) getArguments().getSerializable(ARG_USUARIO);
|
||||||
|
nombreUsuario = getArguments().getString(ARG_NOMBRE_USUARIO);
|
||||||
|
contrasenaUsuario = getArguments().getString(ARG_CONTRASENA_USUARIO);
|
||||||
|
esRegeneracion = getArguments().getBoolean(ARG_ES_REGENERACION, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = new Handler();
|
||||||
|
logItems = new ArrayList<>();
|
||||||
|
|
||||||
|
Log.d(TAG, "LoadingPlanFragment creado - Es regeneración: " + esRegeneracion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_loading_plan, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
FragmentManager fm = requireActivity().getSupportFragmentManager();
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < fm.getBackStackEntryCount(); i++) {
|
||||||
|
FragmentManager.BackStackEntry entry = fm.getBackStackEntryAt(i);
|
||||||
|
if (entry.getName() != null && entry.getName().contains("loading")) {
|
||||||
|
|
||||||
|
fm.popBackStackImmediate(entry.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error limpiando back stack en onViewCreated", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
initViews(view);
|
||||||
|
setupRecyclerView();
|
||||||
|
startLoadingAnimation();
|
||||||
|
startLogSimulation();
|
||||||
|
generatePlan();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void autodestruirse() {
|
||||||
|
try {
|
||||||
|
if (isAdded() && getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
FragmentManager fm = requireActivity().getSupportFragmentManager();
|
||||||
|
fm.beginTransaction()
|
||||||
|
.remove(LoadingPlanFragment.this)
|
||||||
|
.commitNowAllowingStateLoss();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error en autodestrucción", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error general en autodestruirse", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void initViews(View view) {
|
||||||
|
loadingIcon = view.findViewById(R.id.loadingIcon);
|
||||||
|
progressBar = view.findViewById(R.id.progressBar);
|
||||||
|
progressText = view.findViewById(R.id.progressText);
|
||||||
|
statusText = view.findViewById(R.id.statusText);
|
||||||
|
logRecyclerView = view.findViewById(R.id.logRecyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclerView() {
|
||||||
|
logAdapter = new LogAdapter(logItems);
|
||||||
|
logRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
logRecyclerView.setAdapter(logAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLoadingAnimation() {
|
||||||
|
|
||||||
|
RotateAnimation rotateAnimation = new RotateAnimation(
|
||||||
|
0f, 360f,
|
||||||
|
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||||
|
Animation.RELATIVE_TO_SELF, 0.5f
|
||||||
|
);
|
||||||
|
rotateAnimation.setDuration(2000);
|
||||||
|
rotateAnimation.setRepeatCount(Animation.INFINITE);
|
||||||
|
rotateAnimation.setInterpolator(new LinearInterpolator());
|
||||||
|
loadingIcon.startAnimation(rotateAnimation);
|
||||||
|
|
||||||
|
|
||||||
|
if (esRegeneracion) {
|
||||||
|
statusText.setText("Regenerando tu plan personalizado...");
|
||||||
|
} else {
|
||||||
|
statusText.setText("Creando tu plan personalizado...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLogSimulation() {
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (currentLogStep < logMessages.length && isAdded()) {
|
||||||
|
addLogMessage(logMessages[currentLogStep]);
|
||||||
|
updateProgress();
|
||||||
|
|
||||||
|
currentLogStep++;
|
||||||
|
|
||||||
|
if (currentLogStep < logMessages.length) {
|
||||||
|
|
||||||
|
int delay = logDurations[currentLogStep - 1];
|
||||||
|
handler.postDelayed(this, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLogMessage(String message) {
|
||||||
|
|
||||||
|
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||||
|
|
||||||
|
addLogMessageInternal(message);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
runOnUiThreadSafe(() -> addLogMessageInternal(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLogMessageInternal(String message) {
|
||||||
|
if (!isAdded() || getContext() == null) {
|
||||||
|
Log.w(TAG, "Fragment no está adjunto, no se puede actualizar UI");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
LogItem logItem = new LogItem(message, System.currentTimeMillis());
|
||||||
|
logItems.add(logItem);
|
||||||
|
|
||||||
|
if (logAdapter != null) {
|
||||||
|
logAdapter.notifyItemInserted(logItems.size() - 1);
|
||||||
|
|
||||||
|
if (logRecyclerView != null) {
|
||||||
|
logRecyclerView.scrollToPosition(logItems.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al actualizar log: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProgress() {
|
||||||
|
runOnUiThreadSafe(() -> {
|
||||||
|
currentProgress = (currentLogStep * 100) / logMessages.length;
|
||||||
|
|
||||||
|
if (progressBar != null && progressText != null && statusText != null) {
|
||||||
|
|
||||||
|
ObjectAnimator progressAnimator = ObjectAnimator.ofInt(progressBar, "progress", progressBar.getProgress(), currentProgress);
|
||||||
|
progressAnimator.setDuration(500);
|
||||||
|
progressAnimator.start();
|
||||||
|
|
||||||
|
progressText.setText(currentProgress + "%");
|
||||||
|
|
||||||
|
|
||||||
|
if (currentProgress < 30) {
|
||||||
|
statusText.setText(esRegeneracion ? "Reanalyzando tu perfil..." : "Analizando tu perfil...");
|
||||||
|
} else if (currentProgress < 60) {
|
||||||
|
statusText.setText(esRegeneracion ? "Actualizando rutinas..." : "Creando rutinas personalizadas...");
|
||||||
|
} else if (currentProgress < 90) {
|
||||||
|
statusText.setText(esRegeneracion ? "Rediseñando plan nutricional..." : "Diseñando plan nutricional...");
|
||||||
|
} else {
|
||||||
|
statusText.setText(esRegeneracion ? "Finalizando actualización..." : "Finalizando personalización...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generatePlan() {
|
||||||
|
if (usuario == null || nombreUsuario == null) {
|
||||||
|
showError("Datos de usuario incompletos");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String prompt = buildPrompt();
|
||||||
|
|
||||||
|
|
||||||
|
if (!esRegeneracion && contrasenaUsuario != null && !contrasenaUsuario.equals("regeneration")) {
|
||||||
|
Log.d(TAG, "Creando nuevo usuario en BD");
|
||||||
|
crearUsuarioEnBD();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Regenerando plan para usuario existente");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
ChatGptApi.enviarPrompt(prompt, new ChatGptApi.ChatGptCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String jsonResponse) {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
UsuarioDbHelper dbHelper = new UsuarioDbHelper(requireContext());
|
||||||
|
int updated = dbHelper.updatePlanJson(nombreUsuario, jsonResponse);
|
||||||
|
Log.d(TAG, "Filas actualizadas: " + updated);
|
||||||
|
|
||||||
|
if (updated > 0) {
|
||||||
|
boolean saved = dbHelper.guardarPlanJsonEnArchivo(getContext(), nombreUsuario);
|
||||||
|
Log.d(TAG, "Plan guardado en archivo: " + saved);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (isAdded()) {
|
||||||
|
addLogMessage(esRegeneracion ? "🎉 ¡Tu plan ha sido actualizado!" : "🎉 ¡Tu plan está listo!");
|
||||||
|
finishLoading();
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
if (isAdded()) {
|
||||||
|
addLogMessage("❌ Error: " + error);
|
||||||
|
showError("Error al generar el plan: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (JSONException e) {
|
||||||
|
if (isAdded()) {
|
||||||
|
showError("Error en configuración: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runOnUiThreadSafe(Runnable action) {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
if (isAdded() && getContext() != null) {
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void crearUsuarioEnBD() {
|
||||||
|
new Thread(() -> {
|
||||||
|
UsuarioDbHelper dbHelper = new UsuarioDbHelper(requireContext());
|
||||||
|
if (!dbHelper.checkUserEmailExists(nombreUsuario)) {
|
||||||
|
|
||||||
|
UsuarioEntity usuarioEntity = new UsuarioEntity();
|
||||||
|
usuarioEntity.setEmail(nombreUsuario);
|
||||||
|
|
||||||
|
|
||||||
|
String nombreCompleto = nombreUsuario;
|
||||||
|
if (getActivity() instanceof PreguntasActivity) {
|
||||||
|
PreguntasActivity preguntasActivity = (PreguntasActivity) getActivity();
|
||||||
|
String nombre = preguntasActivity.getNombreCompleto();
|
||||||
|
if (nombre != null && !nombre.trim().isEmpty()) {
|
||||||
|
nombreCompleto = nombre;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usuarioEntity.setNombreCompleto(nombreCompleto);
|
||||||
|
usuarioEntity.setPasswordHash(PasswordUtils.hashPassword(contrasenaUsuario));
|
||||||
|
usuarioEntity.setSexo(usuario.getSexo() != null ? usuario.getSexo().getSexo() : "");
|
||||||
|
usuarioEntity.setEdad(usuario.getEdad() != null ? usuario.getEdad().getEdad() : 0);
|
||||||
|
usuarioEntity.setObjetivo(usuario.getObjetivo() != null ? usuario.getObjetivo().getObjetivo() : "");
|
||||||
|
usuarioEntity.setDiasEntrenamiento(usuario.getDias() != null ? usuario.getDias().getCantidad() : 0);
|
||||||
|
usuarioEntity.setIntentensidad(usuario.getIntensidad() != null ? usuario.getIntensidad().getIntensidad() : "");
|
||||||
|
usuarioEntity.setPeso(usuario.getPeso() != null ? usuario.getPeso().getPeso() : 0.0);
|
||||||
|
usuarioEntity.setAltura(usuario.getAltura() != null ? usuario.getAltura().getAltura() : 0.0);
|
||||||
|
|
||||||
|
|
||||||
|
long result = dbHelper.insertarUsuarioCompleto(usuarioEntity);
|
||||||
|
Log.d(TAG, "Usuario insertado con ID: " + result + ", Nombre: " + nombreCompleto + ", Email: " + nombreUsuario);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Usuario ya existe: " + nombreUsuario);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String buildPrompt() {
|
||||||
|
String tipoOperacion = esRegeneracion ? "REGENERA y ACTUALIZA" : "Genera";
|
||||||
|
|
||||||
|
|
||||||
|
int diasEntrenamiento = usuario.getDias().getCantidad();
|
||||||
|
String distribucionDias = generarDistribucionDias(diasEntrenamiento);
|
||||||
|
String explicacionDias = generarExplicacionDias(diasEntrenamiento);
|
||||||
|
|
||||||
|
return "Actúa como un entrenador personal certificado y nutricionista experto. " + tipoOperacion + " un plan semanal personalizado en formato JSON ESTRICTO y FUNCIONAL a partir de los siguientes datos personales:" +
|
||||||
|
|
||||||
|
"\n\n👤 DATOS DEL USUARIO:" +
|
||||||
|
"\n- Edad: " + usuario.getEdad().getEdad() + " años" +
|
||||||
|
"\n- Sexo: " + usuario.getSexo().getSexo() +
|
||||||
|
"\n- Altura: " + usuario.getAltura().getAltura() + " cm" +
|
||||||
|
"\n- Peso: " + usuario.getPeso().getPeso() + " kg" +
|
||||||
|
"\n- Objetivo: " + usuario.getObjetivo().getObjetivo() +
|
||||||
|
"\n- Intensidad: " + usuario.getIntensidad().getIntensidad() +
|
||||||
|
"\n- Días de entrenamiento por semana: " + diasEntrenamiento +
|
||||||
|
|
||||||
|
"\n\n🗓️ DISTRIBUCIÓN OBLIGATORIA DE ENTRENAMIENTOS:" +
|
||||||
|
explicacionDias +
|
||||||
|
"\n" + distribucionDias +
|
||||||
|
|
||||||
|
"\n\n📌 FORMATO DE RESPUESTA:" +
|
||||||
|
"\n- SOLO un JSON válido, funcional y parseable" +
|
||||||
|
"\n- SIN explicaciones, comentarios ni formato markdown" +
|
||||||
|
"\n- Claves en minúsculas, sin tildes ni espacios" +
|
||||||
|
"\n- Estructura JSON igual a la del siguiente ejemplo (entrenamiento y dieta separados por día):" +
|
||||||
|
|
||||||
|
"\n\n🎯 ESTRUCTURA ESPERADA:" +
|
||||||
|
"\n{" +
|
||||||
|
"\n \"semana\": [" +
|
||||||
|
"\n {" +
|
||||||
|
"\n \"dia\": \"Lunes\"," +
|
||||||
|
"\n \"entrenamiento\": {" +
|
||||||
|
"\n \"calentamiento\": [ ]," +
|
||||||
|
"\n \"ejercicios_principales\": [ ]," +
|
||||||
|
"\n \"enfriamiento\": [ ]" +
|
||||||
|
"\n }," +
|
||||||
|
"\n \"dieta\": {" +
|
||||||
|
"\n \"desayuno\": { }," +
|
||||||
|
"\n \"comida\": { }," +
|
||||||
|
"\n \"cena\": { }," +
|
||||||
|
"\n \"snack\": { }" +
|
||||||
|
"\n }" +
|
||||||
|
"\n }," +
|
||||||
|
"\n {" +
|
||||||
|
"\n \"dia\": \"Martes\"," +
|
||||||
|
"\n \"entrenamiento\": null," +
|
||||||
|
"\n \"dieta\": {" +
|
||||||
|
"\n \"desayuno\": { }," +
|
||||||
|
"\n \"comida\": { }," +
|
||||||
|
"\n \"cena\": { }," +
|
||||||
|
"\n \"snack\": { }" +
|
||||||
|
"\n }" +
|
||||||
|
"\n }," +
|
||||||
|
"\n " +
|
||||||
|
"\n ]" +
|
||||||
|
"\n}" +
|
||||||
|
|
||||||
|
"\n\n🧠 DETALLES OBLIGATORIOS EN CADA SECCIÓN:" +
|
||||||
|
|
||||||
|
"\n\n📍 ENTRENAMIENTO:" +
|
||||||
|
"\n- EXACTAMENTE " + diasEntrenamiento + " días con entrenamiento según la distribución especificada" +
|
||||||
|
"\n- Los días SIN entrenamiento deben tener 'entrenamiento': null" +
|
||||||
|
"\n- calentamiento, ejercicios_principales y enfriamiento por día de entrenamiento" +
|
||||||
|
"\n- en los descansos msotrar valores como estos 90s no 90 segundos" +
|
||||||
|
"\n- los videos en los calentamientos y enfriamientos seran link a yotube donde busce en el buscador el nombre del ejercicio. En los ejercicios_principales si que tendras que buscar directamente un video de ese ejercicio" +
|
||||||
|
"\n- Campos por ejercicio: nombre, descripcion, series, repeticiones, descanso, video_url" +
|
||||||
|
"\n- ejercicios_principales tendrá entre 8 a 12, calentamiento de 2 a 4 y de enfriamiento de 2 a 4" +
|
||||||
|
"\n La descripcion que sea mas detallada sobre todo en calentamiento y enfriamientos"+
|
||||||
|
|
||||||
|
|
||||||
|
"\n\n📍 DIETA (obligatoria todos los días):" +
|
||||||
|
"\n- TODOS los 7 días deben tener dieta completa" +
|
||||||
|
"\n- Cada día: desayuno, comida, cena, snack" +
|
||||||
|
"\n- Campos por comida: nombre_del_plato, ingredientes (array), receta, valor_nutricional (con calorias, proteinas, carbohidratos, grasas), video_url" +
|
||||||
|
|
||||||
|
"\n\n⚠️ CRÍTICO - DISTRIBUCIÓN DE DÍAS:" +
|
||||||
|
"\n- RESPETAR EXACTAMENTE la distribución especificada" +
|
||||||
|
"\n- NO crear más días de entrenamiento de los indicados" +
|
||||||
|
"\n- Los días de descanso son parte fundamental del plan" +
|
||||||
|
|
||||||
|
"\n\n⚠️ IMPORTANTE:" +
|
||||||
|
"\n- Usa YouTube en español para los video_url" +
|
||||||
|
"\n- No repitas ejercicios ni platos durante la semana" +
|
||||||
|
"\n- Adecúa calorías y tipos de ejercicios al objetivo e intensidad" +
|
||||||
|
(esRegeneracion ? "\n- Este es un plan ACTUALIZADO, asegúrate de que sea diferente al anterior" : "") +
|
||||||
|
|
||||||
|
"\n\n🚫 NO INCLUYAS ningún texto adicional. Solo responde con un JSON válido exactamente con la estructura descrita.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String generarDistribucionDias(int diasEntrenamiento) {
|
||||||
|
StringBuilder distribucion = new StringBuilder();
|
||||||
|
|
||||||
|
switch (diasEntrenamiento) {
|
||||||
|
case 1:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: DESCANSO\n");
|
||||||
|
distribucion.append("- Miércoles: DESCANSO\n");
|
||||||
|
distribucion.append("- Jueves: DESCANSO\n");
|
||||||
|
distribucion.append("- Viernes: DESCANSO\n");
|
||||||
|
distribucion.append("- Sábado: DESCANSO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: DESCANSO\n");
|
||||||
|
distribucion.append("- Miércoles: DESCANSO\n");
|
||||||
|
distribucion.append("- Jueves: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Viernes: DESCANSO\n");
|
||||||
|
distribucion.append("- Sábado: DESCANSO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: DESCANSO\n");
|
||||||
|
distribucion.append("- Miércoles: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Jueves: DESCANSO\n");
|
||||||
|
distribucion.append("- Viernes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Sábado: DESCANSO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Miércoles: DESCANSO\n");
|
||||||
|
distribucion.append("- Jueves: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Viernes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Sábado: DESCANSO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Miércoles: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Jueves: DESCANSO\n");
|
||||||
|
distribucion.append("- Viernes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Sábado: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Miércoles: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Jueves: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Viernes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Sábado: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Miércoles: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Jueves: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Viernes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Sábado: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Domingo: ENTRENAMIENTO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
distribucion.append("- Lunes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Martes: DESCANSO\n");
|
||||||
|
distribucion.append("- Miércoles: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Jueves: DESCANSO\n");
|
||||||
|
distribucion.append("- Viernes: ENTRENAMIENTO\n");
|
||||||
|
distribucion.append("- Sábado: DESCANSO\n");
|
||||||
|
distribucion.append("- Domingo: DESCANSO");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribucion.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String generarExplicacionDias(int diasEntrenamiento) {
|
||||||
|
switch (diasEntrenamiento) {
|
||||||
|
case 1:
|
||||||
|
return "\n- SOLO 1 día de entrenamiento (recomendado para principiantes absolutos)";
|
||||||
|
case 2:
|
||||||
|
return "\n- SOLO 2 días de entrenamiento con descanso intermedio (principiantes)";
|
||||||
|
case 3:
|
||||||
|
return "\n- SOLO 3 días de entrenamiento con descanso intermedio (nivel básico)";
|
||||||
|
case 4:
|
||||||
|
return "\n- SOLO 4 días de entrenamiento con 2 días consecutivos y descanso de fin de semana";
|
||||||
|
case 5:
|
||||||
|
return "\n- SOLO 5 días de entrenamiento con 1 día de descanso intermedio y domingo libre";
|
||||||
|
case 6:
|
||||||
|
return "\n- SOLO 6 días de entrenamiento con domingo de descanso completo";
|
||||||
|
case 7:
|
||||||
|
return "\n- 7 días de entrenamiento (atletas avanzados con variación de intensidad)";
|
||||||
|
default:
|
||||||
|
return "\n- SOLO 3 días de entrenamiento con descanso intermedio (configuración por defecto)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void finishLoading() {
|
||||||
|
|
||||||
|
progressBar.setProgress(100);
|
||||||
|
progressText.setText("100%");
|
||||||
|
statusText.setText(esRegeneracion ? "¡Plan actualizado exitosamente!" : "¡Plan generado exitosamente!");
|
||||||
|
|
||||||
|
|
||||||
|
loadingIcon.clearAnimation();
|
||||||
|
|
||||||
|
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (isAdded()) {
|
||||||
|
if (esRegeneracion) {
|
||||||
|
|
||||||
|
volverAlHomeLimpio();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
irAlLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void volverAlHomeLimpio() {
|
||||||
|
try {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
mostrarPopupPlanActualizado();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error mostrando popup", e);
|
||||||
|
|
||||||
|
navegarSinPopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error general en volverAlHomeLimpio", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarPopupPlanActualizado() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
PlanActualizadoDialog dialog = PlanActualizadoDialog.newInstance(esRegeneracion);
|
||||||
|
|
||||||
|
|
||||||
|
dialog.setOnDialogCloseListener(() -> {
|
||||||
|
Log.d(TAG, "Popup cerrado, iniciando navegación");
|
||||||
|
navegarDespuesDelPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (getActivity() != null && !getActivity().isFinishing()) {
|
||||||
|
dialog.show(requireActivity().getSupportFragmentManager(), "plan_actualizado_dialog");
|
||||||
|
Log.d(TAG, "Popup mostrado exitosamente");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
navegarSinPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error mostrando popup", e);
|
||||||
|
navegarSinPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navegarDespuesDelPopup() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||||
|
if (getActivity() != null && isAdded()) {
|
||||||
|
|
||||||
|
|
||||||
|
FragmentManager fm = requireActivity().getSupportFragmentManager();
|
||||||
|
fm.beginTransaction()
|
||||||
|
.remove(LoadingPlanFragment.this)
|
||||||
|
.commitNowAllowingStateLoss();
|
||||||
|
|
||||||
|
|
||||||
|
while (fm.getBackStackEntryCount() > 0) {
|
||||||
|
String entryName = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName();
|
||||||
|
if (entryName != null && entryName.contains("loading")) {
|
||||||
|
fm.popBackStackImmediate();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (getActivity() instanceof MainActivity) {
|
||||||
|
((MainActivity) getActivity()).navegarA(R.id.nav_home);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Navegación post-popup completada");
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error en navegación post-popup", e);
|
||||||
|
navegarSinPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navegarSinPopup() {
|
||||||
|
try {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().runOnUiThread(() -> {
|
||||||
|
try {
|
||||||
|
FragmentManager fm = requireActivity().getSupportFragmentManager();
|
||||||
|
fm.beginTransaction()
|
||||||
|
.remove(LoadingPlanFragment.this)
|
||||||
|
.commitNowAllowingStateLoss();
|
||||||
|
|
||||||
|
if (getActivity() instanceof MainActivity) {
|
||||||
|
((MainActivity) getActivity()).navegarA(R.id.nav_home);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Toast.makeText(getActivity(), "¡Plan actualizado correctamente!", Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error en navegación fallback", e);
|
||||||
|
if (getActivity() != null) {
|
||||||
|
getActivity().recreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error general navegación sin popup", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void irAlLogin() {
|
||||||
|
Intent intent = new Intent(requireContext(), LoginActivity.class);
|
||||||
|
intent.putExtra("plan_generated", true);
|
||||||
|
intent.putExtra("user_email", nombreUsuario);
|
||||||
|
startActivity(intent);
|
||||||
|
requireActivity().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showError(String error) {
|
||||||
|
runOnUiThreadSafe(() -> {
|
||||||
|
addLogMessageInternal("❌ " + error);
|
||||||
|
|
||||||
|
if (statusText != null) {
|
||||||
|
statusText.setText("Error en la generación");
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (isAdded()) {
|
||||||
|
if (esRegeneracion) {
|
||||||
|
|
||||||
|
Toast.makeText(getContext(), "Error al regenerar plan. Inténtalo más tarde.", Toast.LENGTH_LONG).show();
|
||||||
|
requireActivity().onBackPressed();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
requireActivity().onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
|
||||||
|
if (handler != null) {
|
||||||
|
handler.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class LogItem {
|
||||||
|
private String message;
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public LogItem(String message, long timestamp) {
|
||||||
|
this.message = message;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() { return message; }
|
||||||
|
public long getTimestamp() { return timestamp; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class LogAdapter extends RecyclerView.Adapter<LogAdapter.LogViewHolder> {
|
||||||
|
private List<LogItem> logItems;
|
||||||
|
|
||||||
|
public LogAdapter(List<LogItem> logItems) {
|
||||||
|
this.logItems = logItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public LogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_log, parent, false);
|
||||||
|
return new LogViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull LogViewHolder holder, int position) {
|
||||||
|
LogItem item = logItems.get(position);
|
||||||
|
holder.bind(item, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return logItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class LogViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView messageText;
|
||||||
|
TextView timestampText;
|
||||||
|
View statusIndicator;
|
||||||
|
|
||||||
|
public LogViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
messageText = itemView.findViewById(R.id.messageText);
|
||||||
|
timestampText = itemView.findViewById(R.id.timestampText);
|
||||||
|
statusIndicator = itemView.findViewById(R.id.statusIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(LogItem item, int position) {
|
||||||
|
messageText.setText(item.getMessage());
|
||||||
|
|
||||||
|
|
||||||
|
long seconds = (System.currentTimeMillis() - item.getTimestamp()) / 1000;
|
||||||
|
timestampText.setText("+" + seconds + "s");
|
||||||
|
|
||||||
|
|
||||||
|
itemView.setAlpha(0f);
|
||||||
|
itemView.animate()
|
||||||
|
.alpha(1f)
|
||||||
|
.setDuration(300)
|
||||||
|
.setStartDelay(100)
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Usuario;
|
||||||
|
|
||||||
|
|
||||||
|
public class LoginFragment extends Fragment {
|
||||||
|
|
||||||
|
private static final String TAG = "LoginFragment";
|
||||||
|
private static final String ARG_USUARIO = "usuario";
|
||||||
|
private static final String ARG_NOMBRE_USUARIO = "nombre_usuario";
|
||||||
|
private static final String ARG_CONTRASENA_USUARIO = "contrasena_usuario";
|
||||||
|
private static final String ARG_ES_REGENERACION = "es_regeneracion";
|
||||||
|
|
||||||
|
private Usuario usuario;
|
||||||
|
private String nombreUsuario;
|
||||||
|
private String contrasenaUsuario;
|
||||||
|
private boolean esRegeneracion = false;
|
||||||
|
|
||||||
|
private TextView placeholderText;
|
||||||
|
private View loadingContainer;
|
||||||
|
|
||||||
|
public static LoginFragment newInstance(Usuario usuario, String nombreUsuario, String contrasenaUsuario) {
|
||||||
|
LoginFragment fragment = new LoginFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_USUARIO, usuario);
|
||||||
|
args.putString(ARG_NOMBRE_USUARIO, nombreUsuario);
|
||||||
|
args.putString(ARG_CONTRASENA_USUARIO, contrasenaUsuario);
|
||||||
|
args.putBoolean(ARG_ES_REGENERACION, false);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoginFragment newInstanceRegeneration(Usuario usuario, String nombreUsuario) {
|
||||||
|
LoginFragment fragment = new LoginFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_USUARIO, usuario);
|
||||||
|
args.putString(ARG_NOMBRE_USUARIO, nombreUsuario);
|
||||||
|
args.putString(ARG_CONTRASENA_USUARIO, "regeneration");
|
||||||
|
args.putBoolean(ARG_ES_REGENERACION, true);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
usuario = (Usuario) getArguments().getSerializable(ARG_USUARIO);
|
||||||
|
nombreUsuario = getArguments().getString(ARG_NOMBRE_USUARIO);
|
||||||
|
contrasenaUsuario = getArguments().getString(ARG_CONTRASENA_USUARIO);
|
||||||
|
esRegeneracion = getArguments().getBoolean(ARG_ES_REGENERACION, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "LoginFragment creado - Es regeneración: " + esRegeneracion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_loading, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
loadingContainer = view.findViewById(R.id.loading_container);
|
||||||
|
|
||||||
|
|
||||||
|
cargarLoadingPlanFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cargarLoadingPlanFragment() {
|
||||||
|
if (usuario == null || nombreUsuario == null) {
|
||||||
|
Log.e(TAG, "Datos de usuario incompletos");
|
||||||
|
mostrarError("Datos de usuario incompletos");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (placeholderText != null) {
|
||||||
|
placeholderText.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LoadingPlanFragment loadingFragment;
|
||||||
|
if (esRegeneracion) {
|
||||||
|
loadingFragment = LoadingPlanFragment.newInstanceRegeneration(usuario, nombreUsuario);
|
||||||
|
} else {
|
||||||
|
loadingFragment = LoadingPlanFragment.newInstance(usuario, nombreUsuario, contrasenaUsuario);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FragmentManager childFragmentManager = getChildFragmentManager();
|
||||||
|
childFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.loading_container, loadingFragment, "loading_plan_child")
|
||||||
|
.commitAllowingStateLoss();
|
||||||
|
|
||||||
|
Log.d(TAG, "LoadingPlanFragment cargado en contenedor específico");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error cargando LoadingPlanFragment", e);
|
||||||
|
mostrarError("Error al iniciar la generación del plan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarError(String mensaje) {
|
||||||
|
if (placeholderText != null) {
|
||||||
|
placeholderText.setText("❌ " + mensaje);
|
||||||
|
placeholderText.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (getView() != null) {
|
||||||
|
getView().postDelayed(() -> {
|
||||||
|
if (isAdded() && getActivity() != null) {
|
||||||
|
getActivity().onBackPressed();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
FragmentManager childFragmentManager = getChildFragmentManager();
|
||||||
|
if (childFragmentManager.getBackStackEntryCount() > 0) {
|
||||||
|
childFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error limpiando child fragments: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Usuario getUsuario() {
|
||||||
|
return usuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombreUsuario() {
|
||||||
|
return nombreUsuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEsRegeneracion() {
|
||||||
|
return esRegeneracion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Objetivo;
|
||||||
|
import com.borjabolufer.elevate.utils.ColorUtils;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class ObjetivoFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnObjetivoListener {
|
||||||
|
void onObjetivoSelected(Objetivo objetivo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnObjetivoListener objetivoListener;
|
||||||
|
private Objetivo objetivoSeleccionado;
|
||||||
|
private MaterialButton selectedButton;
|
||||||
|
|
||||||
|
public ObjetivoFragment() {
|
||||||
|
super(R.layout.fragment_objetivo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnObjetivoListener) {
|
||||||
|
objetivoListener = (IOnObjetivoListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement IOnObjetivoListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
MaterialButton btnPerder = view.findViewById(R.id.objetivo_perder);
|
||||||
|
MaterialButton btnGanar = view.findViewById(R.id.objetivo_ganar);
|
||||||
|
MaterialButton btnTonificar = view.findViewById(R.id.objetivo_tonificar);
|
||||||
|
Button btnContinuar = view.findViewById(R.id.btnContinuarObjetivo);
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
|
||||||
|
View.OnClickListener listener = v -> {
|
||||||
|
if (selectedButton != null) {
|
||||||
|
ColorUtils.limpiarSeleccion(selectedButton);
|
||||||
|
}
|
||||||
|
selectedButton = (MaterialButton) v;
|
||||||
|
ColorUtils.marcarSeleccion(selectedButton);
|
||||||
|
|
||||||
|
if (v.getId() == R.id.objetivo_perder) {
|
||||||
|
objetivoSeleccionado = new Objetivo("Perder grasa");
|
||||||
|
} else if (v.getId() == R.id.objetivo_ganar) {
|
||||||
|
objetivoSeleccionado = new Objetivo("Ganar masa");
|
||||||
|
} else if (v.getId() == R.id.objetivo_tonificar) {
|
||||||
|
objetivoSeleccionado = new Objetivo("Tonificar");
|
||||||
|
}
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
btnPerder.setOnClickListener(listener);
|
||||||
|
btnGanar.setOnClickListener(listener);
|
||||||
|
btnTonificar.setOnClickListener(listener);
|
||||||
|
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
if (objetivoSeleccionado != null && objetivoListener != null) {
|
||||||
|
objetivoListener.onObjetivoSelected(objetivoSeleccionado);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Peso;
|
||||||
|
|
||||||
|
public class PesoFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnPesoListener {
|
||||||
|
void onPesoSelected(Peso peso);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnPesoListener pesoListener;
|
||||||
|
private EditText editPeso;
|
||||||
|
private Button btnContinuar;
|
||||||
|
|
||||||
|
public PesoFragment() {
|
||||||
|
super(R.layout.fragment_peso);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
initializeViews(view);
|
||||||
|
setupTextWatcher();
|
||||||
|
setupClickListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeViews(View view) {
|
||||||
|
editPeso = view.findViewById(R.id.editPeso);
|
||||||
|
btnContinuar = view.findViewById(R.id.btnContinuarPeso);
|
||||||
|
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
btnContinuar.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTextWatcher() {
|
||||||
|
editPeso.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
validateInput(s.toString().trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateInput(String input) {
|
||||||
|
boolean isValid = false;
|
||||||
|
|
||||||
|
if (!input.isEmpty()) {
|
||||||
|
try {
|
||||||
|
float peso = Float.parseFloat(input);
|
||||||
|
|
||||||
|
|
||||||
|
if (peso >= 30.0f && peso <= 300.0f) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(isValid);
|
||||||
|
btnContinuar.setAlpha(isValid ? 1.0f : 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupClickListener() {
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
String pesoTexto = editPeso.getText().toString().trim();
|
||||||
|
|
||||||
|
if (pesoTexto.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, ingresa tu peso", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
float pesoValor = Float.parseFloat(pesoTexto);
|
||||||
|
|
||||||
|
|
||||||
|
if (pesoValor < 30.0f || pesoValor > 300.0f) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, ingresa un peso entre 30 y 300 kg",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Peso peso = new Peso(pesoValor);
|
||||||
|
|
||||||
|
if (pesoListener != null) {
|
||||||
|
pesoListener.onPesoSelected(peso);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Error: No se pudo procesar el peso",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Toast.makeText(getContext(), "Por favor, ingresa un número válido",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnPesoListener) {
|
||||||
|
pesoListener = (IOnPesoListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement IOnPesoListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
pesoListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,466 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.cardview.widget.CardView;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.MainActivity;
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Altura;
|
||||||
|
import com.borjabolufer.elevate.model.DiasEntrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Edad;
|
||||||
|
import com.borjabolufer.elevate.model.Intensidad;
|
||||||
|
import com.borjabolufer.elevate.model.Objetivo;
|
||||||
|
import com.borjabolufer.elevate.model.Peso;
|
||||||
|
import com.borjabolufer.elevate.model.Sexo;
|
||||||
|
import com.borjabolufer.elevate.model.Usuario;
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioDbHelper;
|
||||||
|
import com.borjabolufer.elevate.model.UsuarioEntity;
|
||||||
|
import com.borjabolufer.elevate.utils.JsonUtils;
|
||||||
|
import com.borjabolufer.elevate.utils.PasswordUtils;
|
||||||
|
import com.google.android.material.navigation.NavigationView;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ProfileFragment extends Fragment {
|
||||||
|
|
||||||
|
private static final String TAG = "ProfileFragment";
|
||||||
|
private static final String PREFS_NAME = "ElevatePrefs";
|
||||||
|
private static final String KEY_USER_EMAIL = "user_email";
|
||||||
|
|
||||||
|
|
||||||
|
private EditText editNombre;
|
||||||
|
private EditText editEmail;
|
||||||
|
private EditText editPassword;
|
||||||
|
private Button btnGuardarPerfil;
|
||||||
|
private CardView cardPerfil;
|
||||||
|
|
||||||
|
|
||||||
|
private Spinner spinnerSexo;
|
||||||
|
private EditText editEdad;
|
||||||
|
private EditText editPeso;
|
||||||
|
private EditText editAltura;
|
||||||
|
private Spinner spinnerObjetivo;
|
||||||
|
private Spinner spinnerIntensidad;
|
||||||
|
private Spinner spinnerDias;
|
||||||
|
private Button btnActualizarPlan;
|
||||||
|
private CardView cardPlan;
|
||||||
|
|
||||||
|
|
||||||
|
private UsuarioDbHelper dbHelper;
|
||||||
|
private String currentUserEmail;
|
||||||
|
private UsuarioEntity usuarioActual;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_profile, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
inicializarComponentes(view);
|
||||||
|
configurarSpinners();
|
||||||
|
cargarDatosUsuario();
|
||||||
|
configurarListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inicializarComponentes(View view) {
|
||||||
|
dbHelper = new UsuarioDbHelper(getContext());
|
||||||
|
|
||||||
|
|
||||||
|
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
currentUserEmail = prefs.getString(KEY_USER_EMAIL, null);
|
||||||
|
|
||||||
|
|
||||||
|
cardPerfil = view.findViewById(R.id.card_perfil);
|
||||||
|
editNombre = view.findViewById(R.id.edit_nombre);
|
||||||
|
editEmail = view.findViewById(R.id.edit_email);
|
||||||
|
editPassword = view.findViewById(R.id.edit_password);
|
||||||
|
btnGuardarPerfil = view.findViewById(R.id.btn_guardar_perfil);
|
||||||
|
|
||||||
|
|
||||||
|
cardPlan = view.findViewById(R.id.card_plan);
|
||||||
|
spinnerSexo = view.findViewById(R.id.spinner_sexo);
|
||||||
|
editEdad = view.findViewById(R.id.edit_edad);
|
||||||
|
editPeso = view.findViewById(R.id.edit_peso);
|
||||||
|
editAltura = view.findViewById(R.id.edit_altura);
|
||||||
|
spinnerObjetivo = view.findViewById(R.id.spinner_objetivo);
|
||||||
|
spinnerIntensidad = view.findViewById(R.id.spinner_intensidad);
|
||||||
|
spinnerDias = view.findViewById(R.id.spinner_dias);
|
||||||
|
btnActualizarPlan = view.findViewById(R.id.btn_actualizar_plan);
|
||||||
|
|
||||||
|
Log.d(TAG, "Componentes inicializados para usuario: " + currentUserEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarSpinners() {
|
||||||
|
|
||||||
|
String[] sexoOpciones = {"Masculino", "Femenino" ,"Otro"};
|
||||||
|
ArrayAdapter<String> sexoAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, sexoOpciones);
|
||||||
|
sexoAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerSexo.setAdapter(sexoAdapter);
|
||||||
|
|
||||||
|
|
||||||
|
String[] objetivoOpciones = {"Perder grasa", "Tonificar", "Ganar masa"};
|
||||||
|
ArrayAdapter<String> objetivoAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, objetivoOpciones);
|
||||||
|
objetivoAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerObjetivo.setAdapter(objetivoAdapter);
|
||||||
|
|
||||||
|
|
||||||
|
String[] intensidadOpciones = {"Baja", "Media", "Alta"};
|
||||||
|
ArrayAdapter<String> intensidadAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, intensidadOpciones);
|
||||||
|
intensidadAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerIntensidad.setAdapter(intensidadAdapter);
|
||||||
|
|
||||||
|
|
||||||
|
String[] diasOpciones = {"1","2" ,"3", "4", "5", "6", "7"};
|
||||||
|
ArrayAdapter<String> diasAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, diasOpciones);
|
||||||
|
diasAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerDias.setAdapter(diasAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cargarDatosUsuario() {
|
||||||
|
if (currentUserEmail == null) {
|
||||||
|
Toast.makeText(getContext(), "Error: Usuario no identificado", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usuarioActual = dbHelper.getUsuarioPorEmail(currentUserEmail);
|
||||||
|
if (usuarioActual == null) {
|
||||||
|
Toast.makeText(getContext(), "Error al cargar datos del usuario", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
editNombre.setText(usuarioActual.getNombreCompleto());
|
||||||
|
editEmail.setText(usuarioActual.getEmail());
|
||||||
|
editPassword.setText("");
|
||||||
|
|
||||||
|
|
||||||
|
if (usuarioActual.getSexo() != null) {
|
||||||
|
int sexoPosition = usuarioActual.getSexo().equals("Masculino") ? 0 : 1;
|
||||||
|
spinnerSexo.setSelection(sexoPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usuarioActual.getEdad() > 0) {
|
||||||
|
editEdad.setText(String.valueOf(usuarioActual.getEdad()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usuarioActual.getPeso() > 0) {
|
||||||
|
editPeso.setText(String.valueOf(usuarioActual.getPeso()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usuarioActual.getAltura() > 0) {
|
||||||
|
editAltura.setText(String.valueOf(usuarioActual.getAltura()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
configurarSeleccionSpinner(spinnerObjetivo, usuarioActual.getObjetivo());
|
||||||
|
configurarSeleccionSpinner(spinnerIntensidad, usuarioActual.getIntensidad());
|
||||||
|
|
||||||
|
if (usuarioActual.getDiasEntrenamiento() > 0) {
|
||||||
|
int diasPosition = usuarioActual.getDiasEntrenamiento() - 1;
|
||||||
|
if (diasPosition >= 0 && diasPosition < 4) {
|
||||||
|
spinnerDias.setSelection(diasPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Datos cargados para usuario: " + currentUserEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarSeleccionSpinner(Spinner spinner, String valor) {
|
||||||
|
if (valor == null) return;
|
||||||
|
|
||||||
|
ArrayAdapter adapter = (ArrayAdapter) spinner.getAdapter();
|
||||||
|
for (int i = 0; i < adapter.getCount(); i++) {
|
||||||
|
if (adapter.getItem(i).toString().equals(valor)) {
|
||||||
|
spinner.setSelection(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurarListeners() {
|
||||||
|
btnGuardarPerfil.setOnClickListener(v -> guardarPerfilPersonal());
|
||||||
|
btnActualizarPlan.setOnClickListener(v -> actualizarPlanEntrenamiento());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void guardarPerfilPersonal() {
|
||||||
|
String nuevoNombre = editNombre.getText().toString().trim();
|
||||||
|
String nuevoEmail = editEmail.getText().toString().trim();
|
||||||
|
String nuevaPassword = editPassword.getText().toString().trim();
|
||||||
|
|
||||||
|
if (nuevoNombre.isEmpty() || nuevoEmail.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Nombre y email son obligatorios", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!android.util.Patterns.EMAIL_ADDRESS.matcher(nuevoEmail).matches()) {
|
||||||
|
Toast.makeText(getContext(), "Formato de email inválido", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!nuevoEmail.equals(currentUserEmail) && dbHelper.checkUserEmailExists(nuevoEmail)) {
|
||||||
|
Toast.makeText(getContext(), "Este email ya está en uso", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String emailOriginal = currentUserEmail;
|
||||||
|
|
||||||
|
|
||||||
|
usuarioActual.setNombreCompleto(nuevoNombre);
|
||||||
|
usuarioActual.setEmail(nuevoEmail);
|
||||||
|
|
||||||
|
|
||||||
|
if (!nuevaPassword.isEmpty()) {
|
||||||
|
String passwordHash = PasswordUtils.hashPassword(nuevaPassword);
|
||||||
|
usuarioActual.setPasswordHash(passwordHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean actualizado = actualizarUsuarioEnBD();
|
||||||
|
|
||||||
|
if (actualizado) {
|
||||||
|
|
||||||
|
if (!nuevoEmail.equals(emailOriginal)) {
|
||||||
|
gestionarCambioEmailArchivo(emailOriginal, nuevoEmail);
|
||||||
|
|
||||||
|
|
||||||
|
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
prefs.edit().putString(KEY_USER_EMAIL, nuevoEmail).apply();
|
||||||
|
currentUserEmail = nuevoEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
actualizarHeaderNavigationDrawer(nuevoNombre, nuevoEmail);
|
||||||
|
|
||||||
|
Toast.makeText(getContext(), "Perfil actualizado correctamente", Toast.LENGTH_SHORT).show();
|
||||||
|
editPassword.setText("");
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Error al actualizar perfil", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void actualizarHeaderNavigationDrawer(String nuevoNombre, String nuevoEmail) {
|
||||||
|
try {
|
||||||
|
if (getActivity() instanceof MainActivity) {
|
||||||
|
MainActivity mainActivity = (MainActivity) getActivity();
|
||||||
|
|
||||||
|
|
||||||
|
NavigationView navigationView = mainActivity.findViewById(R.id.nav_view);
|
||||||
|
|
||||||
|
if (navigationView != null) {
|
||||||
|
View headerView = navigationView.getHeaderView(0);
|
||||||
|
|
||||||
|
TextView headerTitle = headerView.findViewById(R.id.nav_header_title);
|
||||||
|
TextView headerSubtitle = headerView.findViewById(R.id.nav_header_subtitle);
|
||||||
|
|
||||||
|
if (headerTitle != null) {
|
||||||
|
headerTitle.setText(nuevoNombre);
|
||||||
|
Log.d(TAG, "Header title actualizado: " + nuevoNombre);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerSubtitle != null) {
|
||||||
|
headerSubtitle.setText(nuevoEmail);
|
||||||
|
Log.d(TAG, "Header subtitle actualizado: " + nuevoEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al actualizar header del navigation drawer", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gestionarCambioEmailArchivo(String emailOriginal, String nuevoEmail) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
boolean migracionBD = dbHelper.migrarPlanJson(emailOriginal, nuevoEmail);
|
||||||
|
Log.d(TAG, "Migración en BD: " + migracionBD);
|
||||||
|
|
||||||
|
|
||||||
|
File fileOriginal = JsonUtils.obtenerArchivoPlan(getContext(), emailOriginal);
|
||||||
|
File fileNuevo = JsonUtils.obtenerArchivoPlan(getContext(), nuevoEmail);
|
||||||
|
|
||||||
|
if (fileOriginal.exists()) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String contenidoPlan = JsonUtils.cargarPlanDelUsuario(getContext(), emailOriginal);
|
||||||
|
|
||||||
|
if (contenidoPlan != null && !contenidoPlan.trim().isEmpty()) {
|
||||||
|
|
||||||
|
java.io.FileWriter writer = new java.io.FileWriter(fileNuevo);
|
||||||
|
writer.write(contenidoPlan);
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
|
||||||
|
boolean eliminado = fileOriginal.delete();
|
||||||
|
|
||||||
|
Log.d(TAG, "Migración de archivos exitosa:");
|
||||||
|
Log.d(TAG, "- Archivo original eliminado: " + eliminado);
|
||||||
|
Log.d(TAG, "- Nuevo archivo creado: " + fileNuevo.exists());
|
||||||
|
Log.d(TAG, "- Plan migrado de " + fileOriginal.getName() + " a " + fileNuevo.getName());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Archivo original vacío o no se pudo leer");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
Log.e(TAG, "Error de I/O al migrar archivo: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Archivo original no existe: " + fileOriginal.getAbsolutePath());
|
||||||
|
|
||||||
|
|
||||||
|
if (migracionBD) {
|
||||||
|
boolean creado = dbHelper.guardarPlanJsonEnArchivo(getContext(), nuevoEmail);
|
||||||
|
Log.d(TAG, "Archivo creado desde BD: " + creado);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error general al gestionar cambio de email", e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actualizarPlanEntrenamiento() {
|
||||||
|
|
||||||
|
String edadStr = editEdad.getText().toString().trim();
|
||||||
|
String pesoStr = editPeso.getText().toString().trim();
|
||||||
|
String alturaStr = editAltura.getText().toString().trim();
|
||||||
|
|
||||||
|
if (edadStr.isEmpty() || pesoStr.isEmpty() || alturaStr.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Todos los campos del plan son obligatorios", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int edad = Integer.parseInt(edadStr);
|
||||||
|
double peso = Double.parseDouble(pesoStr);
|
||||||
|
double altura = Double.parseDouble(alturaStr);
|
||||||
|
|
||||||
|
if (edad < 16 || edad > 100) {
|
||||||
|
Toast.makeText(getContext(), "Edad debe estar entre 16 y 100 años", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peso < 30 || peso > 300) {
|
||||||
|
Toast.makeText(getContext(), "Peso debe estar entre 30 y 300 kg", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altura < 100 || altura > 250) {
|
||||||
|
Toast.makeText(getContext(), "Altura debe estar entre 100 y 250 cm", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
usuarioActual.setSexo(spinnerSexo.getSelectedItem().toString());
|
||||||
|
usuarioActual.setEdad(edad);
|
||||||
|
usuarioActual.setPeso(peso);
|
||||||
|
usuarioActual.setAltura(altura);
|
||||||
|
usuarioActual.setObjetivo(spinnerObjetivo.getSelectedItem().toString());
|
||||||
|
usuarioActual.setIntentensidad(spinnerIntensidad.getSelectedItem().toString());
|
||||||
|
usuarioActual.setDiasEntrenamiento(Integer.parseInt(spinnerDias.getSelectedItem().toString()));
|
||||||
|
|
||||||
|
|
||||||
|
boolean actualizado = actualizarUsuarioEnBD();
|
||||||
|
|
||||||
|
if (actualizado) {
|
||||||
|
mostrarDialogoRegenerarPlan();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Error al actualizar plan", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Toast.makeText(getContext(), "Valores numéricos inválidos", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean actualizarUsuarioEnBD() {
|
||||||
|
try {
|
||||||
|
return dbHelper.actualizarUsuario(usuarioActual) > 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al actualizar usuario", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarDialogoRegenerarPlan() {
|
||||||
|
Snackbar.make(getView(), "¿Regenerar plan de entrenamiento con los nuevos datos?", Snackbar.LENGTH_LONG)
|
||||||
|
.setAction("Regenerar", v -> regenerarPlan())
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void regenerarPlan() {
|
||||||
|
|
||||||
|
Usuario usuario = crearUsuarioParaRegenerar();
|
||||||
|
|
||||||
|
|
||||||
|
LoadingPlanFragment loadingFragment = LoadingPlanFragment.newInstanceRegeneration(
|
||||||
|
usuario,
|
||||||
|
currentUserEmail
|
||||||
|
);
|
||||||
|
|
||||||
|
requireActivity().getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.setCustomAnimations(
|
||||||
|
androidx.navigation.ui.R.anim.nav_default_enter_anim,
|
||||||
|
androidx.navigation.ui.R.anim.nav_default_exit_anim,
|
||||||
|
androidx.navigation.ui.R.anim.nav_default_pop_enter_anim,
|
||||||
|
androidx.navigation.ui.R.anim.nav_default_pop_exit_anim
|
||||||
|
)
|
||||||
|
.replace(R.id.nav_host_fragment_content_main, loadingFragment)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Usuario crearUsuarioParaRegenerar() {
|
||||||
|
Usuario usuario = new Usuario();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
usuario.setSexo(new Sexo(usuarioActual.getSexo()));
|
||||||
|
usuario.setEdad(new Edad(usuarioActual.getEdad()));
|
||||||
|
usuario.setPeso(new Peso((float) usuarioActual.getPeso()));
|
||||||
|
usuario.setAltura(new Altura((int) usuarioActual.getAltura()));
|
||||||
|
usuario.setObjetivo(new Objetivo(usuarioActual.getObjetivo()));
|
||||||
|
usuario.setIntensidad(new Intensidad(usuarioActual.getIntensidad()));
|
||||||
|
usuario.setDias(new DiasEntrenamiento(usuarioActual.getDiasEntrenamiento()));
|
||||||
|
|
||||||
|
return usuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (dbHelper != null) {
|
||||||
|
dbHelper.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Usuario;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class ResumenFragment extends Fragment {
|
||||||
|
private static final String ARG_USUARIO = "usuario";
|
||||||
|
private static final String ARG_NOMBRE_USUARIO = "nombre_usuario";
|
||||||
|
private static final String ARG_CONTRASENA_USUARIO = "contrasena_usuario";
|
||||||
|
private static final String TAG = "ResumenFragment";
|
||||||
|
|
||||||
|
private Usuario usuario;
|
||||||
|
private String nombreUsuario;
|
||||||
|
private String contrasenaUsuario;
|
||||||
|
|
||||||
|
private TextView tvResumen;
|
||||||
|
private Button btnGuardarPerfil;
|
||||||
|
|
||||||
|
public ResumenFragment() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResumenFragment newInstance(Usuario usuario, String nombreUsuario, String contrasenaUsuario) {
|
||||||
|
ResumenFragment fragment = new ResumenFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_USUARIO, (Serializable) usuario);
|
||||||
|
args.putString(ARG_NOMBRE_USUARIO, nombreUsuario);
|
||||||
|
args.putString(ARG_CONTRASENA_USUARIO, contrasenaUsuario);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
Log.d("ResumenFragment_newInstance", "newInstance llamado con usuario: " + (usuario == null ? "null" : "OK") +
|
||||||
|
", nombreUsuario: " + nombreUsuario +
|
||||||
|
", contrasenaUsuario: " + (contrasenaUsuario == null ? "null" : "Presente"));
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
Log.d(TAG, "onCreate: Cargando argumentos del Bundle.");
|
||||||
|
if (getArguments().containsKey(ARG_USUARIO)) {
|
||||||
|
usuario = (Usuario) getArguments().getSerializable(ARG_USUARIO);
|
||||||
|
Log.d(TAG, "onCreate: usuario cargado: " + (usuario == null ? "null" : "OK"));
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "onCreate: No se encontró la clave ARG_USUARIO en los argumentos.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getArguments().containsKey(ARG_NOMBRE_USUARIO)) {
|
||||||
|
nombreUsuario = getArguments().getString(ARG_NOMBRE_USUARIO);
|
||||||
|
Log.d(TAG, "onCreate: nombreUsuario cargado: " + nombreUsuario);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "onCreate: No se encontró la clave ARG_NOMBRE_USUARIO en los argumentos.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getArguments().containsKey(ARG_CONTRASENA_USUARIO)) {
|
||||||
|
contrasenaUsuario = getArguments().getString(ARG_CONTRASENA_USUARIO);
|
||||||
|
Log.d(TAG, "onCreate: contrasenaUsuario cargada: " + (contrasenaUsuario == null ? "null" : "Presente"));
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "onCreate: No se encontró la clave ARG_CONTRASENA_USUARIO en los argumentos.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "onCreate: ¡getArguments() es null! No se pasaron argumentos al fragmento.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_resumen, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
tvResumen = view.findViewById(R.id.tvResumen);
|
||||||
|
btnGuardarPerfil = view.findViewById(R.id.btnGuardarPerfil);
|
||||||
|
|
||||||
|
mostrarDatosUsuario(view);
|
||||||
|
|
||||||
|
|
||||||
|
btnGuardarPerfil.setOnClickListener(v -> {
|
||||||
|
if (validarDatos()) {
|
||||||
|
navegarALoadingPlan();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Faltan datos esenciales para generar el plan.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validarDatos() {
|
||||||
|
return usuario != null &&
|
||||||
|
nombreUsuario != null && !nombreUsuario.isEmpty() &&
|
||||||
|
contrasenaUsuario != null && !contrasenaUsuario.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navegarALoadingPlan() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
LoadingPlanFragment loadingFragment = LoadingPlanFragment.newInstance(
|
||||||
|
usuario,
|
||||||
|
nombreUsuario,
|
||||||
|
contrasenaUsuario
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
|
||||||
|
|
||||||
|
|
||||||
|
transaction.setCustomAnimations(
|
||||||
|
R.anim.slide_in_right,
|
||||||
|
R.anim.slide_out_left,
|
||||||
|
R.anim.slide_in_left,
|
||||||
|
R.anim.slide_out_right
|
||||||
|
);
|
||||||
|
|
||||||
|
transaction.replace(R.id.fragmentContainerViewPreguntas, loadingFragment);
|
||||||
|
transaction.addToBackStack(null);
|
||||||
|
transaction.commit();
|
||||||
|
|
||||||
|
Log.d(TAG, "Navegando al LoadingPlanFragment");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al navegar al LoadingPlanFragment", e);
|
||||||
|
Toast.makeText(getContext(), "Error al iniciar la generación del plan", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarDatosUsuario(View view) {
|
||||||
|
if (usuario != null) {
|
||||||
|
|
||||||
|
String alturaStr = (usuario.getAltura() != null)
|
||||||
|
? String.valueOf((int)usuario.getAltura().getAltura()) : "N/A";
|
||||||
|
String pesoStr = (usuario.getPeso() != null)
|
||||||
|
? String.valueOf((int)usuario.getPeso().getPeso()) : "N/A";
|
||||||
|
String edadStr = (usuario.getEdad() != null)
|
||||||
|
? String.valueOf(usuario.getEdad().getEdad()) : "N/A";
|
||||||
|
String sexoStr = (usuario.getSexo() != null && usuario.getSexo().getSexo() != null)
|
||||||
|
? usuario.getSexo().getSexo() : "N/A";
|
||||||
|
String intensidadStr = (usuario.getIntensidad() != null && usuario.getIntensidad().getIntensidad() != null)
|
||||||
|
? usuario.getIntensidad().getIntensidad() : "N/A";
|
||||||
|
String objetivoStr = (usuario.getObjetivo() != null && usuario.getObjetivo().getObjetivo() != null)
|
||||||
|
? usuario.getObjetivo().getObjetivo() : "N/A";
|
||||||
|
String diasStr = (usuario.getDias() != null)
|
||||||
|
? String.valueOf(usuario.getDias().getCantidad()) : "N/A";
|
||||||
|
|
||||||
|
|
||||||
|
TextView tvAlturaValue = view.findViewById(R.id.tvAlturaValue);
|
||||||
|
TextView tvPesoValue = view.findViewById(R.id.tvPesoValue);
|
||||||
|
TextView tvEdadValue = view.findViewById(R.id.tvEdadValue);
|
||||||
|
TextView tvSexoValue = view.findViewById(R.id.tvSexoValue);
|
||||||
|
TextView tvObjetivoValue = view.findViewById(R.id.tvObjetivoValue);
|
||||||
|
TextView tvIntensidadValue = view.findViewById(R.id.tvIntensidadValue);
|
||||||
|
TextView tvDiasValue = view.findViewById(R.id.tvDiasValue);
|
||||||
|
|
||||||
|
if (tvAlturaValue != null) tvAlturaValue.setText(alturaStr + " cm");
|
||||||
|
if (tvPesoValue != null) tvPesoValue.setText(pesoStr + " kg");
|
||||||
|
if (tvEdadValue != null) tvEdadValue.setText(edadStr + " años");
|
||||||
|
if (tvSexoValue != null) tvSexoValue.setText(sexoStr);
|
||||||
|
if (tvObjetivoValue != null) tvObjetivoValue.setText(objetivoStr);
|
||||||
|
if (tvIntensidadValue != null) tvIntensidadValue.setText(intensidadStr);
|
||||||
|
if (tvDiasValue != null) tvDiasValue.setText(diasStr + " días/semana");
|
||||||
|
|
||||||
|
|
||||||
|
String resumenText = "📊 Resumen de tu perfil:\n\n" +
|
||||||
|
"• Altura: " + alturaStr + " cm\n" +
|
||||||
|
"• Peso: " + pesoStr + " kg\n" +
|
||||||
|
"• Edad: " + edadStr + " años\n" +
|
||||||
|
"• Sexo: " + sexoStr + "\n" +
|
||||||
|
"• Objetivo: " + objetivoStr + "\n" +
|
||||||
|
"• Intensidad: " + intensidadStr + "\n" +
|
||||||
|
"• Días de Entrenamiento: " + diasStr + " días/semana\n\n" +
|
||||||
|
"🚀 Tu plan será generado basado en estos datos.";
|
||||||
|
|
||||||
|
if (tvResumen != null) {
|
||||||
|
tvResumen.setText(resumenText);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Datos del usuario mostrados correctamente");
|
||||||
|
} else {
|
||||||
|
if (tvResumen != null) {
|
||||||
|
tvResumen.setText("❌ No se han proporcionado datos del usuario.");
|
||||||
|
}
|
||||||
|
Log.w(TAG, "No hay datos de usuario para mostrar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Usuario getUsuario() {
|
||||||
|
return usuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNombreUsuario() {
|
||||||
|
return nombreUsuario;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tieneInformacionCompleta() {
|
||||||
|
return validarDatos() &&
|
||||||
|
usuario.getAltura() != null &&
|
||||||
|
usuario.getPeso() != null &&
|
||||||
|
usuario.getEdad() != null &&
|
||||||
|
usuario.getSexo() != null &&
|
||||||
|
usuario.getObjetivo() != null &&
|
||||||
|
usuario.getIntensidad() != null &&
|
||||||
|
usuario.getDias() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String obtenerResumenTexto() {
|
||||||
|
if (!tieneInformacionCompleta()) {
|
||||||
|
return "Información incompleta";
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"Perfil: %s, %d años, %.0f cm, %.0f kg | Objetivo: %s | Intensidad: %s | %d días/semana",
|
||||||
|
usuario.getSexo().getSexo(),
|
||||||
|
usuario.getEdad().getEdad(),
|
||||||
|
usuario.getAltura().getAltura(),
|
||||||
|
usuario.getPeso().getPeso(),
|
||||||
|
usuario.getObjetivo().getObjetivo(),
|
||||||
|
usuario.getIntensidad().getIntensidad(),
|
||||||
|
usuario.getDias().getCantidad()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.borjabolufer.elevate.ui.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
import com.borjabolufer.elevate.model.Sexo;
|
||||||
|
import com.borjabolufer.elevate.utils.ColorUtils;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class SexoFragment extends Fragment {
|
||||||
|
|
||||||
|
public interface IOnSexoListener {
|
||||||
|
void onSexoSelected(Sexo sexo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOnSexoListener sexoListener;
|
||||||
|
private Sexo sexoSeleccionado;
|
||||||
|
private MaterialButton selectedButton;
|
||||||
|
|
||||||
|
public SexoFragment() {
|
||||||
|
super(R.layout.fragment_sexo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof IOnSexoListener) {
|
||||||
|
sexoListener = (IOnSexoListener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement IOnSexoListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
MaterialButton btnMasculino = view.findViewById(R.id.sexo_m);
|
||||||
|
MaterialButton btnFemenino = view.findViewById(R.id.sexo_f);
|
||||||
|
MaterialButton btnOtro = view.findViewById(R.id.sexo_o);
|
||||||
|
Button btnContinuar = view.findViewById(R.id.btnContinuarSexo);
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(false);
|
||||||
|
|
||||||
|
View.OnClickListener listener = v -> {
|
||||||
|
if (selectedButton != null) {
|
||||||
|
ColorUtils.limpiarSeleccion(selectedButton);
|
||||||
|
}
|
||||||
|
selectedButton = (MaterialButton) v;
|
||||||
|
ColorUtils.marcarSeleccion(selectedButton);
|
||||||
|
|
||||||
|
if (v.getId() == R.id.sexo_m) {
|
||||||
|
sexoSeleccionado = new Sexo("Masculino");
|
||||||
|
} else if (v.getId() == R.id.sexo_f) {
|
||||||
|
sexoSeleccionado = new Sexo("Femenino");
|
||||||
|
} else if (v.getId() == R.id.sexo_o) {
|
||||||
|
sexoSeleccionado = new Sexo("Otro");
|
||||||
|
}
|
||||||
|
|
||||||
|
btnContinuar.setEnabled(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
btnMasculino.setOnClickListener(listener);
|
||||||
|
btnFemenino.setOnClickListener(listener);
|
||||||
|
btnOtro.setOnClickListener(listener);
|
||||||
|
|
||||||
|
btnContinuar.setOnClickListener(v -> {
|
||||||
|
if (sexoSeleccionado != null && sexoListener != null) {
|
||||||
|
sexoListener.onSexoSelected(sexoSeleccionado);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
public class AiProviderManager {
|
||||||
|
private static final String TAG = "AiProviderManager";
|
||||||
|
private static final String PREFS_NAME = "AiProviderPrefs";
|
||||||
|
private static final String KEY_SELECTED_PROVIDER = "selected_provider";
|
||||||
|
|
||||||
|
public enum AiProvider {
|
||||||
|
CHATGPT("ChatGPT", "OpenAI GPT-3.5 Turbo"),
|
||||||
|
CLAUDE("Claude", "Anthropic Claude 3 Sonnet");
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
AiProvider(String displayName, String description) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() { return displayName; }
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface AiCallback {
|
||||||
|
void onSuccess(String response);
|
||||||
|
void onFailure(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private AiProvider currentProvider;
|
||||||
|
|
||||||
|
public AiProviderManager(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.currentProvider = getSelectedProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void enviarPrompt(String prompt, AiCallback callback) {
|
||||||
|
Log.d(TAG, "Enviando prompt usando: " + currentProvider.getDisplayName());
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (currentProvider) {
|
||||||
|
case CLAUDE:
|
||||||
|
if (ClaudeApi.isApiKeyConfigured()) {
|
||||||
|
ClaudeApi.enviarPrompt(prompt, new ClaudeApi.ClaudeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String jsonResponse) {
|
||||||
|
Log.d(TAG, "Claude respondió exitosamente");
|
||||||
|
callback.onSuccess(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
Log.e(TAG, "Claude falló: " + error);
|
||||||
|
|
||||||
|
intentarConChatGPT(prompt, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Claude API no configurada, usando ChatGPT");
|
||||||
|
intentarConChatGPT(prompt, callback);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHATGPT:
|
||||||
|
if (ChatGptApi.class != null) {
|
||||||
|
ChatGptApi.enviarPrompt(prompt, new ChatGptApi.ChatGptCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String jsonResponse) {
|
||||||
|
Log.d(TAG, "ChatGPT respondió exitosamente");
|
||||||
|
callback.onSuccess(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
Log.e(TAG, "ChatGPT falló: " + error);
|
||||||
|
|
||||||
|
if (ClaudeApi.isApiKeyConfigured()) {
|
||||||
|
Log.d(TAG, "Intentando fallback a Claude...");
|
||||||
|
intentarConClaude(prompt, callback);
|
||||||
|
} else {
|
||||||
|
callback.onFailure("ChatGPT falló: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "Error en JSON: " + e.getMessage());
|
||||||
|
callback.onFailure("Error en configuración: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void intentarConClaude(String prompt, AiCallback callback) {
|
||||||
|
try {
|
||||||
|
ClaudeApi.enviarPrompt(prompt, new ClaudeApi.ClaudeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String jsonResponse) {
|
||||||
|
Log.d(TAG, "Fallback a Claude exitoso");
|
||||||
|
callback.onSuccess(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
Log.e(TAG, "Fallback a Claude también falló: " + error);
|
||||||
|
callback.onFailure("Ambos proveedores fallaron. ChatGPT y Claude no disponibles.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.onFailure("Error en fallback a Claude: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void intentarConChatGPT(String prompt, AiCallback callback) {
|
||||||
|
try {
|
||||||
|
ChatGptApi.enviarPrompt(prompt, new ChatGptApi.ChatGptCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String jsonResponse) {
|
||||||
|
Log.d(TAG, "Fallback a ChatGPT exitoso");
|
||||||
|
callback.onSuccess(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
Log.e(TAG, "Fallback a ChatGPT también falló: " + error);
|
||||||
|
callback.onFailure("Ambos proveedores fallaron. Claude y ChatGPT no disponibles.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.onFailure("Error en fallback a ChatGPT: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setProvider(AiProvider provider) {
|
||||||
|
this.currentProvider = provider;
|
||||||
|
saveSelectedProvider(provider);
|
||||||
|
Log.d(TAG, "Proveedor cambiado a: " + provider.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AiProvider getCurrentProvider() {
|
||||||
|
return currentProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void saveSelectedProvider(AiProvider provider) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
prefs.edit().putString(KEY_SELECTED_PROVIDER, provider.name()).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AiProvider getSelectedProvider() {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
String providerName = prefs.getString(KEY_SELECTED_PROVIDER, AiProvider.CHATGPT.name());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return AiProvider.valueOf(providerName);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.w(TAG, "Proveedor no reconocido, usando ChatGPT por defecto");
|
||||||
|
return AiProvider.CHATGPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isChatGptAvailable() {
|
||||||
|
|
||||||
|
return ChatGptApi.class != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClaudeAvailable() {
|
||||||
|
return ClaudeApi.isApiKeyConfigured();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProviderStatus() {
|
||||||
|
StringBuilder status = new StringBuilder();
|
||||||
|
status.append("Proveedor actual: ").append(currentProvider.getDisplayName()).append("\n");
|
||||||
|
status.append("ChatGPT disponible: ").append(isChatGptAvailable() ? "Sí" : "No").append("\n");
|
||||||
|
status.append("Claude disponible: ").append(isClaudeAvailable() ? "Sí" : "No").append("\n");
|
||||||
|
return status.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void verificarConectividad(AiCallback callback) {
|
||||||
|
String promptPrueba = "Responde solo con 'OK' para confirmar que estás funcionando.";
|
||||||
|
|
||||||
|
enviarPrompt(promptPrueba, new AiCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String response) {
|
||||||
|
if (response.toLowerCase().contains("ok")) {
|
||||||
|
callback.onSuccess("Conectividad verificada con " + currentProvider.getDisplayName());
|
||||||
|
} else {
|
||||||
|
callback.onSuccess("Conectado con " + currentProvider.getDisplayName() +
|
||||||
|
" pero respuesta inesperada: " + response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
callback.onFailure("Fallo en verificación de conectividad: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import okhttp3.*;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ChatGptApi {
|
||||||
|
private static final String API_URL = "https://api.openai.com/v1/chat/completions";
|
||||||
|
private static final String API_KEY = "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";
|
||||||
|
|
||||||
|
public interface ChatGptCallback {
|
||||||
|
void onSuccess(String jsonResponse);
|
||||||
|
void onFailure(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enviarPrompt(String prompt, ChatGptCallback callback) throws JSONException {
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(10, TimeUnit.MINUTES)
|
||||||
|
.writeTimeout(10, TimeUnit.MINUTES)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
JSONObject jsonBody = new JSONObject();
|
||||||
|
jsonBody.put("model", "gpt-4o");
|
||||||
|
jsonBody.put("messages", new org.json.JSONArray()
|
||||||
|
.put(new JSONObject().put("role", "user").put("content", prompt))
|
||||||
|
);
|
||||||
|
jsonBody.put("temperature", 0.7);
|
||||||
|
|
||||||
|
RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.parse("application/json"));
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(API_URL)
|
||||||
|
.addHeader("Authorization", "Bearer " + API_KEY)
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.post(body)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
callback.onFailure(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response) throws IOException {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
callback.onFailure("Error HTTP: " + response.code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
JSONObject json;
|
||||||
|
try {
|
||||||
|
json = new JSONObject(responseBody);
|
||||||
|
String result = json.getJSONArray("choices")
|
||||||
|
.getJSONObject(0)
|
||||||
|
.getJSONObject("message")
|
||||||
|
.getString("content");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.onFailure("Error al procesar JSON: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import okhttp3.*;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ClaudeApi {
|
||||||
|
private static final String API_URL = "https://api.anthropic.com/v1/messages";
|
||||||
|
private static final String API_KEY = "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";
|
||||||
|
private static final String MODEL = "claude-3-5-haiku-20241022";
|
||||||
|
|
||||||
|
public interface ClaudeCallback {
|
||||||
|
void onSuccess(String jsonResponse);
|
||||||
|
void onFailure(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enviarPrompt(String prompt, ClaudeCallback callback) throws JSONException {
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(15, TimeUnit.MINUTES)
|
||||||
|
.writeTimeout(10, TimeUnit.MINUTES)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
JSONObject jsonBody = new JSONObject();
|
||||||
|
jsonBody.put("model", MODEL);
|
||||||
|
jsonBody.put("max_tokens", 4096);
|
||||||
|
|
||||||
|
|
||||||
|
jsonBody.put("messages", new org.json.JSONArray()
|
||||||
|
.put(new JSONObject().put("role", "user").put("content", prompt))
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
jsonBody.put("temperature", 0.7);
|
||||||
|
jsonBody.put("top_p", 0.9);
|
||||||
|
|
||||||
|
RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.parse("application/json"));
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(API_URL)
|
||||||
|
.addHeader("x-api-key", API_KEY)
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.addHeader("anthropic-version", "2023-06-01")
|
||||||
|
.post(body)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
callback.onFailure("Error de conexión: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response) throws IOException {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
String errorBody = response.body() != null ? response.body().string() : "Sin detalles";
|
||||||
|
callback.onFailure("Error HTTP " + response.code() + ": " + errorBody);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject(responseBody);
|
||||||
|
|
||||||
|
|
||||||
|
String result = json.getJSONArray("content")
|
||||||
|
.getJSONObject(0)
|
||||||
|
.getString("text");
|
||||||
|
|
||||||
|
callback.onSuccess(result);
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.onFailure("Error al procesar respuesta de Claude: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void enviarPromptPersonalizado(String prompt, int maxTokens, double temperature, ClaudeCallback callback) throws JSONException {
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(15, TimeUnit.MINUTES)
|
||||||
|
.writeTimeout(10, TimeUnit.MINUTES)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
JSONObject jsonBody = new JSONObject();
|
||||||
|
jsonBody.put("model", MODEL);
|
||||||
|
jsonBody.put("max_tokens", maxTokens);
|
||||||
|
jsonBody.put("messages", new org.json.JSONArray()
|
||||||
|
.put(new JSONObject().put("role", "user").put("content", prompt))
|
||||||
|
);
|
||||||
|
jsonBody.put("temperature", temperature);
|
||||||
|
jsonBody.put("top_p", 0.9);
|
||||||
|
|
||||||
|
RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.parse("application/json"));
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(API_URL)
|
||||||
|
.addHeader("x-api-key", API_KEY)
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.addHeader("anthropic-version", "2023-06-01")
|
||||||
|
.post(body)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
callback.onFailure("Error de conexión: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response) throws IOException {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
String errorBody = response.body() != null ? response.body().string() : "Sin detalles";
|
||||||
|
callback.onFailure("Error HTTP " + response.code() + ": " + errorBody);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject(responseBody);
|
||||||
|
String result = json.getJSONArray("content")
|
||||||
|
.getJSONObject(0)
|
||||||
|
.getString("text");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.onFailure("Error al procesar respuesta de Claude: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void verificarConexion(ClaudeCallback callback) {
|
||||||
|
try {
|
||||||
|
enviarPrompt("Hola, ¿estás funcionando correctamente? Responde solo con 'Sí'.", new ClaudeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String jsonResponse) {
|
||||||
|
if (jsonResponse.toLowerCase().contains("sí") || jsonResponse.toLowerCase().contains("si")) {
|
||||||
|
callback.onSuccess("Conexión exitosa con Claude API");
|
||||||
|
} else {
|
||||||
|
callback.onSuccess("Conectado, pero respuesta inesperada: " + jsonResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String error) {
|
||||||
|
callback.onFailure("Fallo en verificación: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.onFailure("Error en verificación: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isApiKeyConfigured() {
|
||||||
|
return API_KEY != null &&
|
||||||
|
!API_KEY.isEmpty() &&
|
||||||
|
!API_KEY.equals("TU_CLAUDE_API_KEY_AQUI");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getModelInfo() {
|
||||||
|
return "Modelo: " + MODEL + "\n" +
|
||||||
|
"Provider: Anthropic Claude\n" +
|
||||||
|
"Configuración: Optimizado para generación de planes de fitness";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class ColorUtils {
|
||||||
|
|
||||||
|
|
||||||
|
public static final int COLOR_SELECCIONADO = Color.parseColor("#64B5F6");
|
||||||
|
public static final int COLOR_NORMAL = Color.parseColor("#FAFAFA");
|
||||||
|
public static final int COLOR_STROKE_SELECCIONADO = Color.parseColor("#2196F3");
|
||||||
|
public static final int COLOR_STROKE_NORMAL = Color.parseColor("#E0E0E0");
|
||||||
|
|
||||||
|
public static final int COLOR_TEXTO_SELECCIONADO = Color.parseColor("#FFFFFF");
|
||||||
|
public static final int COLOR_TEXTO_NORMAL = Color.parseColor("#2E2E2E");
|
||||||
|
|
||||||
|
|
||||||
|
public static void marcarSeleccion(MaterialButton boton) {
|
||||||
|
|
||||||
|
boton.setBackgroundTintList(ColorStateList.valueOf(COLOR_SELECCIONADO));
|
||||||
|
|
||||||
|
|
||||||
|
boton.setStrokeColor(ColorStateList.valueOf(COLOR_STROKE_SELECCIONADO));
|
||||||
|
|
||||||
|
|
||||||
|
boton.setTextColor(COLOR_TEXTO_SELECCIONADO);
|
||||||
|
|
||||||
|
|
||||||
|
if (boton.getStrokeWidth() == 0) {
|
||||||
|
boton.setStrokeWidth(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void limpiarSeleccion(MaterialButton boton) {
|
||||||
|
|
||||||
|
boton.setBackgroundTintList(ColorStateList.valueOf(COLOR_NORMAL));
|
||||||
|
|
||||||
|
|
||||||
|
boton.setStrokeColor(ColorStateList.valueOf(COLOR_STROKE_NORMAL));
|
||||||
|
|
||||||
|
|
||||||
|
boton.setTextColor(COLOR_TEXTO_NORMAL);
|
||||||
|
|
||||||
|
|
||||||
|
boton.setStrokeWidth(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean estaSeleccionado(MaterialButton boton) {
|
||||||
|
ColorStateList backgroundTint = boton.getBackgroundTintList();
|
||||||
|
if (backgroundTint != null) {
|
||||||
|
int color = backgroundTint.getDefaultColor();
|
||||||
|
return color == COLOR_SELECCIONADO;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void alternarSeleccion(MaterialButton boton) {
|
||||||
|
if (estaSeleccionado(boton)) {
|
||||||
|
limpiarSeleccion(boton);
|
||||||
|
} else {
|
||||||
|
marcarSeleccion(boton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void limpiarSeleccionMultiple(MaterialButton... botones) {
|
||||||
|
for (MaterialButton boton : botones) {
|
||||||
|
if (boton != null) {
|
||||||
|
limpiarSeleccion(boton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void seleccionarUnico(MaterialButton botonSeleccionado, MaterialButton... todosBotones) {
|
||||||
|
|
||||||
|
limpiarSeleccionMultiple(todosBotones);
|
||||||
|
|
||||||
|
|
||||||
|
if (botonSeleccionado != null) {
|
||||||
|
marcarSeleccion(botonSeleccionado);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,278 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.model.Comida;
|
||||||
|
import com.borjabolufer.elevate.model.DiasEntrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Dieta;
|
||||||
|
import com.borjabolufer.elevate.model.Ejercicio;
|
||||||
|
import com.borjabolufer.elevate.model.Entrenamiento;
|
||||||
|
import com.borjabolufer.elevate.model.Semana;
|
||||||
|
import com.borjabolufer.elevate.model.ValorNutricional;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class JsonParser {
|
||||||
|
private static final String TAG = "JsonElevateParser";
|
||||||
|
private Gson gson;
|
||||||
|
|
||||||
|
|
||||||
|
public JsonParser(Context context) {
|
||||||
|
|
||||||
|
this.gson = new GsonBuilder()
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JSONObject parsearDesdeArchivoInterno(Context context) {
|
||||||
|
try {
|
||||||
|
File file = new File(context.getFilesDir(), "plan_entrenamiento.json");
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
int size = fis.available();
|
||||||
|
byte[] buffer = new byte[size];
|
||||||
|
int bytesRead = fis.read(buffer);
|
||||||
|
fis.close();
|
||||||
|
if (bytesRead < size) {
|
||||||
|
Log.w(TAG, "No se pudo leer el archivo completo.");
|
||||||
|
|
||||||
|
}
|
||||||
|
String jsonStr = new String(buffer, "UTF-8");
|
||||||
|
return new JSONObject(jsonStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al parsear desde archivo interno: " + e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ValorNutricional parseValorNutricional(JSONObject vnObj) throws JSONException {
|
||||||
|
if (vnObj == null) return null;
|
||||||
|
return new ValorNutricional(
|
||||||
|
vnObj.optInt("calorias", 0),
|
||||||
|
vnObj.optInt("proteinas", 0),
|
||||||
|
vnObj.optInt("carbohidratos", 0),
|
||||||
|
vnObj.optInt("grasas", 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Comida parseComida(JSONObject comidaObj) throws JSONException {
|
||||||
|
if (comidaObj == null) return null;
|
||||||
|
|
||||||
|
|
||||||
|
String nombre = comidaObj.optString("nombre_del_plato", "Comida sin nombre");
|
||||||
|
String receta = comidaObj.optString("receta", "");
|
||||||
|
String videoUrl = comidaObj.optString("video_url", "");
|
||||||
|
|
||||||
|
List<String> ingredientes = new ArrayList<>();
|
||||||
|
JSONArray alimentosArray = comidaObj.optJSONArray("ingredientes");
|
||||||
|
if (alimentosArray != null) {
|
||||||
|
for (int i = 0; i < alimentosArray.length(); i++) {
|
||||||
|
ingredientes.add(alimentosArray.getString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValorNutricional valorNutricional = null;
|
||||||
|
if (comidaObj.has("valor_nutricional") && !comidaObj.isNull("valor_nutricional")) {
|
||||||
|
valorNutricional = parseValorNutricional(comidaObj.getJSONObject("valor_nutricional"));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
valorNutricional = new ValorNutricional(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Comida(nombre, ingredientes, receta, valorNutricional, videoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Semana convertirJsonASemana(JSONObject jsonObj) {
|
||||||
|
List<DiasEntrenamiento> listaDias = new ArrayList<>();
|
||||||
|
|
||||||
|
if (jsonObj == null) {
|
||||||
|
Log.e(TAG, "El objeto JSON principal es null en convertirJsonASemana");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONArray diasArray = jsonObj.getJSONArray("semana");
|
||||||
|
|
||||||
|
for (int i = 0; i < diasArray.length(); i++) {
|
||||||
|
JSONObject diaObj = diasArray.getJSONObject(i);
|
||||||
|
String nombreDia = diaObj.getString("dia");
|
||||||
|
|
||||||
|
|
||||||
|
Entrenamiento entrenamiento = null;
|
||||||
|
if (diaObj.has("entrenamiento") && !diaObj.isNull("entrenamiento")) {
|
||||||
|
JSONObject entObj = diaObj.getJSONObject("entrenamiento");
|
||||||
|
entrenamiento = new Entrenamiento(
|
||||||
|
JsonUtils.parseEjercicios(entObj.getJSONArray("calentamiento")),
|
||||||
|
JsonUtils.parseEjerciciosPrincipales(entObj.getJSONArray("ejercicios_principales")),
|
||||||
|
JsonUtils.parseEjercicios(entObj.getJSONArray("enfriamiento"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Dieta dieta = null;
|
||||||
|
if (diaObj.has("dieta") && !diaObj.isNull("dieta")) {
|
||||||
|
JSONObject dietaObj = diaObj.getJSONObject("dieta");
|
||||||
|
Comida desayuno = dietaObj.has("desayuno") && !dietaObj.isNull("desayuno") ? parseComida(dietaObj.getJSONObject("desayuno")) : null;
|
||||||
|
Comida comida = dietaObj.has("comida") && !dietaObj.isNull("comida") ? parseComida(dietaObj.getJSONObject("comida")) : null;
|
||||||
|
Comida cena = dietaObj.has("cena") && !dietaObj.isNull("cena") ? parseComida(dietaObj.getJSONObject("cena")) : null;
|
||||||
|
Comida snacks = dietaObj.has("snack") && !dietaObj.isNull("snack") ? parseComida(dietaObj.getJSONObject("snack")) : null;
|
||||||
|
dieta = new Dieta(desayuno, comida, cena, snacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
DiasEntrenamiento dia = new DiasEntrenamiento(0, nombreDia, entrenamiento, dieta);
|
||||||
|
listaDias.add(dia);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "Error al convertir JSON a Semana: " + e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Semana(listaDias);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Semana parsearDesdeString(String jsonString) {
|
||||||
|
try {
|
||||||
|
Semana semana = gson.fromJson(jsonString, Semana.class);
|
||||||
|
Log.d(TAG, "JSON parseado exitosamente desde String con Gson");
|
||||||
|
return semana;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al parsear JSON desde String con Gson: " + e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String convertirAJson(Semana semana) {
|
||||||
|
try {
|
||||||
|
return gson.toJson(semana);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al convertir a JSON con Gson: " + e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DiasEntrenamiento obtenerDia(Semana semana, String nombreDia) {
|
||||||
|
if (semana == null || semana.getSemana() == null) return null;
|
||||||
|
|
||||||
|
for (DiasEntrenamiento dia : semana.getSemana()) {
|
||||||
|
if (dia.getDia().equalsIgnoreCase(nombreDia)) {
|
||||||
|
return dia;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<String> obtenerEjerciciosDelDia(DiasEntrenamiento dia) {
|
||||||
|
List<String> ejercicios = new ArrayList<>();
|
||||||
|
|
||||||
|
if (dia == null || dia.getEntrenamiento() == null) return ejercicios;
|
||||||
|
|
||||||
|
Entrenamiento entrenamiento = dia.getEntrenamiento();
|
||||||
|
|
||||||
|
if (entrenamiento.getCalentamiento() != null) {
|
||||||
|
for (Ejercicio ejercicio : entrenamiento.getCalentamiento()) {
|
||||||
|
if (ejercicio != null) ejercicios.add("Calentamiento: " + ejercicio.getNombre());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entrenamiento.getEjercicios_principales() != null) {
|
||||||
|
for (Ejercicio ejercicio : entrenamiento.getEjercicios_principales()) {
|
||||||
|
if (ejercicio != null) ejercicios.add("Principal: " + ejercicio.getNombre() +
|
||||||
|
" (" + ejercicio.getSeries() + "x" + ejercicio.getRepeticiones() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entrenamiento.getEnfriamiento() != null) {
|
||||||
|
for (Ejercicio ejercicio : entrenamiento.getEnfriamiento()) {
|
||||||
|
if (ejercicio != null) ejercicios.add("Enfriamiento: " + ejercicio.getNombre());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ejercicios;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int calcularCaloriasTotales(DiasEntrenamiento dia) {
|
||||||
|
if (dia == null || dia.getDieta() == null) return 0;
|
||||||
|
|
||||||
|
Dieta dieta = dia.getDieta();
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
if (dieta.getDesayuno() != null && dieta.getDesayuno().getValor_nutricional() != null) {
|
||||||
|
total += dieta.getDesayuno().getValor_nutricional().getCalorías();
|
||||||
|
}
|
||||||
|
if (dieta.getComida() != null && dieta.getComida().getValor_nutricional() != null) {
|
||||||
|
total += dieta.getComida().getValor_nutricional().getCalorías();
|
||||||
|
}
|
||||||
|
if (dieta.getCena() != null && dieta.getCena().getValor_nutricional() != null) {
|
||||||
|
total += dieta.getCena().getValor_nutricional().getCalorías();
|
||||||
|
}
|
||||||
|
if (dieta.getSnacks() != null && dieta.getSnacks().getValor_nutricional() != null) {
|
||||||
|
total += dieta.getSnacks().getValor_nutricional().getCalorías();
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String obtenerResumenNutricional(DiasEntrenamiento dia) {
|
||||||
|
if (dia == null || dia.getDieta() == null) return "Sin información";
|
||||||
|
|
||||||
|
Dieta dieta = dia.getDieta();
|
||||||
|
int calorias = 0, proteinas = 0, carbohidratos = 0, grasas = 0;
|
||||||
|
|
||||||
|
ValorNutricional[] valores = {
|
||||||
|
dieta.getDesayuno() != null ? dieta.getDesayuno().getValor_nutricional() : null,
|
||||||
|
dieta.getComida() != null ? dieta.getComida().getValor_nutricional() : null,
|
||||||
|
dieta.getCena() != null ? dieta.getCena().getValor_nutricional() : null,
|
||||||
|
dieta.getSnacks() != null ? dieta.getSnacks().getValor_nutricional() : null
|
||||||
|
};
|
||||||
|
|
||||||
|
for (ValorNutricional vn : valores) {
|
||||||
|
if (vn != null) {
|
||||||
|
calorias += vn.getCalorías();
|
||||||
|
proteinas += vn.getProteínas();
|
||||||
|
carbohidratos += vn.getCarbohidratos();
|
||||||
|
grasas += vn.getGrasas();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("Calorías: %d | Proteínas: %dg | Carbohidratos: %dg | Grasas: %dg",
|
||||||
|
calorias, proteinas, carbohidratos, grasas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean validarSemana(Semana semana) {
|
||||||
|
if (semana == null) {
|
||||||
|
Log.w(TAG, "Semana es null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semana.getSemana() == null || semana.getSemana().isEmpty()) {
|
||||||
|
Log.w(TAG, "Lista de días está vacía");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Semana válida con " + semana.getSemana().size() + " días");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import static android.content.ContentValues.TAG;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.model.Ejercicio;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JsonUtils {
|
||||||
|
|
||||||
|
public static List<Ejercicio> parseEjercicios(JSONArray jsonArray) throws JSONException {
|
||||||
|
List<Ejercicio> lista = new ArrayList<>();
|
||||||
|
|
||||||
|
if (jsonArray == null) return lista;
|
||||||
|
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
Object element = jsonArray.get(i);
|
||||||
|
|
||||||
|
if (element instanceof JSONObject) {
|
||||||
|
JSONObject obj = (JSONObject) element;
|
||||||
|
|
||||||
|
String nombre = obj.optString("nombre", "Ejercicio sin nombre");
|
||||||
|
String descripcion = obj.optString("descripcion", "");
|
||||||
|
String videoUrl = obj.optString("video_url", "");
|
||||||
|
String duracion = obj.optString("descanso", "");
|
||||||
|
int series = obj.optInt("series");
|
||||||
|
int repeticiones = obj.optInt("repeticiones");
|
||||||
|
|
||||||
|
lista.add(new Ejercicio(nombre, descripcion, series, repeticiones, duracion, videoUrl));
|
||||||
|
|
||||||
|
} else if (element instanceof String) {
|
||||||
|
String nombre = (String) element;
|
||||||
|
lista.add(new Ejercicio(nombre, "", 0, 0, "", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lista;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Ejercicio> parseEjerciciosPrincipales(JSONArray jsonArray) throws JSONException {
|
||||||
|
List<Ejercicio> lista = new ArrayList<>();
|
||||||
|
|
||||||
|
if (jsonArray == null) return lista;
|
||||||
|
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
JSONObject obj = jsonArray.getJSONObject(i);
|
||||||
|
|
||||||
|
String nombre = obj.optString("nombre", "Ejercicio sin nombre");
|
||||||
|
String descripcion = obj.optString("descripcion", "");
|
||||||
|
String videoUrl = obj.optString("video_url", "");
|
||||||
|
|
||||||
|
int series = obj.optInt("series", 0);
|
||||||
|
int repeticiones = obj.optInt("repeticiones", 0);
|
||||||
|
String descanso = obj.optString("descanso", "");
|
||||||
|
|
||||||
|
StringBuilder descripcionCompleta = new StringBuilder(descripcion);
|
||||||
|
|
||||||
|
lista.add(new Ejercicio(nombre, descripcionCompleta.toString(), series, repeticiones, descanso, videoUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lista;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File obtenerArchivoPlan(Context context, String emailUsuario) {
|
||||||
|
emailUsuario = normalizarEmail(emailUsuario);
|
||||||
|
File archivo = new File(context.getFilesDir(), "plan_"+ emailUsuario + ".json");
|
||||||
|
Log.d(TAG, "Buscando archivo: " + archivo.getAbsolutePath());
|
||||||
|
return archivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String normalizarEmail(String email) {
|
||||||
|
return email.replace("@", "_").replace(".", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String cargarPlanDelUsuario(Context context, String emailUsuario) {
|
||||||
|
try {
|
||||||
|
File archivo = obtenerArchivoPlan(context, emailUsuario);
|
||||||
|
if (archivo.exists()) {
|
||||||
|
Log.d(TAG, "Archivo encontrado. Leyendo contenido...");
|
||||||
|
|
||||||
|
|
||||||
|
return leerArchivoCompatible(archivo);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Archivo no encontrado para el usuario: " + emailUsuario);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error leyendo el archivo JSON del usuario", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String leerArchivoCompatible(File archivo) throws IOException {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|
||||||
|
return new String(Files.readAllBytes(archivo.toPath()));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return leerArchivoTradicional(archivo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String leerArchivoTradicional(File archivo) throws IOException {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(archivo);
|
||||||
|
byte[] buffer = new byte[(int) archivo.length()];
|
||||||
|
int bytesRead = fis.read(buffer);
|
||||||
|
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
return new String(buffer, 0, bytesRead, "UTF-8");
|
||||||
|
} else {
|
||||||
|
throw new IOException("No se pudieron leer datos del archivo");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (fis != null) {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "Error al cerrar FileInputStream: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean existeArchivoPlan(Context context, String emailUsuario) {
|
||||||
|
File archivo = obtenerArchivoPlan(context, emailUsuario);
|
||||||
|
boolean existe = archivo.exists();
|
||||||
|
Log.d(TAG, "Archivo de plan para " + emailUsuario + " existe: " + existe);
|
||||||
|
return existe;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean eliminarArchivoPlan(Context context, String emailUsuario) {
|
||||||
|
try {
|
||||||
|
File archivo = obtenerArchivoPlan(context, emailUsuario);
|
||||||
|
if (archivo.exists()) {
|
||||||
|
boolean eliminado = archivo.delete();
|
||||||
|
Log.d(TAG, "Archivo de plan eliminado para " + emailUsuario + ": " + eliminado);
|
||||||
|
return eliminado;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al eliminar archivo de plan: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean guardarPlanEnArchivo(Context context, String emailUsuario, String contenidoJson) {
|
||||||
|
try {
|
||||||
|
File archivo = obtenerArchivoPlan(context, emailUsuario);
|
||||||
|
|
||||||
|
FileOutputStream fos = new FileOutputStream(archivo);
|
||||||
|
fos.write(contenidoJson.getBytes("UTF-8"));
|
||||||
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
Log.d(TAG, "Plan guardado en archivo para usuario: " + emailUsuario);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error al guardar plan en archivo: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
|
||||||
|
|
||||||
|
public class PasswordUtils {
|
||||||
|
|
||||||
|
private static final String TAG = "PasswordUtils";
|
||||||
|
private static final String ALGORITHM = "PBKDF2WithHmacSHA256";
|
||||||
|
private static final int ITERATION_COUNT = 65536;
|
||||||
|
private static final int KEY_LENGTH = 256;
|
||||||
|
private static final int SALT_SIZE = 16;
|
||||||
|
|
||||||
|
|
||||||
|
public static String hashPassword(String password) {
|
||||||
|
if (password == null || password.isEmpty()) {
|
||||||
|
Log.e(TAG, "La contraseña no puede ser nula o vacía.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] salt = new byte[SALT_SIZE];
|
||||||
|
new SecureRandom().nextBytes(salt);
|
||||||
|
|
||||||
|
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
|
||||||
|
SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
|
||||||
|
byte[] hash = factory.generateSecret(spec).getEncoded();
|
||||||
|
|
||||||
|
String saltBase64 = android.util.Base64.encodeToString(salt, android.util.Base64.NO_WRAP);
|
||||||
|
String hashBase64 = android.util.Base64.encodeToString(hash, android.util.Base64.NO_WRAP);
|
||||||
|
|
||||||
|
return saltBase64 + ":" + hashBase64;
|
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||||
|
Log.e(TAG, "Error al hashear la contraseña", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean verifyPassword(String plainPassword, String storedPasswordWithSalt) {
|
||||||
|
if (plainPassword == null || plainPassword.isEmpty() || storedPasswordWithSalt == null || storedPasswordWithSalt.isEmpty()) {
|
||||||
|
Log.w(TAG, "Contraseña o hash almacenado vacío.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Hash recibido: " + storedPasswordWithSalt);
|
||||||
|
Log.e(TAG, "Valor real recibido para verificar: [" + storedPasswordWithSalt + "]");
|
||||||
|
String[] parts = storedPasswordWithSalt.split(":");
|
||||||
|
if (parts.length != 2) {
|
||||||
|
Log.e(TAG, "Formato de hash incorrecto. Se esperaba 'salt:hash'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] salt = android.util.Base64.decode(parts[0], android.util.Base64.NO_WRAP);
|
||||||
|
byte[] storedHash = android.util.Base64.decode(parts[1], android.util.Base64.NO_WRAP);
|
||||||
|
|
||||||
|
KeySpec spec = new PBEKeySpec(plainPassword.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
|
||||||
|
SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
|
||||||
|
byte[] inputHash = factory.generateSecret(spec).getEncoded();
|
||||||
|
|
||||||
|
return Arrays.equals(storedHash, inputHash);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al verificar la contraseña", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.borjabolufer.elevate.R;
|
||||||
|
|
||||||
|
public class PlanActualizadoDialog extends DialogFragment {
|
||||||
|
|
||||||
|
private static final String ARG_TITULO = "titulo";
|
||||||
|
private static final String ARG_MENSAJE = "mensaje";
|
||||||
|
private static final String ARG_ES_REGENERACION = "es_regeneracion";
|
||||||
|
|
||||||
|
private OnDialogCloseListener listener;
|
||||||
|
|
||||||
|
public interface OnDialogCloseListener {
|
||||||
|
void onDialogClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PlanActualizadoDialog newInstance(boolean esRegeneracion) {
|
||||||
|
PlanActualizadoDialog fragment = new PlanActualizadoDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
|
||||||
|
if (esRegeneracion) {
|
||||||
|
args.putString(ARG_TITULO, "¡Plan Actualizado!");
|
||||||
|
args.putString(ARG_MENSAJE, "Tu plan personalizado ha sido actualizado con éxito. Los nuevos ejercicios y recetas ya están disponibles.");
|
||||||
|
} else {
|
||||||
|
args.putString(ARG_TITULO, "¡Plan Creado!");
|
||||||
|
args.putString(ARG_MENSAJE, "Tu plan personalizado ha sido creado con éxito. ¡Ya puedes comenzar tu journey fitness!");
|
||||||
|
}
|
||||||
|
|
||||||
|
args.putBoolean(ARG_ES_REGENERACION, esRegeneracion);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnDialogCloseListener(OnDialogCloseListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
|
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
Window window = dialog.getWindow();
|
||||||
|
if (window != null) {
|
||||||
|
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
window.requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.dialog_plan_actualizado, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
Bundle args = getArguments();
|
||||||
|
if (args == null) return;
|
||||||
|
|
||||||
|
String titulo = args.getString(ARG_TITULO, "¡Plan Actualizado!");
|
||||||
|
String mensaje = args.getString(ARG_MENSAJE, "Tu plan ha sido actualizado con éxito.");
|
||||||
|
boolean esRegeneracion = args.getBoolean(ARG_ES_REGENERACION, true);
|
||||||
|
|
||||||
|
|
||||||
|
ImageView iconoExito = view.findViewById(R.id.iconoExito);
|
||||||
|
TextView tituloText = view.findViewById(R.id.tituloDialog);
|
||||||
|
TextView mensajeText = view.findViewById(R.id.mensajeDialog);
|
||||||
|
Button btnAceptar = view.findViewById(R.id.btnAceptar);
|
||||||
|
|
||||||
|
|
||||||
|
tituloText.setText(titulo);
|
||||||
|
mensajeText.setText(mensaje);
|
||||||
|
|
||||||
|
|
||||||
|
if (esRegeneracion) {
|
||||||
|
btnAceptar.setText("Continuar");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
animarEntrada(view);
|
||||||
|
|
||||||
|
|
||||||
|
btnAceptar.setOnClickListener(v -> cerrarDialog());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
view.setOnClickListener(v -> {
|
||||||
|
if (v.getId() == R.id.backgroundDialog) {
|
||||||
|
cerrarDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animarEntrada(View view) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
View contenedor = view.findViewById(R.id.contenedorDialog);
|
||||||
|
Animation slideIn = AnimationUtils.loadAnimation(getContext(), R.anim.slide_in_bottom);
|
||||||
|
contenedor.startAnimation(slideIn);
|
||||||
|
|
||||||
|
|
||||||
|
ImageView icono = view.findViewById(R.id.iconoExito);
|
||||||
|
Animation scaleIn = AnimationUtils.loadAnimation(getContext(), R.anim.scale_in);
|
||||||
|
icono.postDelayed(() -> icono.startAnimation(scaleIn), 300);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cerrarDialog() {
|
||||||
|
try {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onDialogClosed();
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
dismissAllowingStateLoss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
|
||||||
|
Dialog dialog = getDialog();
|
||||||
|
if (dialog != null && dialog.getWindow() != null) {
|
||||||
|
int width = (int) (getResources().getDisplayMetrics().widthPixels * 0.9);
|
||||||
|
dialog.getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.borjabolufer.elevate.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserFileManager {
|
||||||
|
private static final String TAG = "UserFileManager";
|
||||||
|
|
||||||
|
|
||||||
|
public static String generarNombreArchivo(String email, String tipo) {
|
||||||
|
String emailLimpio = email.replace("@", "_").replace(".", "_");
|
||||||
|
return tipo + "_" + emailLimpio + ".json";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean existeArchivo(Context context, String email, String tipo) {
|
||||||
|
String fileName = generarNombreArchivo(email, tipo);
|
||||||
|
File file = new File(context.getFilesDir(), fileName);
|
||||||
|
return file.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static long obtenerTamanoArchivo(Context context, String email, String tipo) {
|
||||||
|
String fileName = generarNombreArchivo(email, tipo);
|
||||||
|
File file = new File(context.getFilesDir(), fileName);
|
||||||
|
return file.exists() ? file.length() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean eliminarArchivo(Context context, String email, String tipo) {
|
||||||
|
String fileName = generarNombreArchivo(email, tipo);
|
||||||
|
File file = new File(context.getFilesDir(), fileName);
|
||||||
|
|
||||||
|
if (file.exists()) {
|
||||||
|
boolean deleted = file.delete();
|
||||||
|
Log.i(TAG, "Archivo " + fileName + (deleted ? " eliminado" : " NO eliminado"));
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static List<String> listarUsuariosConPlanes(Context context) {
|
||||||
|
List<String> usuarios = new ArrayList<>();
|
||||||
|
File[] files = context.getFilesDir().listFiles();
|
||||||
|
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.getName().startsWith("plan_") && file.getName().endsWith(".json")) {
|
||||||
|
String email = extraerEmailDeNombreArchivo(file.getName());
|
||||||
|
if (email != null) {
|
||||||
|
usuarios.add(email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usuarios;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extraerEmailDeNombreArchivo(String fileName) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String sinExtension = fileName.replace("plan_", "").replace(".json", "");
|
||||||
|
|
||||||
|
int lastUnderscore = sinExtension.lastIndexOf("_");
|
||||||
|
if (lastUnderscore > 0) {
|
||||||
|
String parte1 = sinExtension.substring(0, lastUnderscore);
|
||||||
|
String parte2 = sinExtension.substring(lastUnderscore + 1);
|
||||||
|
return parte1.replace("_", "") + "@" + parte2 + ".com";
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error al extraer email: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<scale
|
||||||
|
android:duration="400"
|
||||||
|
android:fromXScale="0.0"
|
||||||
|
android:fromYScale="0.0"
|
||||||
|
android:toXScale="1.0"
|
||||||
|
android:toYScale="1.0"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%"
|
||||||
|
android:interpolator="@android:anim/bounce_interpolator" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate
|
||||||
|
android:duration="300"
|
||||||
|
android:fromYDelta="100%"
|
||||||
|
android:toYDelta="0%"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator" />
|
||||||
|
<alpha
|
||||||
|
android:duration="300"
|
||||||
|
android:fromAlpha="0.0"
|
||||||
|
android:toAlpha="1.0" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate
|
||||||
|
android:fromXDelta="-100%p"
|
||||||
|
android:toXDelta="0"
|
||||||
|
android:duration="300" />
|
||||||
|
<alpha
|
||||||
|
android:fromAlpha="0.0"
|
||||||
|
android:toAlpha="1.0"
|
||||||
|
android:duration="300" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate
|
||||||
|
android:fromXDelta="100%p"
|
||||||
|
android:toXDelta="0"
|
||||||
|
android:duration="300" />
|
||||||
|
<alpha
|
||||||
|
android:fromAlpha="0.0"
|
||||||
|
android:toAlpha="1.0"
|
||||||
|
android:duration="300" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate
|
||||||
|
android:fromXDelta="0"
|
||||||
|
android:toXDelta="-100%p"
|
||||||
|
android:duration="300" />
|
||||||
|
<alpha
|
||||||
|
android:fromAlpha="1.0"
|
||||||
|
android:toAlpha="0.0"
|
||||||
|
android:duration="300" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate
|
||||||
|
android:fromXDelta="0"
|
||||||
|
android:toXDelta="100%p"
|
||||||
|
android:duration="300" />
|
||||||
|
<alpha
|
||||||
|
android:fromAlpha="1.0"
|
||||||
|
android:toAlpha="0.0"
|
||||||
|
android:duration="300" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="#0D47A1" android:state_checked="false"/> <!-- Azul oscuro cuando no seleccionado -->
|
||||||
|
<item android:color="#4CAF50" android:state_checked="true"/> <!-- Verde cuando seleccionado -->
|
||||||
|
</selector>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:type="linear"
|
||||||
|
android:angle="135"
|
||||||
|
android:startColor="#71FDF0"
|
||||||
|
android:centerColor="#03A9F4"
|
||||||
|
android:endColor="#2196F3"
|
||||||
|
android:useLevel="false" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="#66000000" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</layer-list>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M7,14l5,-5 5,5z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20,3h-1L19,1h-2v2L7,3L7,1L5,1v2L4,3c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,5c0,-1.1 -0.9,-2 -2,-2zM20,21L4,21L4,8h16v13z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M17.5,9.5C17.5,6.46 15.04,4 12,4S6.5,6.46 6.5,9.5c0,2.7 1.94,4.93 4.5,5.4V17H9v2h2v2h2v-2h2v-2h-2v-2.1C15.56,14.43 17.5,12.2 17.5,9.5zM8.5,9.5C8.5,7.57 10.07,6 12,6s3.5,1.57 3.5,3.5S13.93,13 12,13S8.5,11.43 8.5,9.5z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9.5,11c1.93,0 3.5,1.57 3.5,3.5S11.43,18 9.5,18S6,16.43 6,14.5S7.57,11 9.5,11zM9.5,9C6.46,9 4,11.46 4,14.5S6.46,20 9.5,20s5.5,-2.46 5.5,-5.5c0,-1.16 -0.36,-2.23 -0.97,-3.12L18,7.42V10h2V4h-6v2h2.58l-3.97,3.97C11.73,9.36 10.66,9 9.5,9z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1L5.9,18.1L5.9,17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,13c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M16.24,7.76C15.07,6.59 13.54,6 12,6v6l-4.24,4.24c2.34,2.34 6.14,2.34 8.49,0 2.34,-2.34 2.34,-6.14 -0.01,-8.48zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@android:color/white" />
|
||||||
|
<stroke android:width="2dp" android:color="@color/primary" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/primary"
|
||||||
|
android:endColor="@color/primary_dark"
|
||||||
|
android:angle="135" />
|
||||||
|
<size
|
||||||
|
android:width="80dp"
|
||||||
|
android:height="80dp" />
|
||||||
|
</shape>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<solid android:color="@android:color/white" />
|
||||||
|
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#CCCCCC" /> </shape>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<solid android:color="@android:color/white" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#E9ECEF" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:startColor="#EBF4FF"
|
||||||
|
android:endColor="#DBEAFE"
|
||||||
|
android:angle="135" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_light" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="#2C3E50"
|
||||||
|
android:endColor="#4A6741"
|
||||||
|
android:angle="135" />
|
||||||
|
</shape>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnPrimary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M7,10l5,5 5,-5z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnPrimary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
|
||||||
|
</vector>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnPrimary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,3h-1L19,1h-2v2L7,3L7,1L5,1v2L4,3c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,5c0,-1.1 -0.9,-2 -2,-2zM20,21L4,21L4,8h16v13z"/>
|
||||||
|
</vector>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnSurface">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z" />
|
||||||
|
</vector>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue