Mejora de xml para que se vea mucho mas formal!
This commit is contained in:
parent
2566402280
commit
92fa136769
Binary file not shown.
|
|
@ -7,18 +7,34 @@ import retrofit2.converter.gson.GsonConverterFactory;
|
|||
|
||||
public class ApiClient {
|
||||
|
||||
/**
|
||||
* URL base del servidor donde está alojada la API del backend.
|
||||
* <p>Nota: "10.0.2.2" es una dirección especial utilizada para acceder al localhost
|
||||
* del host desde el emulador de Android.</p>
|
||||
*/
|
||||
private static final String BASE_URL = "http://10.0.2.2:3000/";
|
||||
|
||||
/** Instancia única de Retrofit. */
|
||||
private static Retrofit retrofit = null;
|
||||
|
||||
/**
|
||||
* Devuelve una instancia de Retrofit configurada con la URL base, un cliente HTTP
|
||||
* con un interceptor para loguear las peticiones/respuestas, y un convertidor JSON.
|
||||
*
|
||||
* @return una instancia única de Retrofit lista para usar con las interfaces de servicio.
|
||||
*/
|
||||
public static Retrofit getClient() {
|
||||
if (retrofit == null) {
|
||||
// Interceptor para mostrar logs del cuerpo de la petición y respuesta HTTP
|
||||
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
|
||||
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
|
||||
// Cliente HTTP personalizado con el interceptor de logging
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.addInterceptor(logging)
|
||||
.build();
|
||||
|
||||
// Construcción de la instancia de Retrofit
|
||||
retrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(client)
|
||||
|
|
|
|||
|
|
@ -14,36 +14,58 @@ import com.santiparra.yomitrack.model.RegisterResponse;
|
|||
import com.santiparra.yomitrack.model.UserStatsResponse;
|
||||
import com.santiparra.yomitrack.utils.ActivityLog;
|
||||
|
||||
|
||||
|
||||
import java.util.List;
|
||||
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;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.*;
|
||||
|
||||
/**
|
||||
* Interfaz ApiService que define todos los endpoints disponibles
|
||||
* para la comunicación entre la app YomiTrack y el backend.
|
||||
*
|
||||
* <p>Usa Retrofit para declarar métodos HTTP de forma declarativa.</p>
|
||||
*/
|
||||
public interface ApiService {
|
||||
|
||||
// ---------------- Usuario ----------------
|
||||
// -------------------- USUARIO --------------------
|
||||
|
||||
/**
|
||||
* Registra un nuevo usuario en la aplicación.
|
||||
*
|
||||
* @param user Objeto con los datos del usuario.
|
||||
* @return respuesta del backend con éxito o error.
|
||||
*/
|
||||
@POST("users/register")
|
||||
Call<RegisterResponse> registerUser(@Body UserEntity user);
|
||||
|
||||
/**
|
||||
* Inicia sesión con un usuario existente.
|
||||
*
|
||||
* @param user Objeto con email y contraseña.
|
||||
* @return respuesta con los datos del usuario autenticado.
|
||||
*/
|
||||
@POST("users/login")
|
||||
Call<LoginResponse> loginUser(@Body UserEntity user);
|
||||
|
||||
/**
|
||||
* Solicita un enlace de recuperación de contraseña al correo proporcionado.
|
||||
*
|
||||
* @param email correo electrónico del usuario.
|
||||
* @return respuesta de estado de la operación.
|
||||
*/
|
||||
@FormUrlEncoded
|
||||
@POST("users/forgot-password")
|
||||
Call<ApiResponse> forgotPassword(@Field("email") String email);
|
||||
|
||||
/**
|
||||
* Restablece la contraseña del usuario con un token válido.
|
||||
*
|
||||
* @param email correo electrónico del usuario.
|
||||
* @param token token enviado al correo.
|
||||
* @param newPassword nueva contraseña.
|
||||
* @return respuesta de la operación.
|
||||
*/
|
||||
@FormUrlEncoded
|
||||
@POST("users/reset-password")
|
||||
Call<ApiResponse> resetPassword(
|
||||
|
|
@ -52,11 +74,25 @@ public interface ApiService {
|
|||
@Field("newPassword") String newPassword
|
||||
);
|
||||
|
||||
// ---------------- Anime ----------------
|
||||
// -------------------- ANIME --------------------
|
||||
|
||||
/**
|
||||
* Inserta un nuevo anime en la lista del usuario.
|
||||
*
|
||||
* @param anime Objeto con los datos del anime.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@POST("anime/add")
|
||||
Call<ApiResponse> insertAnime(@Body AnimeEntity anime);
|
||||
|
||||
// Scroll infinito: obtener lista paginada
|
||||
/**
|
||||
* Obtiene la lista paginada de animes de un usuario.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @param page número de página.
|
||||
* @param size cantidad de ítems por página.
|
||||
* @return página con lista de animes.
|
||||
*/
|
||||
@GET("/anime/list/{userId}")
|
||||
Call<AnimePageResponse> getAnimes(
|
||||
@Path("userId") int userId,
|
||||
|
|
@ -64,16 +100,57 @@ public interface ApiService {
|
|||
@Query("size") int size
|
||||
);
|
||||
|
||||
/**
|
||||
* Actualiza un anime existente.
|
||||
*
|
||||
* @param animeId ID del anime.
|
||||
* @param anime Objeto con datos actualizados.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@PUT("anime/{id}")
|
||||
Call<ApiResponse> updateAnime(@Path("id") int animeId, @Body AnimeEntity anime);
|
||||
|
||||
/**
|
||||
* Elimina un anime por su ID.
|
||||
*
|
||||
* @param id ID del anime.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@DELETE("anime/delete/{id}")
|
||||
Call<ApiResponse> deleteAnime(@Path("id") int id);
|
||||
|
||||
// ---------------- Manga ----------------
|
||||
/**
|
||||
* Obtiene animes por estado para un usuario específico.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @param status estado deseado (e.g., Watching, Completed).
|
||||
* @return lista de animes con dicho estado.
|
||||
*/
|
||||
@GET("anime/user/{userId}/status/{status}")
|
||||
Call<List<AnimeEntity>> getAnimeByUserAndStatus(
|
||||
@Path("userId") int userId,
|
||||
@Path("status") String status
|
||||
);
|
||||
|
||||
// -------------------- MANGA --------------------
|
||||
|
||||
/**
|
||||
* Inserta un nuevo manga en la lista del usuario.
|
||||
*
|
||||
* @param manga Objeto con los datos del manga.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@POST("manga/add")
|
||||
Call<ApiResponse> insertManga(@Body MangaEntity manga);
|
||||
|
||||
/**
|
||||
* Obtiene la lista paginada de mangas de un usuario.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @param page número de página.
|
||||
* @param size cantidad de ítems por página.
|
||||
* @return página con lista de mangas.
|
||||
*/
|
||||
@GET("/manga/list/{userId}")
|
||||
Call<MangaPageResponse> getMangas(
|
||||
@Path("userId") int userId,
|
||||
|
|
@ -81,57 +158,125 @@ public interface ApiService {
|
|||
@Query("size") int size
|
||||
);
|
||||
|
||||
/**
|
||||
* Obtiene mangas por estado para un usuario específico.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @param status estado deseado (e.g., Reading, Completed).
|
||||
* @return lista de mangas con dicho estado.
|
||||
*/
|
||||
@GET("manga/user/{userId}/status/{status}")
|
||||
Call<List<MangaEntity>> getMangaByUserAndStatus(
|
||||
@Path("userId") int userId,
|
||||
@Path("status") String status
|
||||
);
|
||||
|
||||
/**
|
||||
* Actualiza un manga existente.
|
||||
*
|
||||
* @param mangaId ID del manga.
|
||||
* @param manga Objeto con datos actualizados.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@PUT("manga/{id}")
|
||||
Call<ApiResponse> updateManga(@Path("id") int mangaId, @Body MangaEntity manga);
|
||||
|
||||
/**
|
||||
* Elimina un manga por su ID.
|
||||
*
|
||||
* @param id ID del manga.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@DELETE("manga/delete/{id}")
|
||||
Call<ApiResponse> deleteManga(@Path("id") int id);
|
||||
|
||||
// ---------------- Activity -------------------
|
||||
// -------------------- ACTIVIDAD --------------------
|
||||
|
||||
/**
|
||||
* Obtiene estadísticas de anime y manga del usuario.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @return objeto con estadísticas.
|
||||
*/
|
||||
@GET("users/{id}/stats")
|
||||
Call<UserStatsResponse> getUserStats(@Path("id") int userId);
|
||||
|
||||
/**
|
||||
* Obtiene el historial de actividad de un usuario.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @return lista de actividades.
|
||||
*/
|
||||
@GET("api/activity/list/{userId}")
|
||||
Call<List<ActivityLog>> getActivityLog(@Path("userId") int userId);
|
||||
|
||||
/**
|
||||
* Obtiene los comentarios de una actividad específica.
|
||||
*
|
||||
* @param activityId ID de la actividad.
|
||||
* @return lista de comentarios.
|
||||
*/
|
||||
@GET("api/activity/comments/{activityId}")
|
||||
Call<List<CommentModel>> getCommentsByActivity(@Path("activityId") int activityId);
|
||||
|
||||
/**
|
||||
* Verifica si un usuario dio like a una actividad.
|
||||
*
|
||||
* @param userId ID del usuario.
|
||||
* @param activityId ID de la actividad.
|
||||
* @return objeto JSON con resultado (true/false).
|
||||
*/
|
||||
@GET("/api/activity/like/{userId}/{activityId}")
|
||||
Call<JsonObject> checkLike(
|
||||
@Path("userId") int userId,
|
||||
@Path("activityId") int activityId
|
||||
);
|
||||
|
||||
@GET("anime/user/{userId}/status/{status}")
|
||||
Call<List<AnimeEntity>> getAnimeByUserAndStatus(
|
||||
@Path("userId") int userId,
|
||||
@Path("status") String status
|
||||
);
|
||||
|
||||
/**
|
||||
* Envía un like a una actividad.
|
||||
*
|
||||
* @param body JSON con userId y activityId.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@POST("api/activity/like")
|
||||
Call<JsonObject> postLike(@Body JsonObject body);
|
||||
|
||||
@POST("api/activity/comment")
|
||||
Call<JsonObject> postComment(@Body JsonObject body);
|
||||
|
||||
@POST("api/activity/add")
|
||||
Call<JsonObject> postActivity(@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* Elimina un like de una actividad.
|
||||
*
|
||||
* @param body JSON con userId y activityId.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@HTTP(method = "DELETE", path = "api/activity/like/remove", hasBody = true)
|
||||
Call<JsonObject> deleteLike(@Body JsonObject body);
|
||||
|
||||
/**
|
||||
* Publica un comentario en una actividad.
|
||||
*
|
||||
* @param body JSON con userId, activityId, texto y otros datos.
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@POST("api/activity/comment")
|
||||
Call<JsonObject> postComment(@Body JsonObject body);
|
||||
|
||||
// ---------------- AniList API ----------------
|
||||
/**
|
||||
* Publica una nueva actividad.
|
||||
*
|
||||
* @param body mapa con los datos de la actividad (userId, tipo, contenido...).
|
||||
* @return respuesta del backend.
|
||||
*/
|
||||
@POST("api/activity/add")
|
||||
Call<JsonObject> postActivity(@Body Map<String, Object> body);
|
||||
|
||||
// -------------------- ANIList API --------------------
|
||||
|
||||
/**
|
||||
* Busca animes o mangas en AniList.
|
||||
*
|
||||
* @param query término de búsqueda.
|
||||
* @param type tipo de media: "ANIME" o "MANGA".
|
||||
* @return lista de resultados obtenidos desde AniList.
|
||||
*/
|
||||
@GET("anilist/search")
|
||||
Call<List<AniListMedia>> searchAniList(@Query("query") String query, @Query("type") String type);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,22 +4,55 @@ import java.io.Serializable;
|
|||
|
||||
/**
|
||||
* Entidad que representa un anime guardado por el usuario en la base de datos local.
|
||||
* Implementa Serializable para facilitar el paso entre componentes.
|
||||
*/
|
||||
public class AnimeEntity implements Serializable {
|
||||
|
||||
/** ID único del anime en la base de datos. */
|
||||
private int id;
|
||||
|
||||
/** ID del usuario al que pertenece este anime. */
|
||||
private int userId;
|
||||
|
||||
/** Título del anime. */
|
||||
private String title;
|
||||
|
||||
/** Puntuación asignada por el usuario (por ejemplo, del 1 al 10). */
|
||||
private int score;
|
||||
|
||||
/** Progreso actual del usuario (número de episodios vistos). */
|
||||
private int progress;
|
||||
|
||||
/** Estado del anime (Watching, Completed, Paused, etc.). */
|
||||
private String status;
|
||||
|
||||
/** Tipo del anime (TV, Movie, OVA, etc.). */
|
||||
private String type;
|
||||
|
||||
/** URL de la imagen de portada del anime. */
|
||||
private String imageUrl;
|
||||
|
||||
/** Número total de episodios del anime. */
|
||||
private int totalEpisodes;
|
||||
|
||||
/**
|
||||
* Constructor vacío requerido por algunas librerías como Room o Retrofit.
|
||||
*/
|
||||
public AnimeEntity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor completo de la entidad Anime.
|
||||
*
|
||||
* @param id ID único del anime.
|
||||
* @param title Título del anime.
|
||||
* @param status Estado del anime.
|
||||
* @param userId ID del usuario propietario.
|
||||
* @param imageUrl URL de la imagen del anime.
|
||||
* @param progress Episodios vistos.
|
||||
* @param score Puntuación asignada.
|
||||
* @param totalEpisodes Total de episodios del anime.
|
||||
*/
|
||||
public AnimeEntity(int id, String title, String status, int userId, String imageUrl, int progress, int score, int totalEpisodes) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
|
|
@ -31,75 +64,92 @@ public class AnimeEntity implements Serializable {
|
|||
this.totalEpisodes = totalEpisodes;
|
||||
}
|
||||
|
||||
// Getters y Setters
|
||||
/** @return ID del anime. */
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @param id ID del anime. */
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** @return ID del usuario. */
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/** @param userId ID del usuario. */
|
||||
public void setUserId(int userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
/** @return Título del anime. */
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/** @param title Título del anime. */
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/** @return Puntuación del anime. */
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/** @param score Puntuación del anime. */
|
||||
public void setScore(int score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
/** @return Progreso del usuario (episodios vistos). */
|
||||
public int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/** @param progress Episodios vistos por el usuario. */
|
||||
public void setProgress(int progress) {
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
/** @return Estado del anime. */
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/** @param status Estado del anime. */
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/** @return Tipo de anime (TV, Movie, etc.). */
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/** @param type Tipo de anime. */
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/** @return URL de la imagen del anime. */
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
/** @param imageUrl URL de la imagen del anime. */
|
||||
public void setImageUrl(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
/** @return Total de episodios del anime. */
|
||||
public int getTotalEpisodes() {
|
||||
return totalEpisodes;
|
||||
}
|
||||
|
||||
/** @param totalEpisodes Número total de episodios. */
|
||||
public void setTotalEpisodes(int totalEpisodes) {
|
||||
this.totalEpisodes = totalEpisodes;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,22 +4,55 @@ import java.io.Serializable;
|
|||
|
||||
/**
|
||||
* Entidad que representa un manga guardado por el usuario en la base de datos local.
|
||||
* Implementa Serializable para permitir su paso entre actividades y fragmentos.
|
||||
*/
|
||||
public class MangaEntity implements Serializable {
|
||||
|
||||
/** ID único del manga en la base de datos. */
|
||||
private int id;
|
||||
|
||||
/** ID del usuario al que pertenece este manga. */
|
||||
private int userId;
|
||||
|
||||
/** Título del manga. */
|
||||
private String title;
|
||||
|
||||
/** Puntuación asignada por el usuario (por ejemplo, del 1 al 10). */
|
||||
private int score;
|
||||
|
||||
/** Progreso actual del usuario (capítulos leídos). */
|
||||
private int progress;
|
||||
|
||||
/** Estado del manga (Reading, Completed, On-Hold, etc.). */
|
||||
private String status;
|
||||
|
||||
/** Tipo del manga (Manga, Manhwa, Doujinshi, etc.). */
|
||||
private String type;
|
||||
|
||||
/** URL de la imagen de portada del manga. */
|
||||
private String imageUrl;
|
||||
|
||||
/** Número total de capítulos del manga. */
|
||||
private int totalChapters;
|
||||
|
||||
/**
|
||||
* Constructor vacío necesario para serialización y frameworks como Room o Retrofit.
|
||||
*/
|
||||
public MangaEntity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor completo para inicializar una instancia de manga.
|
||||
*
|
||||
* @param id ID único del manga.
|
||||
* @param title Título del manga.
|
||||
* @param status Estado actual del manga.
|
||||
* @param userId ID del usuario que lo añadió.
|
||||
* @param imageUrl URL de la portada del manga.
|
||||
* @param progress Capítulos leídos.
|
||||
* @param score Puntuación dada por el usuario.
|
||||
* @param totalChapters Total de capítulos disponibles.
|
||||
*/
|
||||
public MangaEntity(int id, String title, String status, int userId, String imageUrl, int progress, int score, int totalChapters) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
|
|
@ -31,76 +64,92 @@ public class MangaEntity implements Serializable {
|
|||
this.totalChapters = totalChapters;
|
||||
}
|
||||
|
||||
// Getters y Setters
|
||||
|
||||
/** @return ID del manga. */
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @param id ID del manga. */
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** @return ID del usuario propietario del manga. */
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/** @param userId ID del usuario propietario. */
|
||||
public void setUserId(int userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
/** @return Título del manga. */
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/** @param title Título del manga. */
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/** @return Puntuación asignada al manga. */
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/** @param score Puntuación del manga. */
|
||||
public void setScore(int score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
/** @return Capítulos leídos por el usuario. */
|
||||
public int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/** @param progress Capítulos que el usuario ha leído. */
|
||||
public void setProgress(int progress) {
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
/** @return Estado del manga (Reading, Dropped, etc.). */
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/** @param status Estado actual del manga. */
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/** @return Tipo de manga (e.g., Manhwa, Doujinshi). */
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/** @param type Tipo de manga. */
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/** @return URL de la imagen del manga. */
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
/** @param imageUrl URL de la imagen de portada. */
|
||||
public void setImageUrl(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
/** @return Total de capítulos del manga. */
|
||||
public int getTotalChapters() {
|
||||
return totalChapters;
|
||||
}
|
||||
|
||||
/** @param totalChapters Cantidad total de capítulos. */
|
||||
public void setTotalChapters(int totalChapters) {
|
||||
this.totalChapters = totalChapters;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,60 +3,87 @@ package com.santiparra.yomitrack.db.entities;
|
|||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Entidad que representa un usuario.
|
||||
* Entidad que representa un usuario dentro del sistema.
|
||||
* Utilizada tanto para autenticación como para registro y obtención de datos del backend.
|
||||
*/
|
||||
public class UserEntity {
|
||||
|
||||
/** Identificador único del usuario. */
|
||||
@SerializedName("id")
|
||||
private int id;
|
||||
|
||||
/** Nombre de usuario utilizado para iniciar sesión y mostrar en el perfil. */
|
||||
@SerializedName("username")
|
||||
private String username;
|
||||
|
||||
/** Contraseña del usuario (debe manejarse con cuidado y encriptación en producción). */
|
||||
@SerializedName("password")
|
||||
private String password;
|
||||
|
||||
/** Dirección de correo electrónico del usuario. */
|
||||
@SerializedName("email")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* Constructor utilizado para iniciar sesión con username y contraseña.
|
||||
*
|
||||
* @param username nombre de usuario.
|
||||
* @param password contraseña del usuario.
|
||||
*/
|
||||
public UserEntity(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor utilizado para registrar un nuevo usuario.
|
||||
*
|
||||
* @param username nombre de usuario.
|
||||
* @param email correo electrónico.
|
||||
* @param password contraseña del usuario.
|
||||
*/
|
||||
public UserEntity(String username, String email, String password) {
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/** @return ID del usuario. */
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @param id ID del usuario. */
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** @return nombre de usuario. */
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/** @param username nombre de usuario. */
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/** @return contraseña del usuario. */
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/** @param password contraseña del usuario. */
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/** @return correo electrónico del usuario. */
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
/** @param email correo electrónico del usuario. */
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,75 @@
|
|||
package com.santiparra.yomitrack.model;
|
||||
|
||||
/**
|
||||
* Modelo de datos que representa un resultado de búsqueda desde la API de AniList.
|
||||
* Utilizado tanto para anime como manga.
|
||||
*/
|
||||
public class AniListMedia {
|
||||
|
||||
/** ID único del media (anime o manga) proporcionado por AniList. */
|
||||
private int id;
|
||||
|
||||
/** Título del anime o manga. */
|
||||
private String title;
|
||||
|
||||
/** URL de la imagen de portada del anime o manga. */
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* Constructor vacío necesario para serialización/deserialización automática.
|
||||
*/
|
||||
public AniListMedia() {}
|
||||
|
||||
/**
|
||||
* Devuelve el ID del media.
|
||||
*
|
||||
* @return ID del anime o manga.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece el ID del media.
|
||||
*
|
||||
* @param id ID del anime o manga.
|
||||
*/
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve el título del media.
|
||||
*
|
||||
* @return título del anime o manga.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece el título del media.
|
||||
*
|
||||
* @param title título del anime o manga.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la URL de la imagen de portada.
|
||||
*
|
||||
* @return URL de la imagen.
|
||||
*/
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece la URL de la imagen de portada.
|
||||
*
|
||||
* @param imageUrl URL de la imagen.
|
||||
*/
|
||||
public void setImageUrl(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package com.santiparra.yomitrack.model;
|
||||
|
||||
public class ApiResponse {
|
||||
private String message;
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.santiparra.yomitrack.model.adapters.airing;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.santiparra.yomitrack.R;
|
||||
import com.santiparra.yomitrack.model.ItemModel;
|
||||
|
||||
public class AiringViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public ImageView imageView;
|
||||
public TextView titleTextView;
|
||||
public TextView progressTextView;
|
||||
|
||||
public AiringViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
imageView = itemView.findViewById(R.id.mediaImage);
|
||||
titleTextView = itemView.findViewById(R.id.titleTextView);
|
||||
progressTextView = itemView.findViewById(R.id.progressTextView);
|
||||
}
|
||||
|
||||
public void bind(ItemModel item) {
|
||||
titleTextView.setText(item.getTitle());
|
||||
progressTextView.setText("Progress: " + item.getProgress());
|
||||
|
||||
if (item.getImageUrl() != null && !item.getImageUrl().isEmpty()) {
|
||||
Glide.with(itemView.getContext())
|
||||
.load(item.getImageUrl())
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.error(R.drawable.error_image)
|
||||
.into(imageView);
|
||||
} else {
|
||||
imageView.setImageResource(R.drawable.placeholder_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.santiparra.yomitrack.model.adapters.airing;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.santiparra.yomitrack.R;
|
||||
import com.santiparra.yomitrack.model.ItemModel;
|
||||
|
||||
public class AnimeViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public ImageView imageView;
|
||||
public TextView titleTextView;
|
||||
public TextView progressTextView;
|
||||
|
||||
public AnimeViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
imageView = itemView.findViewById(R.id.mediaImage);
|
||||
titleTextView = itemView.findViewById(R.id.titleTextView);
|
||||
progressTextView = itemView.findViewById(R.id.progressTextView);
|
||||
}
|
||||
|
||||
public void bind(ItemModel item) {
|
||||
titleTextView.setText(item.getTitle());
|
||||
progressTextView.setText("Progress: " + item.getProgress());
|
||||
|
||||
if (item.getImageUrl() != null && !item.getImageUrl().isEmpty()) {
|
||||
Glide.with(itemView.getContext())
|
||||
.load(item.getImageUrl())
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.error(R.drawable.error_image)
|
||||
.into(imageView);
|
||||
} else {
|
||||
imageView.setImageResource(R.drawable.placeholder_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import com.santiparra.yomitrack.api.ApiService;
|
|||
import com.santiparra.yomitrack.db.entities.AnimeEntity;
|
||||
import com.santiparra.yomitrack.db.entities.MangaEntity;
|
||||
import com.santiparra.yomitrack.model.AniListMedia;
|
||||
import com.santiparra.yomitrack.utils.ActivityLog;
|
||||
import com.santiparra.yomitrack.utils.DateUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
@ -31,18 +30,36 @@ import retrofit2.Call;
|
|||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Adaptador para mostrar resultados de búsqueda provenientes de AniList
|
||||
* y permitir al usuario añadir animes o mangas a su lista local mediante la API.
|
||||
*/
|
||||
public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdapter.ViewHolder> {
|
||||
|
||||
private final List<AniListMedia> mediaList;
|
||||
private final Context context;
|
||||
private final String mediaType;
|
||||
|
||||
/**
|
||||
* Constructor del adaptador.
|
||||
*
|
||||
* @param context contexto de la aplicación.
|
||||
* @param mediaList lista de resultados de búsqueda de AniList.
|
||||
* @param mediaType tipo de media ("ANIME" o "MANGA").
|
||||
*/
|
||||
public AniListSearchAdapter(Context context, List<AniListMedia> mediaList, String mediaType) {
|
||||
this.context = context;
|
||||
this.mediaList = mediaList;
|
||||
this.mediaType = mediaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infla el layout de cada ítem de la lista.
|
||||
*
|
||||
* @param parent el ViewGroup padre.
|
||||
* @param viewType el tipo de vista.
|
||||
* @return ViewHolder que contiene la vista del ítem.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
|
@ -50,6 +67,12 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asigna los datos de un ítem a su vista correspondiente.
|
||||
*
|
||||
* @param holder ViewHolder que contiene la vista.
|
||||
* @param position posición del ítem en la lista.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
AniListMedia item = mediaList.get(position);
|
||||
|
|
@ -69,6 +92,7 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
ApiService apiService = ApiClient.getClient().create(ApiService.class);
|
||||
|
||||
if (mediaType.equals("ANIME")) {
|
||||
// Crear objeto AnimeEntity
|
||||
AnimeEntity anime = new AnimeEntity(
|
||||
item.getId(),
|
||||
item.getTitle(),
|
||||
|
|
@ -81,12 +105,13 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
);
|
||||
anime.setType("TV");
|
||||
|
||||
// Llamada para insertar anime
|
||||
apiService.insertAnime(anime).enqueue(new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
Toast.makeText(context, "Anime añadido", Toast.LENGTH_SHORT).show();
|
||||
|
||||
// 🔁 Registrar actividad usando Map
|
||||
// Registrar actividad
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("userId", userId);
|
||||
body.put("action", "añadió");
|
||||
|
|
@ -96,7 +121,9 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
|
||||
apiService.postActivity(body).enqueue(new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {}
|
||||
public void onResponse(Call call, Response response) {
|
||||
// Actividad registrada (sin acción adicional)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call call, Throwable t) {
|
||||
|
|
@ -112,6 +139,7 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
});
|
||||
|
||||
} else {
|
||||
// Crear objeto MangaEntity
|
||||
MangaEntity manga = new MangaEntity(
|
||||
item.getId(),
|
||||
item.getTitle(),
|
||||
|
|
@ -124,6 +152,7 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
);
|
||||
manga.setType("Manga");
|
||||
|
||||
// Llamada para insertar manga
|
||||
apiService.insertManga(manga).enqueue(new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
|
|
@ -156,16 +185,35 @@ public class AniListSearchAdapter extends RecyclerView.Adapter<AniListSearchAdap
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cantidad de elementos en la lista.
|
||||
*
|
||||
* @return número total de ítems en la búsqueda.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mediaList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clase interna que representa un ítem individual del RecyclerView.
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
/** Título del anime/manga. */
|
||||
TextView title;
|
||||
|
||||
/** Imagen de portada del anime/manga. */
|
||||
ImageView cover;
|
||||
|
||||
/** Botón para añadir el ítem a la lista del usuario. */
|
||||
Button btnAdd;
|
||||
|
||||
/**
|
||||
* Constructor del ViewHolder.
|
||||
*
|
||||
* @param itemView vista del ítem individual.
|
||||
*/
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
title = itemView.findViewById(R.id.itemTitle);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.santiparra.yomitrack.model.adapters.anime_adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -18,17 +17,41 @@ import com.santiparra.yomitrack.db.entities.AnimeEntity;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adaptador de RecyclerView para mostrar una lista de animes con soporte para múltiples tipos de vista:
|
||||
* normal, compacta y grande. Permite manejar clics normales y prolongados sobre los ítems.
|
||||
*/
|
||||
public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHolder> {
|
||||
|
||||
/** Vista normal por defecto. */
|
||||
public static final int VIEW_NORMAL = 0;
|
||||
|
||||
/** Vista compacta. */
|
||||
public static final int VIEW_COMPACT = 1;
|
||||
|
||||
/** Vista ampliada. */
|
||||
public static final int VIEW_LARGE = 2;
|
||||
|
||||
/** Lista de animes a mostrar. */
|
||||
private List<AnimeEntity> animeList;
|
||||
|
||||
/** Tipo de vista actual. */
|
||||
private int viewType;
|
||||
|
||||
/** Listener para clics normales (edición). */
|
||||
private final OnAnimeClickListener onEditClick;
|
||||
|
||||
/** Listener para clics prolongados (acciones extendidas). */
|
||||
private final OnAnimeClickListener onLongClick;
|
||||
|
||||
/**
|
||||
* Constructor del adaptador.
|
||||
*
|
||||
* @param animeList lista de animes.
|
||||
* @param viewType tipo de vista a usar (normal, compacta, grande).
|
||||
* @param onEditClick callback para clics normales.
|
||||
* @param onLongClick callback para clics prolongados.
|
||||
*/
|
||||
public AnimeAdapter(List<AnimeEntity> animeList, int viewType,
|
||||
OnAnimeClickListener onEditClick,
|
||||
OnAnimeClickListener onLongClick) {
|
||||
|
|
@ -38,13 +61,23 @@ public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHol
|
|||
this.onLongClick = onLongClick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cambia el tipo de vista del adaptador.
|
||||
*
|
||||
* @param viewType nuevo tipo de vista.
|
||||
*/
|
||||
public void setViewType(int viewType) {
|
||||
this.viewType = viewType;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiza la lista de animes mostrada.
|
||||
*
|
||||
* @param newList nueva lista de animes.
|
||||
*/
|
||||
public void updateList(List<AnimeEntity> newList) {
|
||||
this.animeList = newList != null ? newList : new ArrayList<>();
|
||||
this.animeList = new ArrayList<>(newList); // o .clear() + .addAll()
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
|
@ -145,11 +178,37 @@ public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHol
|
|||
return viewType;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder que representa un ítem individual del RecyclerView de animes.
|
||||
*/
|
||||
public static class AnimeViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
/** Imagen de portada del anime. */
|
||||
ImageView imageCover;
|
||||
TextView textTitle, textStatus, textProgress, textScore, textType;
|
||||
|
||||
/** Título del anime. */
|
||||
TextView textTitle;
|
||||
|
||||
/** Texto que muestra el estado y tipo del anime. */
|
||||
TextView textStatus;
|
||||
|
||||
/** Texto que muestra el progreso (episodios vistos). */
|
||||
TextView textProgress;
|
||||
|
||||
/** Texto que muestra la puntuación. */
|
||||
TextView textScore;
|
||||
|
||||
/** Texto que muestra el tipo de anime. */
|
||||
TextView textType;
|
||||
|
||||
/** Punto de color que indica el estado visualmente. */
|
||||
View statusDot;
|
||||
|
||||
/**
|
||||
* Constructor del ViewHolder.
|
||||
*
|
||||
* @param itemView vista inflada del ítem.
|
||||
*/
|
||||
public AnimeViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
imageCover = itemView.findViewById(R.id.imageCover);
|
||||
|
|
@ -162,7 +221,15 @@ public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHol
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interfaz para manejar clics sobre un anime.
|
||||
*/
|
||||
public interface OnAnimeClickListener {
|
||||
/**
|
||||
* Método invocado al hacer clic en un ítem de anime.
|
||||
*
|
||||
* @param anime objeto de anime clicado.
|
||||
*/
|
||||
void onClick(AnimeEntity anime);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
package com.santiparra.yomitrack.model.adapters.browser_section_adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.santiparra.yomitrack.R;
|
||||
import com.santiparra.yomitrack.model.ItemModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BrowseGridAdapter extends RecyclerView.Adapter<BrowseGridAdapter.ViewHolder> {
|
||||
|
||||
private final List<ItemModel> items;
|
||||
|
||||
public BrowseGridAdapter(List<ItemModel> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_browse_card, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ItemModel item = items.get(position);
|
||||
holder.title.setText(item.getTitle());
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.load(item.getImageUrl())
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(holder.cover);
|
||||
|
||||
// 👉 Animación
|
||||
Animation animation = AnimationUtils.loadAnimation(holder.itemView.getContext(), R.anim.item_animation_fade_scale);
|
||||
holder.itemView.startAnimation(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView cover;
|
||||
TextView title;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
cover = itemView.findViewById(R.id.imageViewCover);
|
||||
title = itemView.findViewById(R.id.textViewTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.santiparra.yomitrack.model.adapters.browser_section_adapter;
|
||||
|
||||
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.santiparra.yomitrack.R;
|
||||
import com.santiparra.yomitrack.model.BrowseSection;
|
||||
import com.santiparra.yomitrack.model.adapters.homeadapter.HomeAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BrowseSectionAdapter extends RecyclerView.Adapter<BrowseSectionAdapter.BrowseViewHolder> {
|
||||
|
||||
private final List<BrowseSection> sectionList;
|
||||
|
||||
public BrowseSectionAdapter(List<BrowseSection> sectionList) {
|
||||
this.sectionList = sectionList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BrowseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_section, parent, false);
|
||||
return new BrowseViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull BrowseViewHolder holder, int position) {
|
||||
BrowseSection section = sectionList.get(position);
|
||||
holder.sectionTitle.setText(section.getTitle());
|
||||
|
||||
HomeAdapter adapter = new HomeAdapter(section.getItems(), section.getTitle());
|
||||
holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
holder.recyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return sectionList.size();
|
||||
}
|
||||
|
||||
static class BrowseViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView sectionTitle;
|
||||
RecyclerView recyclerView;
|
||||
|
||||
public BrowseViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
sectionTitle = itemView.findViewById(R.id.sectionTitle);
|
||||
recyclerView = itemView.findViewById(R.id.sectionRecyclerView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package com.santiparra.yomitrack.model.adapters.homeadapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.santiparra.yomitrack.R;
|
||||
import com.santiparra.yomitrack.model.ItemModel;
|
||||
import com.santiparra.yomitrack.model.adapters.airing.AiringViewHolder;
|
||||
import com.santiparra.yomitrack.model.adapters.airing.AnimeViewHolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private List<ItemModel> itemList;
|
||||
private String sectionTitle;
|
||||
|
||||
private static final int TYPE_AIRING = 0;
|
||||
private static final int TYPE_ANIME_MANGA = 1;
|
||||
|
||||
public HomeAdapter(List<ItemModel> itemList, String sectionTitle) {
|
||||
this.itemList = itemList;
|
||||
this.sectionTitle = sectionTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (sectionTitle.equalsIgnoreCase("Airing")) {
|
||||
return TYPE_AIRING;
|
||||
} else {
|
||||
return TYPE_ANIME_MANGA;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_media_card, parent, false);
|
||||
|
||||
// Ajustamos manualmente el ancho
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
view.setLayoutParams(layoutParams);
|
||||
|
||||
if (viewType == TYPE_AIRING) {
|
||||
return new AiringViewHolder(view);
|
||||
} else {
|
||||
return new AnimeViewHolder(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
ItemModel item = itemList.get(position);
|
||||
|
||||
if (holder instanceof AiringViewHolder) {
|
||||
((AiringViewHolder) holder).bind(item);
|
||||
} else if (holder instanceof AnimeViewHolder) {
|
||||
((AnimeViewHolder) holder).bind(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemList.size();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,20 +17,48 @@ import com.santiparra.yomitrack.model.ItemModel;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adaptador para el RecyclerView del fragmento de inicio (Home).
|
||||
* Muestra tarjetas con imagen, título y progreso de animes o mangas recientes.
|
||||
*/
|
||||
public class HomeCardAdapter extends RecyclerView.Adapter<HomeCardAdapter.ViewHolder> {
|
||||
|
||||
/**
|
||||
* Interfaz para manejar clics en los ítems del RecyclerView.
|
||||
*/
|
||||
public interface OnItemClickListener {
|
||||
/**
|
||||
* Se llama cuando el usuario hace clic sobre un ítem.
|
||||
*
|
||||
* @param item el ítem seleccionado.
|
||||
*/
|
||||
void onItemClick(ItemModel item);
|
||||
}
|
||||
|
||||
/** Lista de ítems a mostrar (animes o mangas). */
|
||||
private final List<ItemModel> itemList;
|
||||
|
||||
/** Listener que maneja clics en los ítems. */
|
||||
private final OnItemClickListener listener;
|
||||
|
||||
/**
|
||||
* Constructor del adaptador.
|
||||
*
|
||||
* @param itemList lista de ítems a mostrar.
|
||||
* @param listener manejador de eventos de clic.
|
||||
*/
|
||||
public HomeCardAdapter(List<ItemModel> itemList, OnItemClickListener listener) {
|
||||
this.itemList = itemList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infla la vista para un ítem del RecyclerView.
|
||||
*
|
||||
* @param parent vista contenedora.
|
||||
* @param viewType tipo de vista (no usado aquí).
|
||||
* @return instancia de ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
|
@ -38,26 +66,58 @@ public class HomeCardAdapter extends RecyclerView.Adapter<HomeCardAdapter.ViewHo
|
|||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asocia los datos del ítem con su vista.
|
||||
*
|
||||
* @param holder ViewHolder con las vistas del ítem.
|
||||
* @param position posición del ítem en la lista.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ItemModel item = itemList.get(position);
|
||||
holder.title.setText(item.getTitle());
|
||||
holder.progress.setText(item.getProgress());
|
||||
Glide.with(holder.itemView.getContext()).load(item.getImageUrl()).into(holder.cover);
|
||||
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.load(item.getImageUrl())
|
||||
.placeholder(R.drawable.rectangle_placeholder)
|
||||
.into(holder.cover);
|
||||
|
||||
holder.card.setOnClickListener(v -> listener.onItemClick(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cantidad de ítems en la lista.
|
||||
*
|
||||
* @return número total de ítems.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder que representa cada tarjeta (ítem) en el RecyclerView.
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView title, progress;
|
||||
|
||||
/** Texto que muestra el título del ítem. */
|
||||
TextView title;
|
||||
|
||||
/** Texto que muestra el progreso del ítem. */
|
||||
TextView progress;
|
||||
|
||||
/** Imagen de portada del ítem. */
|
||||
ImageView cover;
|
||||
|
||||
/** Tarjeta contenedora del ítem. */
|
||||
CardView card;
|
||||
|
||||
/**
|
||||
* Constructor del ViewHolder.
|
||||
*
|
||||
* @param itemView vista del ítem inflada desde el layout.
|
||||
*/
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
title = itemView.findViewById(R.id.itemTitle);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.santiparra.yomitrack.model.adapters.manga_adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -17,17 +16,41 @@ import com.santiparra.yomitrack.db.entities.MangaEntity;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adaptador para mostrar una lista de mangas en un RecyclerView con distintos tipos de vista:
|
||||
* normal, compacta y grande. Soporta clics normales y prolongados.
|
||||
*/
|
||||
public class MangaAdapter extends RecyclerView.Adapter<MangaAdapter.MangaViewHolder> {
|
||||
|
||||
/** Vista estándar. */
|
||||
public static final int VIEW_NORMAL = 0;
|
||||
|
||||
/** Vista compacta. */
|
||||
public static final int VIEW_COMPACT = 1;
|
||||
|
||||
/** Vista grande. */
|
||||
public static final int VIEW_LARGE = 2;
|
||||
|
||||
/** Lista de mangas a mostrar. */
|
||||
private List<MangaEntity> mangaList;
|
||||
|
||||
/** Tipo de vista actual. */
|
||||
private int viewType;
|
||||
|
||||
/** Listener para clic corto (edición). */
|
||||
private final OnMangaClickListener onEditClick;
|
||||
|
||||
/** Listener para clic prolongado (acciones extendidas). */
|
||||
private final OnMangaClickListener onLongClick;
|
||||
|
||||
/**
|
||||
* Constructor del adaptador.
|
||||
*
|
||||
* @param mangaList lista de mangas a mostrar.
|
||||
* @param viewType tipo de vista deseado.
|
||||
* @param onEditClick callback para clics normales.
|
||||
* @param onLongClick callback para clics prolongados.
|
||||
*/
|
||||
public MangaAdapter(List<MangaEntity> mangaList, int viewType,
|
||||
OnMangaClickListener onEditClick,
|
||||
OnMangaClickListener onLongClick) {
|
||||
|
|
@ -124,21 +147,51 @@ public class MangaAdapter extends RecyclerView.Adapter<MangaAdapter.MangaViewHol
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cantidad de mangas en la lista.
|
||||
*
|
||||
* @return número total de ítems.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mangaList != null ? mangaList.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve el tipo de vista para el ítem en la posición dada.
|
||||
*
|
||||
* @param position posición del ítem.
|
||||
* @return tipo de vista.
|
||||
*/
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return viewType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reemplaza la lista actual por una nueva y actualiza el adaptador.
|
||||
*
|
||||
* @param newList nueva lista de mangas.
|
||||
*/
|
||||
public void updateList(List<MangaEntity> newList) {
|
||||
mangaList.clear();
|
||||
mangaList.addAll(newList);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder para representar un ítem de manga en el RecyclerView.
|
||||
*/
|
||||
public static class MangaViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView imageCover;
|
||||
TextView textTitle, textStatus, textProgress, textScore, textType;
|
||||
View statusDot;
|
||||
|
||||
/**
|
||||
* Constructor del ViewHolder.
|
||||
*
|
||||
* @param itemView vista inflada del ítem.
|
||||
*/
|
||||
public MangaViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
imageCover = itemView.findViewById(R.id.imageCover);
|
||||
|
|
@ -151,12 +204,15 @@ public class MangaAdapter extends RecyclerView.Adapter<MangaAdapter.MangaViewHol
|
|||
}
|
||||
}
|
||||
|
||||
public void updateList(List<MangaEntity> newList) {
|
||||
this.mangaList = newList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interfaz para manejar clics sobre ítems de manga.
|
||||
*/
|
||||
public interface OnMangaClickListener {
|
||||
/**
|
||||
* Se ejecuta cuando se hace clic en un manga.
|
||||
*
|
||||
* @param manga el ítem clicado.
|
||||
*/
|
||||
void onClick(MangaEntity manga);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,25 +15,58 @@ import com.santiparra.yomitrack.model.AniListMedia;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adaptador para mostrar resultados de búsqueda de mangas desde AniList.
|
||||
* Utilizado en el fragmento de búsqueda para permitir seleccionar un manga.
|
||||
*/
|
||||
public class MangaSearchAdapter extends RecyclerView.Adapter<MangaSearchAdapter.SearchViewHolder> {
|
||||
|
||||
/** Lista de mangas obtenidos desde la API de AniList. */
|
||||
private List<AniListMedia> mangaList;
|
||||
|
||||
/** Listener para manejar clics en los ítems del RecyclerView. */
|
||||
private final OnMangaClickListener clickListener;
|
||||
|
||||
/**
|
||||
* Interfaz que define el callback cuando se hace clic en un manga.
|
||||
*/
|
||||
public interface OnMangaClickListener {
|
||||
/**
|
||||
* Método invocado al hacer clic sobre un manga.
|
||||
*
|
||||
* @param manga objeto de AniList clicado.
|
||||
*/
|
||||
void onClick(AniListMedia manga);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor del adaptador.
|
||||
*
|
||||
* @param mangaList lista de resultados de búsqueda.
|
||||
* @param clickListener listener para manejar el clic en cada ítem.
|
||||
*/
|
||||
public MangaSearchAdapter(List<AniListMedia> mangaList, OnMangaClickListener clickListener) {
|
||||
this.mangaList = mangaList;
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reemplaza la lista de mangas actual por una nueva y actualiza el RecyclerView.
|
||||
*
|
||||
* @param mangaList nueva lista de mangas.
|
||||
*/
|
||||
public void setMangaList(List<AniListMedia> mangaList) {
|
||||
this.mangaList = mangaList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Infla el layout para un ítem individual del RecyclerView.
|
||||
*
|
||||
* @param parent el ViewGroup padre.
|
||||
* @param viewType tipo de vista (no utilizado aquí).
|
||||
* @return instancia del ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public SearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
|
@ -41,6 +74,12 @@ public class MangaSearchAdapter extends RecyclerView.Adapter<MangaSearchAdapter.
|
|||
return new SearchViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asocia los datos del manga con la vista.
|
||||
*
|
||||
* @param holder ViewHolder que contiene la vista.
|
||||
* @param position posición del ítem en la lista.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SearchViewHolder holder, int position) {
|
||||
AniListMedia manga = mangaList.get(position);
|
||||
|
|
@ -54,15 +93,31 @@ public class MangaSearchAdapter extends RecyclerView.Adapter<MangaSearchAdapter.
|
|||
holder.itemView.setOnClickListener(v -> clickListener.onClick(manga));
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cantidad total de mangas en la lista.
|
||||
*
|
||||
* @return tamaño de la lista.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mangaList != null ? mangaList.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder que representa cada ítem del RecyclerView.
|
||||
*/
|
||||
static class SearchViewHolder extends RecyclerView.ViewHolder {
|
||||
/** Imagen de portada del manga. */
|
||||
ImageView imageCover;
|
||||
|
||||
/** Título del manga. */
|
||||
TextView title;
|
||||
|
||||
/**
|
||||
* Constructor del ViewHolder.
|
||||
*
|
||||
* @param itemView vista inflada del ítem.
|
||||
*/
|
||||
public SearchViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
imageCover = itemView.findViewById(R.id.imageCover);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.widget.ImageButton;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
|
@ -18,11 +19,24 @@ import com.santiparra.yomitrack.model.RecentActivityModel;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adaptador para mostrar la actividad reciente del usuario en forma de tarjetas.
|
||||
* Cada tarjeta puede incluir una acción, portada, comentarios, botón de like y comentar.
|
||||
*/
|
||||
public class RecentActivityAdapter extends RecyclerView.Adapter<RecentActivityAdapter.ActivityViewHolder> {
|
||||
|
||||
/** Lista de actividades recientes. */
|
||||
private List<RecentActivityModel> activityList;
|
||||
|
||||
/** ID del usuario actualmente logueado (para comentarios y likes). */
|
||||
private final int currentUserId;
|
||||
|
||||
/**
|
||||
* Constructor del adaptador.
|
||||
*
|
||||
* @param activityList lista de actividades a mostrar.
|
||||
* @param currentUserId ID del usuario actual.
|
||||
*/
|
||||
public RecentActivityAdapter(List<RecentActivityModel> activityList, int currentUserId) {
|
||||
this.activityList = activityList;
|
||||
this.currentUserId = currentUserId;
|
||||
|
|
@ -31,7 +45,8 @@ public class RecentActivityAdapter extends RecyclerView.Adapter<RecentActivityAd
|
|||
@NonNull
|
||||
@Override
|
||||
public ActivityViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_activity_card, parent, false);
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_activity_card, parent, false);
|
||||
return new ActivityViewHolder(view);
|
||||
}
|
||||
|
||||
|
|
@ -49,9 +64,11 @@ public class RecentActivityAdapter extends RecyclerView.Adapter<RecentActivityAd
|
|||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(holder.image);
|
||||
|
||||
// Limpiar contenedor de comentarios antes de agregar los nuevos
|
||||
holder.commentContainer.removeAllViews();
|
||||
LayoutInflater inflater = LayoutInflater.from(holder.itemView.getContext());
|
||||
|
||||
// Mostrar comentarios
|
||||
for (CommentModel comment : activity.comments) {
|
||||
View commentView = inflater.inflate(R.layout.item_comment, holder.commentContainer, false);
|
||||
|
||||
|
|
@ -75,19 +92,16 @@ public class RecentActivityAdapter extends RecyclerView.Adapter<RecentActivityAd
|
|||
likeButton.setImageResource(comment.isLiked() ? R.drawable.ic_heart_filled : R.drawable.ic_heart_outline);
|
||||
likeButton.setColorFilter(commentView.getContext().getColor(comment.isLiked() ? R.color.pink : R.color.gray));
|
||||
|
||||
likeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Manejo de likes en comentarios
|
||||
likeButton.setOnClickListener(v -> {
|
||||
boolean newLike = !comment.isLiked();
|
||||
comment.setLiked(newLike);
|
||||
likeButton.setImageResource(newLike ? R.drawable.ic_heart_filled : R.drawable.ic_heart_outline);
|
||||
likeButton.setColorFilter(commentView.getContext().getColor(newLike ? R.color.pink : R.color.gray));
|
||||
}
|
||||
});
|
||||
|
||||
replyButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Manejo de respuesta a un comentario
|
||||
replyButton.setOnClickListener(v -> {
|
||||
int adapterPos = holder.getAdapterPosition();
|
||||
if (adapterPos == RecyclerView.NO_POSITION) return;
|
||||
RecentActivityModel activityItem = activityList.get(adapterPos);
|
||||
|
|
@ -99,15 +113,13 @@ public class RecentActivityAdapter extends RecyclerView.Adapter<RecentActivityAd
|
|||
comment.getUsername()
|
||||
);
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
|
||||
holder.commentContainer.addView(commentView);
|
||||
}
|
||||
|
||||
holder.commentButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Botón para añadir nuevo comentario a la actividad
|
||||
holder.commentButton.setOnClickListener(v -> {
|
||||
int adapterPos = holder.getAdapterPosition();
|
||||
if (adapterPos == RecyclerView.NO_POSITION) return;
|
||||
RecentActivityModel activityItem = activityList.get(adapterPos);
|
||||
|
|
@ -118,36 +130,51 @@ public class RecentActivityAdapter extends RecyclerView.Adapter<RecentActivityAd
|
|||
() -> notifyItemChanged(adapterPos)
|
||||
);
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
|
||||
holder.likeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Botón de like para la actividad
|
||||
holder.likeButton.setOnClickListener(v -> {
|
||||
activity.liked = !activity.liked;
|
||||
holder.likeButton.setImageResource(activity.liked ? R.drawable.ic_heart_filled : R.drawable.ic_heart_outline);
|
||||
holder.likeButton.setColorFilter(holder.itemView.getContext().getColor(
|
||||
activity.liked ? R.color.pink : R.color.textPrimary));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cantidad de actividades en la lista.
|
||||
*
|
||||
* @return tamaño de la lista.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return activityList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reemplaza la lista actual por una nueva y actualiza la vista.
|
||||
*
|
||||
* @param newList nueva lista de actividades.
|
||||
*/
|
||||
public void updateData(List<RecentActivityModel> newList) {
|
||||
this.activityList = newList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder que representa una tarjeta de actividad reciente.
|
||||
*/
|
||||
static class ActivityViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView user, action, title, time;
|
||||
ImageView image;
|
||||
ImageButton likeButton, commentButton;
|
||||
LinearLayout commentContainer;
|
||||
|
||||
/**
|
||||
* Constructor del ViewHolder.
|
||||
*
|
||||
* @param itemView vista inflada del ítem.
|
||||
*/
|
||||
public ActivityViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
user = itemView.findViewById(R.id.activityUser);
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
package com.santiparra.yomitrack.model.adapters.sectionadapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.santiparra.yomitrack.R;
|
||||
import com.santiparra.yomitrack.model.ItemModel;
|
||||
import com.santiparra.yomitrack.model.adapters.homeadapter.HomeAdapter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SectionAdapter extends RecyclerView.Adapter<SectionAdapter.SectionViewHolder> {
|
||||
|
||||
private final List<String> sectionTitles;
|
||||
private final Map<String, List<ItemModel>> sectionItems;
|
||||
|
||||
public SectionAdapter(List<String> sectionTitles, Map<String, List<ItemModel>> sectionItems) {
|
||||
this.sectionTitles = sectionTitles;
|
||||
this.sectionItems = sectionItems;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SectionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_section, parent, false);
|
||||
return new SectionViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SectionViewHolder holder, int position) {
|
||||
String sectionTitle = sectionTitles.get(position);
|
||||
holder.title.setText(sectionTitle);
|
||||
|
||||
List<ItemModel> fullList = sectionItems.get(sectionTitle);
|
||||
|
||||
HomeAdapter adapter = new HomeAdapter(fullList, sectionTitle);
|
||||
holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
holder.recyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return sectionTitles.size();
|
||||
}
|
||||
|
||||
static class SectionViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView title;
|
||||
RecyclerView recyclerView;
|
||||
|
||||
public SectionViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
title = itemView.findViewById(R.id.sectionTitle);
|
||||
recyclerView = itemView.findViewById(R.id.sectionRecyclerView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,18 +75,12 @@ public class AddAnimeFragment extends Fragment {
|
|||
|
||||
private void setupSpinners() {
|
||||
ArrayAdapter<CharSequence> statusAdapter = ArrayAdapter.createFromResource(
|
||||
requireContext(),
|
||||
R.array.anime_status_array,
|
||||
R.layout.item_spinner
|
||||
);
|
||||
statusAdapter.setDropDownViewResource(R.layout.item_spinner); // ✅ blanco también al desplegar
|
||||
requireContext(), R.array.anime_status_array, R.layout.item_spinner);
|
||||
statusAdapter.setDropDownViewResource(R.layout.item_spinner);
|
||||
statusSpinner.setAdapter(statusAdapter);
|
||||
|
||||
ArrayAdapter<CharSequence> typeAdapter = ArrayAdapter.createFromResource(
|
||||
requireContext(),
|
||||
R.array.anime_type_array,
|
||||
R.layout.item_spinner
|
||||
);
|
||||
requireContext(), R.array.anime_type_array, R.layout.item_spinner);
|
||||
typeAdapter.setDropDownViewResource(R.layout.item_spinner);
|
||||
typeSpinner.setAdapter(typeAdapter);
|
||||
}
|
||||
|
|
@ -128,23 +122,16 @@ public class AddAnimeFragment extends Fragment {
|
|||
String status = statusSpinner.getSelectedItem().toString();
|
||||
String type = typeSpinner.getSelectedItem().toString();
|
||||
|
||||
int score = 0;
|
||||
int progress = 0;
|
||||
try {
|
||||
score = Integer.parseInt(scoreEditText.getText().toString());
|
||||
progress = Integer.parseInt(progressEditText.getText().toString());
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
int score = parseIntOrZero(scoreEditText.getText().toString());
|
||||
int progress = parseIntOrZero(progressEditText.getText().toString());
|
||||
|
||||
AnimeEntity anime = new AnimeEntity();
|
||||
anime.setUserId(userId);
|
||||
anime.setTitle(selected.getTitle());
|
||||
|
||||
if (selected.getImageUrl() == null || selected.getImageUrl().isEmpty()) {
|
||||
selectedImageUrl = "android.resource://" + requireContext().getPackageName() + "/" + R.drawable.sample_cover;
|
||||
} else {
|
||||
selectedImageUrl = selected.getImageUrl();
|
||||
}
|
||||
selectedImageUrl = (selected.getImageUrl() == null || selected.getImageUrl().isEmpty())
|
||||
? "android.resource://" + requireContext().getPackageName() + "/" + R.drawable.sample_cover
|
||||
: selected.getImageUrl();
|
||||
|
||||
anime.setImageUrl(selectedImageUrl);
|
||||
anime.setStatus(status);
|
||||
|
|
@ -158,6 +145,7 @@ public class AddAnimeFragment extends Fragment {
|
|||
if (response.isSuccessful() && response.body() != null) {
|
||||
Toast.makeText(getContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show();
|
||||
registrarActividad(anime.getTitle());
|
||||
notificarAñadido();
|
||||
requireActivity().getSupportFragmentManager().popBackStack();
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Error al guardar anime", Toast.LENGTH_SHORT).show();
|
||||
|
|
@ -182,17 +170,29 @@ public class AddAnimeFragment extends Fragment {
|
|||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
Log.d("ACTIVITY_POST", "Código de respuesta: " + response.code());
|
||||
if (!response.isSuccessful()) {
|
||||
Log.e("ACTIVITY_POST", "Error en response: " + response.errorBody());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
Log.e("ACTIVITY_POST", "Error al registrar actividad: " + t.getMessage(), t);
|
||||
if (!isAdded()) return;
|
||||
if (isAdded()) {
|
||||
Toast.makeText(getContext(), "Error al registrar actividad", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notificarAñadido() {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean("anime_added", true);
|
||||
getParentFragmentManager().setFragmentResult("anime_add_request", result);
|
||||
}
|
||||
|
||||
private int parseIntOrZero(String value) {
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ public class AddMangaFragment extends Fragment {
|
|||
typeSpinner.setAdapter(typeAdapter);
|
||||
}
|
||||
|
||||
|
||||
private void setupRecycler() {
|
||||
searchAdapter = new MangaSearchAdapter(new ArrayList<>(), this::onMangaSelected);
|
||||
searchResults.setAdapter(searchAdapter);
|
||||
|
|
@ -98,8 +97,7 @@ public class AddMangaFragment extends Fragment {
|
|||
|
||||
private void setupSearch() {
|
||||
searchEditText.setOnEditorActionListener((TextView v, int actionId, KeyEvent event) -> {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
|
||||
(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
|
||||
String query = searchEditText.getText().toString().trim();
|
||||
if (!query.isEmpty()) {
|
||||
api.searchAniList(query, "MANGA").enqueue(new Callback<List<AniListMedia>>() {
|
||||
|
|
@ -133,8 +131,7 @@ public class AddMangaFragment extends Fragment {
|
|||
try {
|
||||
score = Integer.parseInt(scoreEditText.getText().toString());
|
||||
progress = Integer.parseInt(progressEditText.getText().toString());
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
|
||||
MangaEntity manga = new MangaEntity();
|
||||
manga.setUserId(userId);
|
||||
|
|
@ -158,6 +155,9 @@ public class AddMangaFragment extends Fragment {
|
|||
if (response.isSuccessful() && response.body() != null) {
|
||||
Toast.makeText(getContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show();
|
||||
registrarActividad(manga.getTitle());
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean("manga_added", true);
|
||||
getParentFragmentManager().setFragmentResult("manga_add_request", result);
|
||||
requireActivity().getSupportFragmentManager().popBackStack();
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Error al guardar manga", Toast.LENGTH_SHORT).show();
|
||||
|
|
@ -180,11 +180,7 @@ public class AddMangaFragment extends Fragment {
|
|||
|
||||
api.postActivity(actividad).enqueue(new Callback<JsonObject>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
// Puedes logear el error si lo deseas
|
||||
}
|
||||
}
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
|
|
|
|||
|
|
@ -59,15 +59,12 @@ public class FragmentAnime extends Fragment {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_alist, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
initViews(view);
|
||||
|
|
@ -76,6 +73,7 @@ public class FragmentAnime extends Fragment {
|
|||
setupSearchListener();
|
||||
setupFab(view);
|
||||
setupInsets(view);
|
||||
setupResultListener();
|
||||
|
||||
SharedPreferences prefs = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE);
|
||||
userId = prefs.getInt("user_id", -1);
|
||||
|
|
@ -84,7 +82,7 @@ public class FragmentAnime extends Fragment {
|
|||
Toast.makeText(getContext(), "Error: sesión no iniciada", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
// Mostrar el nombre del usuario
|
||||
|
||||
String username = prefs.getString("username", "Usuario");
|
||||
TextView textViewUsername = view.findViewById(R.id.textViewUsername);
|
||||
textViewUsername.setText(username);
|
||||
|
|
@ -124,6 +122,30 @@ public class FragmentAnime extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
private void setupResultListener() {
|
||||
getParentFragmentManager().setFragmentResultListener("anime_add_request", this, (requestKey, bundle) -> {
|
||||
if (bundle.getBoolean("anime_added", false)) {
|
||||
currentPage = 1;
|
||||
animeList.clear();
|
||||
adapter.updateList(new ArrayList<>());
|
||||
loadMoreAnimes(currentPage);
|
||||
}
|
||||
});
|
||||
|
||||
getParentFragmentManager().setFragmentResultListener("anime_delete_request", this, (requestKey, bundle) -> {
|
||||
int deletedId = bundle.getInt("anime_id", -1);
|
||||
if (deletedId != -1) {
|
||||
for (int i = 0; i < animeList.size(); i++) {
|
||||
if (animeList.get(i).getId() == deletedId) {
|
||||
animeList.remove(i);
|
||||
adapter.updateList(animeList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupViewButtons() {
|
||||
btnViewCompact.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_COMPACT));
|
||||
btnViewNormal.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_NORMAL));
|
||||
|
|
@ -197,9 +219,8 @@ public class FragmentAnime extends Fragment {
|
|||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
Toast.makeText(requireContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show();
|
||||
currentPage = 1;
|
||||
animeList.clear();
|
||||
loadMoreAnimes(currentPage);
|
||||
animeList.remove(anime);
|
||||
adapter.updateList(animeList);
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
|
@ -225,8 +246,15 @@ public class FragmentAnime extends Fragment {
|
|||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
List<AnimeEntity> nuevos = response.body().getData();
|
||||
if (page == 1) {
|
||||
animeList.clear();
|
||||
animeList.addAll(nuevos);
|
||||
adapter.notifyItemRangeInserted(animeList.size() - nuevos.size(), nuevos.size());
|
||||
adapter.updateList(animeList);
|
||||
} else {
|
||||
int start = animeList.size();
|
||||
animeList.addAll(nuevos);
|
||||
adapter.notifyItemRangeInserted(start, nuevos.size());
|
||||
}
|
||||
isLoading = response.body().isHasNextPage();
|
||||
} else {
|
||||
isLoading = false;
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public class EditAnimeFragment extends Fragment {
|
|||
String[] typeArray = getResources().getStringArray(R.array.anime_type_array);
|
||||
|
||||
ArrayAdapter<String> statusAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, statusArray);
|
||||
statusAdapter.setDropDownViewResource(R.layout.item_spinner); // Aplica color blanco en lista desplegable también
|
||||
statusAdapter.setDropDownViewResource(R.layout.item_spinner);
|
||||
spinnerStatus.setAdapter(statusAdapter);
|
||||
|
||||
ArrayAdapter<String> typeAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, typeArray);
|
||||
|
|
@ -142,8 +142,6 @@ public class EditAnimeFragment extends Fragment {
|
|||
api.updateAnime(anime.getId(), anime).enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
|
||||
Log.d("API_RESPONSE", "onResponse ejecutado: " + response.body());
|
||||
|
||||
if (!isAdded()) return;
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
|
|
@ -160,7 +158,6 @@ public class EditAnimeFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse> call, Throwable t) {
|
||||
Log.e("API_RESPONSE", "onFailure ejecutado: " + t.getMessage(), t);
|
||||
if (!isAdded()) return;
|
||||
Toast.makeText(requireContext(), "Fallo en la conexión", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
|
@ -179,6 +176,10 @@ public class EditAnimeFragment extends Fragment {
|
|||
Toast.makeText(requireContext(), "Anime eliminado", Toast.LENGTH_SHORT).show();
|
||||
requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE)
|
||||
.edit().putBoolean("refresh_profile", true).apply();
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean("anime_deleted", true);
|
||||
result.putInt("anime_id", anime.getId());
|
||||
getParentFragmentManager().setFragmentResult("anime_delete_request", result);
|
||||
requireActivity().getSupportFragmentManager().popBackStack();
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
|
||||
|
|
@ -231,7 +232,6 @@ public class EditAnimeFragment extends Fragment {
|
|||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
Log.d("ACTIVITY_DELETE", "Actividad de eliminación registrada");
|
||||
|
||||
|
||||
if (getParentFragment() instanceof FragmentProfile) {
|
||||
((FragmentProfile) getParentFragment()).loadActivity();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,30 +61,18 @@ public class EditMangaFragment extends Fragment {
|
|||
|
||||
fillFields();
|
||||
|
||||
String[] statusArray = getResources().getStringArray(R.array.manga_status_array);
|
||||
String[] typeArray = getResources().getStringArray(R.array.manga_type_array);
|
||||
|
||||
ArrayAdapter<String> statusAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, statusArray);
|
||||
ArrayAdapter<String> statusAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner,
|
||||
getResources().getStringArray(R.array.manga_status_array));
|
||||
statusAdapter.setDropDownViewResource(R.layout.item_spinner);
|
||||
spinnerStatus.setAdapter(statusAdapter);
|
||||
|
||||
ArrayAdapter<String> typeAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, typeArray);
|
||||
ArrayAdapter<String> typeAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner,
|
||||
getResources().getStringArray(R.array.manga_type_array));
|
||||
typeAdapter.setDropDownViewResource(R.layout.item_spinner);
|
||||
spinnerType.setAdapter(typeAdapter);
|
||||
|
||||
for (int i = 0; i < statusArray.length; i++) {
|
||||
if (statusArray[i].equalsIgnoreCase(manga.getStatus())) {
|
||||
spinnerStatus.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < typeArray.length; i++) {
|
||||
if (typeArray[i].equalsIgnoreCase(manga.getType())) {
|
||||
spinnerType.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
setSpinnerSelection(spinnerStatus, manga.getStatus());
|
||||
setSpinnerSelection(spinnerType, manga.getType());
|
||||
|
||||
buttonSave.setOnClickListener(v -> saveChanges());
|
||||
|
||||
|
|
@ -104,6 +92,15 @@ public class EditMangaFragment extends Fragment {
|
|||
editTextProgress.setText(String.valueOf(manga.getProgress()));
|
||||
}
|
||||
|
||||
private void setSpinnerSelection(Spinner spinner, String value) {
|
||||
for (int i = 0; i < spinner.getCount(); i++) {
|
||||
if (spinner.getItemAtPosition(i).toString().equalsIgnoreCase(value)) {
|
||||
spinner.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveChanges() {
|
||||
if (manga == null) {
|
||||
Toast.makeText(requireContext(), "Error: Manga no cargado", Toast.LENGTH_SHORT).show();
|
||||
|
|
@ -128,14 +125,11 @@ public class EditMangaFragment extends Fragment {
|
|||
return;
|
||||
}
|
||||
|
||||
String status = spinnerStatus.getSelectedItem().toString();
|
||||
String type = spinnerType.getSelectedItem().toString();
|
||||
|
||||
manga.setTitle(title);
|
||||
manga.setScore(score);
|
||||
manga.setProgress(progress);
|
||||
manga.setStatus(status);
|
||||
manga.setType(type);
|
||||
manga.setStatus(spinnerStatus.getSelectedItem().toString());
|
||||
manga.setType(spinnerType.getSelectedItem().toString());
|
||||
|
||||
api.updateManga(manga.getId(), manga).enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
|
|
@ -144,7 +138,7 @@ public class EditMangaFragment extends Fragment {
|
|||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
Toast.makeText(requireContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show();
|
||||
registrarActividad(manga.getTitle(), manga.getImageUrl());
|
||||
registrarActividad("update de un manga", manga.getTitle(), manga.getImageUrl());
|
||||
requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE)
|
||||
.edit().putBoolean("refresh_profile", true).apply();
|
||||
requireActivity().getSupportFragmentManager().popBackStack();
|
||||
|
|
@ -162,7 +156,7 @@ public class EditMangaFragment extends Fragment {
|
|||
}
|
||||
|
||||
private void deleteManga() {
|
||||
registrarActividadEliminacionManga(manga.getTitle(), manga.getImageUrl());
|
||||
registrarActividad("eliminó un manga", manga.getTitle(), manga.getImageUrl());
|
||||
|
||||
api.deleteManga(manga.getId()).enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
|
|
@ -173,6 +167,12 @@ public class EditMangaFragment extends Fragment {
|
|||
Toast.makeText(requireContext(), "Manga eliminado", Toast.LENGTH_SHORT).show();
|
||||
requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE)
|
||||
.edit().putBoolean("refresh_profile", true).apply();
|
||||
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean("manga_deleted", true);
|
||||
result.putInt("manga_id", manga.getId());
|
||||
getParentFragmentManager().setFragmentResult("manga_delete_request", result);
|
||||
|
||||
requireActivity().getSupportFragmentManager().popBackStack();
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
|
||||
|
|
@ -187,44 +187,20 @@ public class EditMangaFragment extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
private void registrarActividad(String titulo, String imagen) {
|
||||
private void registrarActividad(String action, String titulo, String imagen) {
|
||||
int userId = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE).getInt("user_id", -1);
|
||||
if (userId == -1) return;
|
||||
|
||||
Map<String, Object> actividad = new HashMap<>();
|
||||
actividad.put("userId", userId);
|
||||
actividad.put("action", "update de un manga");
|
||||
actividad.put("action", action);
|
||||
actividad.put("mediaTitle", titulo);
|
||||
actividad.put("imageUrl", imagen);
|
||||
|
||||
api.postActivity(actividad).enqueue(new Callback<JsonObject>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
Log.d("ACTIVITY_DELETE", "Actividad de edicion registrada");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
Log.e("ACTIVITY_DELETE", "Error al registrar actividad: " + t.getMessage(), t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void registrarActividadEliminacionManga(String titulo, String imagen) {
|
||||
int userId = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE).getInt("user_id", -1);
|
||||
if (userId == -1) return;
|
||||
|
||||
Map<String, Object> actividad = new HashMap<>();
|
||||
actividad.put("userId", userId);
|
||||
actividad.put("action", "eliminó un manga");
|
||||
actividad.put("mediaTitle", titulo);
|
||||
actividad.put("imageUrl", imagen);
|
||||
|
||||
api.postActivity(actividad).enqueue(new Callback<JsonObject>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
Log.d("ACTIVITY_DELETE", "Actividad de eliminación registrada");
|
||||
|
||||
Log.d("ACTIVITY", "Actividad registrada: " + action);
|
||||
if (getParentFragment() instanceof FragmentProfile) {
|
||||
((FragmentProfile) getParentFragment()).loadActivity();
|
||||
}
|
||||
|
|
@ -232,10 +208,8 @@ public class EditMangaFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
Log.e("ACTIVITY_DELETE", "Error al registrar actividad: " + t.getMessage(), t);
|
||||
Log.e("ACTIVITY", "Error al registrar actividad: " + t.getMessage(), t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
// FragmentHome.java
|
||||
package com.santiparra.yomitrack.ui.fragments.home;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -15,13 +13,11 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.santiparra.yomitrack.R;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public class FragmentManga extends Fragment {
|
|||
initViews(view);
|
||||
setupListeners();
|
||||
setupRecyclerView();
|
||||
setupResultListeners();
|
||||
|
||||
api = ApiClient.getClient().create(ApiService.class);
|
||||
SharedPreferences prefs = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE);
|
||||
|
|
@ -79,10 +80,9 @@ public class FragmentManga extends Fragment {
|
|||
Toast.makeText(getContext(), "Error: sesión no iniciada", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
// Mostrar el nombre del usuario
|
||||
|
||||
String username = prefs.getString("username", "Usuario");
|
||||
TextView textViewUsername = view.findViewById(R.id.textViewUsername);
|
||||
textViewUsername.setText(username);
|
||||
((TextView) view.findViewById(R.id.textViewUsername)).setText(username);
|
||||
|
||||
setViewType(currentViewType);
|
||||
loadMoreMangas(currentPage);
|
||||
|
|
@ -151,15 +151,37 @@ public class FragmentManga extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
private void setupResultListeners() {
|
||||
getParentFragmentManager().setFragmentResultListener("manga_add_request", this, (key, bundle) -> {
|
||||
if (bundle.getBoolean("manga_added", false)) {
|
||||
currentPage = 1;
|
||||
mangaList.clear();
|
||||
adapter.updateList(new ArrayList<>());
|
||||
loadMoreMangas(currentPage);
|
||||
}
|
||||
});
|
||||
|
||||
getParentFragmentManager().setFragmentResultListener("manga_delete_request", this, (key, bundle) -> {
|
||||
int deletedId = bundle.getInt("manga_id", -1);
|
||||
if (deletedId != -1) {
|
||||
for (int i = 0; i < mangaList.size(); i++) {
|
||||
if (mangaList.get(i).getId() == deletedId) {
|
||||
mangaList.remove(i);
|
||||
adapter.notifyItemRemoved(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setViewType(int viewType) {
|
||||
currentViewType = viewType;
|
||||
|
||||
if (viewType == MangaAdapter.VIEW_LARGE) {
|
||||
recyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
|
||||
} else {
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
}
|
||||
|
||||
adapter = new MangaAdapter(mangaList, viewType, this::showEditDialog, this::deleteManga);
|
||||
recyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
|
@ -187,12 +209,15 @@ public class FragmentManga extends Fragment {
|
|||
@Override
|
||||
public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
|
||||
if (!isAdded()) return;
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
for (int i = 0; i < mangaList.size(); i++) {
|
||||
if (mangaList.get(i).getId() == manga.getId()) {
|
||||
mangaList.remove(i);
|
||||
adapter.notifyItemRemoved(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Toast.makeText(requireContext(), response.body() != null ? response.body().getMessage() : "Manga eliminado", Toast.LENGTH_SHORT).show();
|
||||
currentPage = 1;
|
||||
mangaList.clear();
|
||||
loadMoreMangas(currentPage);
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
|
@ -214,7 +239,6 @@ public class FragmentManga extends Fragment {
|
|||
@Override
|
||||
public void onResponse(Call<MangaPageResponse> call, Response<MangaPageResponse> response) {
|
||||
if (!isAdded()) return;
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
List<MangaEntity> nuevos = response.body().getData();
|
||||
mangaList.addAll(nuevos);
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -7,11 +7,10 @@
|
|||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Progreso (color por defecto, se sobrescribe desde Java) -->
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" /> <!-- No uses referencia aquí -->
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:radius="50dp" />
|
||||
</shape>
|
||||
</clip>
|
||||
|
|
|
|||
|
|
@ -59,13 +59,6 @@
|
|||
android:textColor="@android:color/white"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonGoToReset"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ya tengo el código"
|
||||
android:backgroundTint="#2C2F38"
|
||||
android:textColor="@android:color/white" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</FrameLayout>
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<!-- Título de la sección -->
|
||||
<TextView
|
||||
android:id="@+id/sectionTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Section Title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/textPrimary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp" />
|
||||
|
||||
<!-- Lista horizontal de ítems -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/sectionRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
</LinearLayout>
|
||||
Loading…
Reference in New Issue