Reparacion de registrarte con email y recuperacion de contraseña!

This commit is contained in:
santi 2025-05-29 12:18:27 +02:00
parent 5edbf539bd
commit 73109cfbfb
13 changed files with 181 additions and 86 deletions

Binary file not shown.

View File

@ -14,7 +14,7 @@ import com.santiparra.yomitrack.model.RegisterResponse;
import com.santiparra.yomitrack.model.UserStatsResponse; import com.santiparra.yomitrack.model.UserStatsResponse;
import com.santiparra.yomitrack.utils.ActivityLog; import com.santiparra.yomitrack.utils.ActivityLog;
import org.json.JSONObject;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -22,6 +22,8 @@ import java.util.Map;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.HTTP; import retrofit2.http.HTTP;
import retrofit2.http.POST; import retrofit2.http.POST;
@ -38,6 +40,18 @@ public interface ApiService {
@POST("users/login") @POST("users/login")
Call<LoginResponse> loginUser(@Body UserEntity user); Call<LoginResponse> loginUser(@Body UserEntity user);
@FormUrlEncoded
@POST("users/forgot-password")
Call<ApiResponse> forgotPassword(@Field("email") String email);
@FormUrlEncoded
@POST("users/reset-password")
Call<ApiResponse> resetPassword(
@Field("email") String email,
@Field("token") String token,
@Field("newPassword") String newPassword
);
// ---------------- Anime ---------------- // ---------------- Anime ----------------
@POST("anime/add") @POST("anime/add")
Call<ApiResponse> insertAnime(@Body AnimeEntity anime); Call<ApiResponse> insertAnime(@Body AnimeEntity anime);

View File

@ -15,12 +15,20 @@ public class UserEntity {
@SerializedName("password") @SerializedName("password")
private String password; private String password;
@SerializedName("email")
private String email;
public UserEntity(String username, String password) { public UserEntity(String username, String password) {
this.username = username; this.username = username;
this.password = password; this.password = password;
} }
public UserEntity(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
public int getId() { public int getId() {
return id; return id;
} }
@ -44,4 +52,12 @@ public class UserEntity {
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
} }

View File

@ -93,8 +93,9 @@ public class MainActivity extends AppCompatActivity {
.setTitle("Cerrar sesión") .setTitle("Cerrar sesión")
.setMessage("¿Deseas cerrar sesión?") .setMessage("¿Deseas cerrar sesión?")
.setPositiveButton("", (dialog, which) -> { .setPositiveButton("", (dialog, which) -> {
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
prefs.edit().remove("current_user_id").apply(); prefs.edit().clear().apply(); // ¡esto borra la sesión real!
startActivity(new Intent(MainActivity.this, LoginActivity.class) startActivity(new Intent(MainActivity.this, LoginActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)); .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
}) })

View File

@ -76,9 +76,9 @@ public class AddMangaFragment extends Fragment {
ArrayAdapter<CharSequence> statusAdapter = ArrayAdapter.createFromResource( ArrayAdapter<CharSequence> statusAdapter = ArrayAdapter.createFromResource(
requireContext(), requireContext(),
R.array.manga_status_array, R.array.manga_status_array,
R.layout.item_spinner // texto blanco para ítem seleccionado R.layout.item_spinner
); );
statusAdapter.setDropDownViewResource(R.layout.item_spinner); // también para el desplegable statusAdapter.setDropDownViewResource(R.layout.item_spinner);
statusSpinner.setAdapter(statusAdapter); statusSpinner.setAdapter(statusAdapter);
ArrayAdapter<CharSequence> typeAdapter = ArrayAdapter.createFromResource( ArrayAdapter<CharSequence> typeAdapter = ArrayAdapter.createFromResource(

View File

@ -31,23 +31,25 @@ public class LoginFragment extends Fragment {
private FragmentLoginBinding binding; private FragmentLoginBinding binding;
public LoginFragment() { public LoginFragment() {}
// Required empty public constructor
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
binding = FragmentLoginBinding.inflate(inflater, container, false); binding = FragmentLoginBinding.inflate(inflater, container, false);
initListeners();
return binding.getRoot();
}
private void initListeners() {
binding.buttonLogin.setOnClickListener(v -> loginUser()); binding.buttonLogin.setOnClickListener(v -> loginUser());
binding.buttonGuest.setOnClickListener(v -> loginAsGuest()); binding.buttonGuest.setOnClickListener(v -> loginAsGuest());
binding.buttonGoRegister.setOnClickListener(v -> { binding.buttonGoRegister.setOnClickListener(v ->
NavController navController = NavHostFragment.findNavController(LoginFragment.this); navigateTo(R.id.action_loginFragment_to_registerFragment)
navController.navigate(R.id.action_loginFragment_to_registerFragment); );
}); binding.textForgotPassword.setOnClickListener(v ->
navigateTo(R.id.action_loginFragment_to_forgotPasswordFragment)
return binding.getRoot(); );
} }
private void loginUser() { private void loginUser() {
@ -64,15 +66,20 @@ public class LoginFragment extends Fragment {
apiService.loginUser(user).enqueue(new Callback<LoginResponse>() { apiService.loginUser(user).enqueue(new Callback<LoginResponse>() {
@Override @Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) { public void onResponse(@NonNull Call<LoginResponse> call, @NonNull Response<LoginResponse> response) {
if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) { if (response.isSuccessful() && response.body() != null) {
int userId = response.body().getUser().getId(); LoginResponse loginResponse = response.body();
String username = response.body().getUser().getUsername(); if (loginResponse.isSuccess()) {
saveUserSession(userId, username, false); saveUserSession(loginResponse.getUser().getId(), loginResponse.getUser().getUsername());
showToast("Inicio de sesión exitoso"); showToast("Inicio de sesión exitoso");
goToMainActivity(); goToMainActivity();
} else {
showToast("Credenciales incorrectas");
}
} else if (response.code() == 403) {
showToast("Tu correo no ha sido verificado");
} else { } else {
showToast("Credenciales incorrectas"); showToast("Error de autenticación");
} }
} }
@ -84,16 +91,13 @@ public class LoginFragment extends Fragment {
} }
private void loginAsGuest() { private void loginAsGuest() {
saveUserSession(-1, "Invitado", true); saveUserSession(-1, "Invitado");
showToast("Sesión como invitado");
goToMainActivity(); goToMainActivity();
} }
private void saveUserSession(int userId, String username, boolean isGuest) { private void saveUserSession(int userId, String username) {
SharedPreferences prefs = requireActivity().getSharedPreferences("user_session", Context.MODE_PRIVATE); SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("user_session", Context.MODE_PRIVATE);
prefs.edit() sharedPreferences.edit()
.putBoolean("is_logged_in", true)
.putBoolean("guest", isGuest)
.putInt("user_id", userId) .putInt("user_id", userId)
.putString("username", username) .putString("username", username)
.apply(); .apply();
@ -104,8 +108,13 @@ public class LoginFragment extends Fragment {
requireActivity().finish(); requireActivity().finish();
} }
private void navigateTo(int destinationId) {
NavController navController = NavHostFragment.findNavController(this);
navController.navigate(destinationId);
}
private void showToast(String message) { private void showToast(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
} }
@Override @Override

View File

@ -5,7 +5,9 @@ import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.santiparra.yomitrack.ui.MainActivity; import com.santiparra.yomitrack.ui.MainActivity;
import com.santiparra.yomitrack.ui.fragments.login.LoginActivity;
/** /**
* Actividad inicial que decide si ir a LoginActivity o directamente a MainActivity. * Actividad inicial que decide si ir a LoginActivity o directamente a MainActivity.
@ -16,20 +18,21 @@ public class SplashActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// Espera 1.5 segundos antes de navegar // Espera 1.5 segundos antes de decidir a dónde ir
new Handler().postDelayed(() -> { new Handler().postDelayed(() -> {
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE); SharedPreferences sharedPreferences = getSharedPreferences("user_session", MODE_PRIVATE);
boolean isLoggedIn = prefs.getBoolean("is_logged_in", false); int userId = sharedPreferences.getInt("user_id", -1);
String username = sharedPreferences.getString("username", null);
if (isLoggedIn) { if (userId != -1 && username != null) {
// Usuario ya ha iniciado sesión previamente // Sesión activa MainActivity
startActivity(new Intent(SplashActivity.this, MainActivity.class)); startActivity(new Intent(this, MainActivity.class));
} else { } else {
// Ir a login si no ha iniciado sesión // No hay sesión Login
startActivity(new Intent(SplashActivity.this, LoginActivity.class)); startActivity(new Intent(this, LoginActivity.class));
} }
finish(); finish(); // cerrar splash
}, 1500); // 1.5 segundos (puedes ajustar el tiempo) }, 1500);
} }
} }

View File

@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.navigation.fragment.NavHostFragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -15,6 +16,7 @@ import com.santiparra.yomitrack.api.ApiClient;
import com.santiparra.yomitrack.api.ApiService; import com.santiparra.yomitrack.api.ApiService;
import com.santiparra.yomitrack.databinding.FragmentRegisterBinding; import com.santiparra.yomitrack.databinding.FragmentRegisterBinding;
import com.santiparra.yomitrack.db.entities.UserEntity; import com.santiparra.yomitrack.db.entities.UserEntity;
import com.santiparra.yomitrack.model.ApiResponse;
import com.santiparra.yomitrack.model.RegisterResponse; import com.santiparra.yomitrack.model.RegisterResponse;
import retrofit2.Call; import retrofit2.Call;
@ -40,34 +42,31 @@ public class RegisterFragment extends Fragment {
private void registerUser() { private void registerUser() {
String username = binding.editTextUsernameRegister.getText().toString().trim(); String username = binding.editTextUsernameRegister.getText().toString().trim();
String email = binding.editEmail.getText().toString().trim();
String password = binding.editTextPasswordRegister.getText().toString().trim(); String password = binding.editTextPasswordRegister.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) { if (username.isEmpty() || email.isEmpty() || password.isEmpty()) {
showToast("Todos los campos son obligatorios"); Toast.makeText(getContext(), "Completa todos los campos", Toast.LENGTH_SHORT).show();
return; return;
} }
UserEntity user = new UserEntity(username, password); UserEntity user = new UserEntity(username, email, password);
ApiService apiService = ApiClient.getClient().create(ApiService.class); ApiService apiService = ApiClient.getClient().create(ApiService.class);
apiService.registerUser(user).enqueue(new Callback<RegisterResponse>() { apiService.registerUser(user).enqueue(new Callback<RegisterResponse>() {
@Override @Override
public void onResponse(@NonNull Call<RegisterResponse> call, @NonNull Response<RegisterResponse> response) { public void onResponse(Call<RegisterResponse> call, Response<RegisterResponse> response) {
if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) { if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) {
showToast("Registro exitoso"); Toast.makeText(getContext(), "Verifica tu correo para activar la cuenta", Toast.LENGTH_LONG).show();
NavController navController = Navigation.findNavController(binding.getRoot()); NavHostFragment.findNavController(RegisterFragment.this).popBackStack();
navController.popBackStack(); // Volver al LoginFragment
} else { } else {
String errorMsg = (response.body() != null && response.body().getMessage() != null) Toast.makeText(getContext(), "Error al registrar", Toast.LENGTH_SHORT).show();
? response.body().getMessage()
: "Error desconocido al registrar";
showToast(errorMsg);
} }
} }
@Override @Override
public void onFailure(@NonNull Call<RegisterResponse> call, @NonNull Throwable t) { public void onFailure(Call<RegisterResponse> call, Throwable t) {
showToast("Fallo de red: " + t.getMessage()); Toast.makeText(getContext(), "Fallo de red: " + t.getMessage(), Toast.LENGTH_SHORT).show();
} }
}); });
} }

Binary file not shown.

View File

@ -1,38 +1,50 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:orientation="vertical"
android:padding="24dp">
<EditText <LinearLayout
android:id="@+id/editTextUsername" android:orientation="vertical"
android:hint="Usuario" android:padding="24dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content">
<EditText <EditText
android:id="@+id/editTextPassword" android:id="@+id/editTextUsername"
android:hint="Contraseña" android:hint="Usuario"
android:inputType="textPassword" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content" />
android:layout_height="wrap_content" />
<Button <EditText
android:id="@+id/buttonLogin" android:id="@+id/editTextPassword"
android:text="Iniciar Sesión" android:hint="Contraseña"
android:layout_width="match_parent" android:inputType="textPassword"
android:layout_height="wrap_content" /> android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button <Button
android:id="@+id/buttonGuest" android:id="@+id/buttonLogin"
android:text="Entrar como Invitado" android:text="Iniciar Sesión"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<Button <Button
android:id="@+id/buttonGoRegister" android:id="@+id/buttonGuest"
android:text="Registrarse" android:text="Entrar como Invitado"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:id="@+id/buttonGoRegister"
android:text="Registrarse"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textForgotPassword"
android:text="¿Olvidaste tu contraseña?"
android:textColor="@android:color/holo_blue_dark"
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>

View File

@ -4,12 +4,23 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="24dp"> android:padding="24dp">
<!-- Usuario -->
<EditText <EditText
android:id="@+id/editTextUsernameRegister" android:id="@+id/editTextUsernameRegister"
android:hint="Nuevo usuario" android:hint="Nuevo usuario"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:inputType="textPersonName" />
<!-- Correo electrónico -->
<EditText
android:id="@+id/editEmail"
android:hint="Correo electrónico"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress" />
<!-- Contraseña -->
<EditText <EditText
android:id="@+id/editTextPasswordRegister" android:id="@+id/editTextPasswordRegister"
android:hint="Contraseña" android:hint="Contraseña"
@ -17,6 +28,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<!-- Botón de registro -->
<Button <Button
android:id="@+id/buttonRegister" android:id="@+id/buttonRegister"
android:text="Registrarse" android:text="Registrarse"

View File

@ -1,20 +1,48 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android" <navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph" android:id="@+id/nav_graph"
app:startDestination="@id/loginFragment"> app:startDestination="@id/loginFragment">
<fragment <fragment
android:id="@+id/loginFragment" android:id="@+id/loginFragment"
android:name="com.santiparra.yomitrack.ui.fragments.login.LoginFragment" android:name="com.santiparra.yomitrack.ui.fragments.login.LoginFragment"
android:label="Login"> android:label="Login"
tools:layout="@layout/fragment_login">
<action <action
android:id="@+id/action_loginFragment_to_registerFragment" android:id="@+id/action_loginFragment_to_registerFragment"
app:destination="@id/registerFragment" /> app:destination="@id/registerFragment" />
<action
android:id="@+id/action_loginFragment_to_forgotPasswordFragment"
app:destination="@id/forgotPasswordFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/registerFragment" android:id="@+id/registerFragment"
android:name="com.santiparra.yomitrack.ui.fragments.register.RegisterFragment" android:name="com.santiparra.yomitrack.ui.fragments.register.RegisterFragment"
android:label="Register" /> android:label="Register" />
<fragment
android:id="@+id/forgotPasswordFragment"
android:name="com.santiparra.yomitrack.ui.fragments.forgotpassword.ForgotPasswordFragment"
android:label="Olvidé mi contraseña" >
<action
android:id="@+id/action_forgotPasswordFragment_to_resetPasswordFragment"
app:destination="@id/resetPasswordFragment" />
</fragment>
<fragment
android:id="@+id/resetPasswordFragment"
android:name="com.santiparra.yomitrack.ui.fragments.resetpassword.ResetPasswordFragment"
android:label="Restablecer contraseña"
tools:layout="@layout/fragment_reset_password">
<action
android:id="@+id/action_resetPasswordFragment_to_loginFragment"
app:destination="@id/loginFragment" />
</fragment>
</navigation> </navigation>

View File

@ -60,4 +60,5 @@
<item name="btnAdd" type="id" /> <item name="btnAdd" type="id" />
<item name="itemImage" type="id" /> <item name="itemImage" type="id" />
<item name="commentDialogLayout" type="id" /> <item name="commentDialogLayout" type="id" />
<item name="editTextUsernameRegister" type="id" />
</resources> </resources>