diff --git a/app/src/main/java.zip b/app/src/main/java.zip index 0546ab5..e99cfcd 100644 Binary files a/app/src/main/java.zip and b/app/src/main/java.zip differ diff --git a/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java b/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java index 644e2d9..26135cb 100644 --- a/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java +++ b/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java @@ -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 loginUser(@Body UserEntity user); + @FormUrlEncoded + @POST("users/forgot-password") + Call forgotPassword(@Field("email") String email); + + @FormUrlEncoded + @POST("users/reset-password") + Call resetPassword( + @Field("email") String email, + @Field("token") String token, + @Field("newPassword") String newPassword + ); + // ---------------- Anime ---------------- @POST("anime/add") Call insertAnime(@Body AnimeEntity anime); diff --git a/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java b/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java index 84aa753..df6dbff 100644 --- a/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java +++ b/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java @@ -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; + } } diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/MainActivity.java b/app/src/main/java/com/santiparra/yomitrack/ui/MainActivity.java index 46a60d8..29d308c 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/MainActivity.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/MainActivity.java @@ -93,8 +93,9 @@ public class MainActivity extends AppCompatActivity { .setTitle("Cerrar sesión") .setMessage("¿Deseas cerrar sesión?") .setPositiveButton("Sí", (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)); }) diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java index bf207dc..ab4e580 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java @@ -76,9 +76,9 @@ public class AddMangaFragment extends Fragment { ArrayAdapter 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 typeAdapter = ArrayAdapter.createFromResource( diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/LoginFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/LoginFragment.java index d957fcc..c98b0e5 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/LoginFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/LoginFragment.java @@ -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,15 +66,20 @@ public class LoginFragment extends Fragment { apiService.loginUser(user).enqueue(new Callback() { @Override - public void onResponse(Call call, Response 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); - showToast("Inicio de sesión exitoso"); - goToMainActivity(); + public void onResponse(@NonNull Call call, @NonNull Response 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("Credenciales incorrectas"); + showToast("Error de autenticación"); } } @@ -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 diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/SplashActivity.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/SplashActivity.java index 5b0eb54..0ffbf34 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/SplashActivity.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/login/SplashActivity.java @@ -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); } } diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/register/RegisterFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/register/RegisterFragment.java index e184e90..1bd87eb 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/register/RegisterFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/register/RegisterFragment.java @@ -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() { @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { + public void onResponse(Call call, Response 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 call, @NonNull Throwable t) { - showToast("Fallo de red: " + t.getMessage()); + public void onFailure(Call call, Throwable t) { + Toast.makeText(getContext(), "Fallo de red: " + t.getMessage(), Toast.LENGTH_SHORT).show(); } }); } diff --git a/app/src/main/res.zip b/app/src/main/res.zip index 1c63593..029812c 100644 Binary files a/app/src/main/res.zip and b/app/src/main/res.zip differ diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index d5e749a..aac4044 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,38 +1,50 @@ - - + android:layout_height="match_parent"> - + android:layout_height="wrap_content"> - + -