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.utils.ActivityLog;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
@ -22,6 +22,8 @@ import java.util.Map;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.POST;
@ -38,6 +40,18 @@ public interface ApiService {
@POST("users/login")
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 ----------------
@POST("anime/add")
Call<ApiResponse> insertAnime(@Body AnimeEntity anime);

View File

@ -15,12 +15,20 @@ public class UserEntity {
@SerializedName("password")
private String password;
@SerializedName("email")
private String email;
public UserEntity(String username, String password) {
this.username = username;
this.password = password;
}
public UserEntity(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
public int getId() {
return id;
}
@ -44,4 +52,12 @@ public class UserEntity {
public void setPassword(String 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")
.setMessage("¿Deseas cerrar sesión?")
.setPositiveButton("", (dialog, which) -> {
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
prefs.edit().remove("current_user_id").apply();
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
prefs.edit().clear().apply(); // ¡esto borra la sesión real!
startActivity(new Intent(MainActivity.this, LoginActivity.class)
.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(
requireContext(),
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);
ArrayAdapter<CharSequence> typeAdapter = ArrayAdapter.createFromResource(

View File

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

View File

@ -5,7 +5,9 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import androidx.appcompat.app.AppCompatActivity;
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.
@ -16,20 +18,21 @@ public class SplashActivity extends AppCompatActivity {
protected void onCreate(Bundle 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(() -> {
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
boolean isLoggedIn = prefs.getBoolean("is_logged_in", false);
SharedPreferences sharedPreferences = getSharedPreferences("user_session", MODE_PRIVATE);
int userId = sharedPreferences.getInt("user_id", -1);
String username = sharedPreferences.getString("username", null);
if (isLoggedIn) {
// Usuario ya ha iniciado sesión previamente
startActivity(new Intent(SplashActivity.this, MainActivity.class));
if (userId != -1 && username != null) {
// Sesión activa MainActivity
startActivity(new Intent(this, MainActivity.class));
} else {
// Ir a login si no ha iniciado sesión
startActivity(new Intent(SplashActivity.this, LoginActivity.class));
// No hay sesión Login
startActivity(new Intent(this, LoginActivity.class));
}
finish();
}, 1500); // 1.5 segundos (puedes ajustar el tiempo)
finish(); // cerrar splash
}, 1500);
}
}

View File

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

Binary file not shown.

View File

@ -1,9 +1,12 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:padding="24dp">
android:padding="24dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/editTextUsername"
@ -35,4 +38,13 @@
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:padding="24dp">
<!-- Usuario -->
<EditText
android:id="@+id/editTextUsernameRegister"
android:hint="Nuevo usuario"
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
android:id="@+id/editTextPasswordRegister"
android:hint="Contraseña"
@ -17,6 +28,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Botón de registro -->
<Button
android:id="@+id/buttonRegister"
android:text="Registrarse"

View File

@ -1,20 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.santiparra.yomitrack.ui.fragments.login.LoginFragment"
android:label="Login">
android:label="Login"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/action_loginFragment_to_registerFragment"
app:destination="@id/registerFragment" />
<action
android:id="@+id/action_loginFragment_to_forgotPasswordFragment"
app:destination="@id/forgotPasswordFragment" />
</fragment>
<fragment
android:id="@+id/registerFragment"
android:name="com.santiparra.yomitrack.ui.fragments.register.RegisterFragment"
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>

View File

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