Creacion de nuevas clases y de nuevos xml, funcionamiento de la api de animelist , tambien ya se consume los datos del anime list y ya puedo adaptar el anime, es decir puedo añadir y editar flata por implementar eliminar!

This commit is contained in:
santi 2025-05-16 22:28:14 +02:00
parent 132cdeafc9
commit 1f526aa91e
36 changed files with 1328 additions and 1334 deletions

View File

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-04-14T15:52:07.015308900Z"> <DropdownSelection timestamp="2025-05-14T14:38:01.362664600Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\santi\.android\avd\Pixel_9_Pro.avd" /> <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\santi\.android\avd\Pixel_9_Pro_2.avd" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@ -41,11 +41,26 @@ dependencies {
implementation(libs.material) implementation(libs.material)
implementation(libs.activity) implementation(libs.activity)
implementation(libs.constraintlayout) implementation(libs.constraintlayout)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.espresso.core)
// Room
implementation ("androidx.room:room-runtime:2.6.1")
annotationProcessor ("androidx.room:room-compiler:2.6.1")
/*Dependecia para usar glide y cargar imagenes mediante url*/ /*Dependecia para usar glide y cargar imagenes mediante url*/
implementation("com.github.bumptech.glide:glide:4.16.0") implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0") annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
// Retrofit para llamadas HTTP
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
// Convertidor Gson (JSON <-> Java)
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
// (Opcional) Logging para depurar peticiones
implementation ("com.squareup.okhttp3:logging-interceptor:4.9.0")
} }

View File

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<!-- Agregado el permiso de Internet --> <!-- Permiso para internet si tu app lo requiere -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
@ -14,16 +14,29 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.YomiTrack" android:theme="@style/Theme.YomiTrack"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31"> tools:targetApi="31">
<!-- PANTALLA INICIAL: SplashActivity -->
<activity <activity
android:name=".ui.MainActivity" android:name=".ui.fragments.login.SplashActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- LOGIN: alojará login y registro -->
<activity
android:name=".ui.fragments.login.LoginActivity"
android:exported="false" />
<!-- APP PRINCIPAL: BottomNavigationView -->
<activity
android:name=".ui.MainActivity"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View File

@ -1,49 +0,0 @@
package com.santiparra.yomitrack.model;
public class AnimeItem {
private String title;
private String imageUrl;
private int watchedEpisodes;
private int totalEpisodes;
private double score;
private String type;
private String status; // Nuevo atributo
public AnimeItem(String title, String imageUrl, int watchedEpisodes, int totalEpisodes, double score, String type, String status) {
this.title = title;
this.imageUrl = imageUrl;
this.watchedEpisodes = watchedEpisodes;
this.totalEpisodes = totalEpisodes;
this.score = score;
this.type = type;
this.status = status;
}
public String getTitle() {
return title;
}
public String getImageUrl() {
return imageUrl;
}
public int getWatchedEpisodes() {
return watchedEpisodes;
}
public int getTotalEpisodes() {
return totalEpisodes;
}
public double getScore() {
return score;
}
public String getType() {
return type;
}
public String getStatus() {
return status;
}
}

View File

@ -1,18 +1,20 @@
package com.santiparra.yomitrack.model; // Ajusta el paquete si necesitas package com.santiparra.yomitrack.model;
/**
* Modelo general de ítem para mostrar en secciones del home.
* Se usa tanto para anime como manga.
*/
public class ItemModel { public class ItemModel {
private String title; private String title;
private String progress; private String progress;
private String imageUrl; private String imageUrl;
private ContentType contentType; // Anime o Manga private ContentType contentType;
public enum ContentType { public enum ContentType {
ANIME, ANIME,
MANGA MANGA
} }
// Constructor completo
public ItemModel(String title, String progress, String imageUrl, ContentType contentType) { public ItemModel(String title, String progress, String imageUrl, ContentType contentType) {
this.title = title; this.title = title;
this.progress = progress; this.progress = progress;
@ -20,7 +22,6 @@ public class ItemModel {
this.contentType = contentType; this.contentType = contentType;
} }
// Getters
public String getTitle() { public String getTitle() {
return title; return title;
} }
@ -37,7 +38,6 @@ public class ItemModel {
return contentType; return contentType;
} }
// Setters (opcional si quieres mutar los datos después)
public void setTitle(String title) { public void setTitle(String title) {
this.title = title; this.title = title;
} }

View File

@ -1,7 +1,14 @@
package com.santiparra.yomitrack.model; package com.santiparra.yomitrack.model;
/**
* Modelo de actividad reciente para mostrar acciones de usuario.
*/
public class RecentActivityModel { public class RecentActivityModel {
public String user, action, title, time, imageUrl; public String user;
public String action;
public String title;
public String time;
public String imageUrl;
public RecentActivityModel(String user, String action, String title, String time, String imageUrl) { public RecentActivityModel(String user, String action, String title, String time, String imageUrl) {
this.user = user; this.user = user;

View File

@ -1,5 +1,9 @@
package com.santiparra.yomitrack.model; package com.santiparra.yomitrack.model;
/**
* Representa una estadística del usuario, usada para barras de progreso.
* Incluye categoría, cantidad y porcentaje de completado.
*/
public class UserStats { public class UserStats {
private String category; private String category;
private int count; private int count;

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -5,131 +5,174 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.santiparra.yomitrack.R; import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.AnimeItem; import com.santiparra.yomitrack.db.entities.AnimeEntity;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHolder> { public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHolder> {
private final List<AnimeItem> animeList; public static final int VIEW_NORMAL = 0;
private final Context context; public static final int VIEW_COMPACT = 1;
private int viewMode = 0; public static final int VIEW_LARGE = 2;
public interface OnAnimeRemoveListener { private List<AnimeEntity> animeList;
void onAnimeRemoved(AnimeItem anime); private int viewType;
private final OnAnimeClickListener onEditClick;
private final OnAnimeClickListener onLongClick;
// Constructor optimizado para uso simple
public AnimeAdapter(Context context) {
this.animeList = new ArrayList<>();
this.viewType = VIEW_NORMAL;
this.onEditClick = null;
this.onLongClick = null;
} }
private OnAnimeRemoveListener removeListener; public AnimeAdapter(List<AnimeEntity> animeList, int viewType,
OnAnimeClickListener onEditClick,
public void setOnAnimeRemoveListener(OnAnimeRemoveListener listener) { OnAnimeClickListener onLongClick) {
this.removeListener = listener;
}
public void setViewMode(int mode) {
this.viewMode = mode;
}
public AnimeAdapter(Context context, List<AnimeItem> animeList) {
this.context = context;
this.animeList = animeList; this.animeList = animeList;
this.viewType = viewType;
this.onEditClick = onEditClick;
this.onLongClick = onLongClick;
}
public void setViewType(int viewType) {
this.viewType = viewType;
notifyDataSetChanged();
} }
@NonNull @NonNull
@Override @Override
public AnimeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public AnimeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
int layoutId; int layout = R.layout.item_anime;
switch (viewMode) { if (viewType == VIEW_COMPACT) layout = R.layout.item_anime_compact;
case 1: else if (viewType == VIEW_LARGE) layout = R.layout.item_anime_large;
layoutId = R.layout.item_anime_large; // solo imagen con score y episodios
break; View view = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
case 2:
layoutId = R.layout.item_anime_compact; // solo texto
break;
default:
layoutId = R.layout.item_anime; // imagen + texto
break;
}
View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
return new AnimeViewHolder(view); return new AnimeViewHolder(view);
} }
@Override @Override
public void onBindViewHolder(@NonNull AnimeViewHolder holder, int position) { public void onBindViewHolder(@NonNull AnimeViewHolder holder, int position) {
AnimeItem anime = animeList.get(position); AnimeEntity anime = animeList.get(position);
if (holder.title != null) String title = anime.getTitle() != null ? anime.getTitle() : "Sin título";
holder.title.setText(anime.getTitle()); String status = anime.getStatus() != null ? anime.getStatus() : "";
String type = anime.getType() != null ? anime.getType() : "";
String imageUrl = anime.getImageUrl();
if (holder.progress != null) if (holder.textTitle != null) holder.textTitle.setText(title);
holder.progress.setText("Progress: " + anime.getWatchedEpisodes() + "/" + anime.getTotalEpisodes());
if (holder.score != null) if (holder.textStatus != null) {
holder.score.setText(String.valueOf(anime.getScore())); String statusText = status + (type.isEmpty() ? "" : "" + type);
holder.textStatus.setText(statusText);
if (holder.type != null)
holder.type.setText(anime.getType());
if (holder.cover != null) {
Glide.with(context)
.load(anime.getImageUrl())
.placeholder(R.drawable.sample_anime_cover)
.into(holder.cover);
} }
if (holder.buttonOptions != null) { if (holder.textProgress != null) {
holder.buttonOptions.setOnClickListener(v -> { String progress = anime.getProgress() + " eps";
PopupMenu popup = new PopupMenu(context, holder.buttonOptions); holder.textProgress.setText(progress);
popup.inflate(R.menu.anime_item_menu);
popup.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.action_edit) {
Toast.makeText(context, "Edit: " + anime.getTitle(), Toast.LENGTH_SHORT).show();
return true;
} else if (id == R.id.action_remove) {
int pos = holder.getAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
AnimeItem removed = animeList.remove(pos);
notifyItemRemoved(pos);
if (removeListener != null) {
removeListener.onAnimeRemoved(removed);
}
Toast.makeText(context, "Removed: " + removed.getTitle(), Toast.LENGTH_SHORT).show();
}
return true;
}
return false;
});
popup.show();
});
} }
if (holder.textScore != null) {
holder.textScore.setText("Score: " + anime.getScore());
}
if (holder.textType != null) {
holder.textType.setText("Tipo: " + type);
}
if (holder.imageCover != null && imageUrl != null && !imageUrl.isEmpty()) {
Glide.with(holder.itemView.getContext())
.load(imageUrl)
.placeholder(R.drawable.rectangle_placeholder)
.into(holder.imageCover);
} else if (holder.imageCover != null) {
holder.imageCover.setImageResource(R.drawable.rectangle_placeholder);
}
if (holder.statusDot != null) {
int colorResId;
switch (anime.getStatus()) {
case "Completed":
colorResId = R.color.status_completed;
break;
case "Watching":
colorResId = R.color.status_watching;
break;
case "Paused":
colorResId = R.color.status_paused;
break;
case "Dropped":
colorResId = R.color.status_dropped;
break;
case "Planning":
default:
colorResId = R.color.status_planning;
break;
}
holder.statusDot.setBackgroundTintList(
ContextCompat.getColorStateList(holder.itemView.getContext(), colorResId)
);
}
holder.itemView.setOnClickListener(v -> {
if (onEditClick != null) onEditClick.onClick(anime);
});
holder.itemView.setOnLongClickListener(v -> {
if (onLongClick != null) {
onLongClick.onClick(anime);
return true;
}
return false;
});
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return animeList.size(); return animeList != null ? animeList.size() : 0;
} }
static class AnimeViewHolder extends RecyclerView.ViewHolder { @Override
TextView title, progress, score, type; public int getItemViewType(int position) {
ImageView cover, buttonOptions; return viewType;
}
AnimeViewHolder(View itemView) { public static class AnimeViewHolder extends RecyclerView.ViewHolder {
ImageView imageCover;
TextView textTitle, textStatus, textProgress, textScore, textType;
View statusDot;
public AnimeViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
title = itemView.findViewById(R.id.textViewTitle); imageCover = itemView.findViewById(R.id.imageCover);
progress = itemView.findViewById(R.id.textViewProgress); textTitle = itemView.findViewById(R.id.textTitle);
score = itemView.findViewById(R.id.textViewScore); textStatus = itemView.findViewById(R.id.textStatus);
type = itemView.findViewById(R.id.textViewType); textProgress = itemView.findViewById(R.id.textProgress);
cover = itemView.findViewById(R.id.imageViewCover); textScore = itemView.findViewById(R.id.textScore);
buttonOptions = itemView.findViewById(R.id.buttonOptions); textType = itemView.findViewById(R.id.textType);
statusDot = itemView.findViewById(R.id.statusDot);
} }
} }
public void updateList(List<AnimeEntity> newList) {
this.animeList = newList;
notifyDataSetChanged();
}
public interface OnAnimeClickListener {
void onClick(AnimeEntity anime);
}
} }

View File

@ -17,7 +17,7 @@ import java.util.List;
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ItemModel> itemList; private List<ItemModel> itemList;
private String sectionTitle; // Para saber si es Airing o no private String sectionTitle;
private static final int TYPE_AIRING = 0; private static final int TYPE_AIRING = 0;
private static final int TYPE_ANIME_MANGA = 1; private static final int TYPE_ANIME_MANGA = 1;

View File

@ -1,136 +1,162 @@
// MangaAdapter.java actualizado con statusDot dinámico
package com.santiparra.yomitrack.model.adapters.manga_adapter; package com.santiparra.yomitrack.model.adapters.manga_adapter;
import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.santiparra.yomitrack.R; import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.AnimeItem; import com.santiparra.yomitrack.db.entities.MangaEntity;
import java.util.List; import java.util.List;
public class MangaAdapter extends RecyclerView.Adapter<MangaAdapter.MangaViewHolder> { public class MangaAdapter extends RecyclerView.Adapter<MangaAdapter.MangaViewHolder> {
private final List<AnimeItem> mangaList; public static final int VIEW_NORMAL = 0;
private final Context context; public static final int VIEW_COMPACT = 1;
private int viewMode = 0; public static final int VIEW_LARGE = 2;
public interface OnMangaRemoveListener { private List<MangaEntity> mangaList;
void onMangaRemoved(AnimeItem manga); private int viewType;
} private final OnMangaClickListener onEditClick;
private final OnMangaClickListener onLongClick;
private OnMangaRemoveListener removeListener; public MangaAdapter(List<MangaEntity> mangaList, int viewType,
OnMangaClickListener onEditClick,
public void setOnMangaRemoveListener(OnMangaRemoveListener listener) { OnMangaClickListener onLongClick) {
this.removeListener = listener;
}
public void setViewMode(int mode) {
this.viewMode = mode;
}
public MangaAdapter(Context context, List<AnimeItem> mangaList) {
this.context = context;
this.mangaList = mangaList; this.mangaList = mangaList;
this.viewType = viewType;
this.onEditClick = onEditClick;
this.onLongClick = onLongClick;
}
public void setViewType(int viewType) {
this.viewType = viewType;
notifyDataSetChanged();
} }
@NonNull @NonNull
@Override @Override
public MangaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public MangaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
int layoutId; int layout = R.layout.item_manga;
switch (viewMode) { if (viewType == VIEW_COMPACT) layout = R.layout.item_manga_compact;
case 1: else if (viewType == VIEW_LARGE) layout = R.layout.item_manga_large;
layoutId = R.layout.item_anime_large;
break; View view = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
case 2:
layoutId = R.layout.item_anime_compact;
break;
default:
layoutId = R.layout.item_anime;
break;
}
View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
return new MangaViewHolder(view); return new MangaViewHolder(view);
} }
@Override @Override
public void onBindViewHolder(@NonNull MangaViewHolder holder, int position) { public void onBindViewHolder(@NonNull MangaViewHolder holder, int position) {
AnimeItem manga = mangaList.get(position); MangaEntity manga = mangaList.get(position);
if (holder.title != null) String title = manga.getTitle() != null ? manga.getTitle() : "Sin título";
holder.title.setText(manga.getTitle()); String status = manga.getStatus() != null ? manga.getStatus() : "";
String type = manga.getType() != null ? manga.getType() : "";
String imageUrl = manga.getImageUrl();
if (holder.progress != null) if (holder.textTitle != null) holder.textTitle.setText(title);
holder.progress.setText("Progress: " + manga.getWatchedEpisodes() + "/" + manga.getTotalEpisodes());
if (holder.score != null) if (holder.textStatus != null) {
holder.score.setText(String.valueOf(manga.getScore())); String statusText = status + (type.isEmpty() ? "" : "" + type);
holder.textStatus.setText(statusText);
if (holder.type != null)
holder.type.setText(manga.getType());
if (holder.cover != null) {
Glide.with(context)
.load(manga.getImageUrl())
.placeholder(R.drawable.sample_anime_cover)
.into(holder.cover);
} }
if (holder.buttonOptions != null) { if (holder.textProgress != null) {
holder.buttonOptions.setOnClickListener(v -> { holder.textProgress.setText(manga.getProgress() + " caps");
PopupMenu popup = new PopupMenu(context, holder.buttonOptions);
popup.inflate(R.menu.anime_item_menu);
popup.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.action_edit) {
Toast.makeText(context, "Edit: " + manga.getTitle(), Toast.LENGTH_SHORT).show();
return true;
} else if (id == R.id.action_remove) {
int pos = holder.getAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
AnimeItem removed = mangaList.remove(pos);
notifyItemRemoved(pos);
if (removeListener != null) {
removeListener.onMangaRemoved(removed);
}
Toast.makeText(context, "Removed: " + removed.getTitle(), Toast.LENGTH_SHORT).show();
}
return true;
}
return false;
});
popup.show();
});
} }
if (holder.textScore != null) {
holder.textScore.setText("Score: " + manga.getScore());
}
if (holder.textType != null) {
holder.textType.setText("Tipo: " + type);
}
if (holder.imageCover != null && imageUrl != null && !imageUrl.isEmpty()) {
Glide.with(holder.itemView.getContext())
.load(imageUrl)
.placeholder(R.drawable.rectangle_placeholder)
.into(holder.imageCover);
} else if (holder.imageCover != null) {
holder.imageCover.setImageResource(R.drawable.rectangle_placeholder);
}
if (holder.statusDot != null) {
int colorResId;
switch (manga.getStatus()) {
case "Completed":
colorResId = R.color.status_completed;
break;
case "Reading":
colorResId = R.color.status_watching;
break;
case "Paused":
colorResId = R.color.status_paused;
break;
case "Dropped":
colorResId = R.color.status_dropped;
break;
case "Planning":
default:
colorResId = R.color.status_planning;
break;
}
holder.statusDot.setBackgroundTintList(
ContextCompat.getColorStateList(holder.itemView.getContext(), colorResId)
);
}
holder.itemView.setOnClickListener(v -> {
if (onEditClick != null) onEditClick.onClick(manga);
});
holder.itemView.setOnLongClickListener(v -> {
if (onLongClick != null) {
onLongClick.onClick(manga);
return true;
}
return false;
});
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return mangaList.size(); return mangaList != null ? mangaList.size() : 0;
} }
static class MangaViewHolder extends RecyclerView.ViewHolder { @Override
TextView title, progress, score, type; public int getItemViewType(int position) {
ImageView cover, buttonOptions; return viewType;
}
MangaViewHolder(View itemView) { public static class MangaViewHolder extends RecyclerView.ViewHolder {
ImageView imageCover;
TextView textTitle, textStatus, textProgress, textScore, textType;
View statusDot;
public MangaViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
title = itemView.findViewById(R.id.textViewTitle); imageCover = itemView.findViewById(R.id.imageCover);
progress = itemView.findViewById(R.id.textViewProgress); textTitle = itemView.findViewById(R.id.textTitle);
score = itemView.findViewById(R.id.textViewScore); textStatus = itemView.findViewById(R.id.textStatus);
type = itemView.findViewById(R.id.textViewType); textProgress = itemView.findViewById(R.id.textProgress);
cover = itemView.findViewById(R.id.imageViewCover); textScore = itemView.findViewById(R.id.textScore);
buttonOptions = itemView.findViewById(R.id.buttonOptions); textType = itemView.findViewById(R.id.textType);
statusDot = itemView.findViewById(R.id.statusDot);
} }
} }
}
public interface OnMangaClickListener {
void onClick(MangaEntity manga);
}
}

View File

@ -1,8 +1,12 @@
package com.santiparra.yomitrack.ui; package com.santiparra.yomitrack.ui;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.widget.ImageView;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
@ -19,6 +23,7 @@ import com.santiparra.yomitrack.databinding.ActivityMainBinding;
import com.santiparra.yomitrack.ui.fragments.anime_list.FragmentAnime; import com.santiparra.yomitrack.ui.fragments.anime_list.FragmentAnime;
import com.santiparra.yomitrack.ui.fragments.browse.FragmentBrowse; import com.santiparra.yomitrack.ui.fragments.browse.FragmentBrowse;
import com.santiparra.yomitrack.ui.fragments.home.FragmentHome; import com.santiparra.yomitrack.ui.fragments.home.FragmentHome;
import com.santiparra.yomitrack.ui.fragments.login.LoginActivity;
import com.santiparra.yomitrack.ui.fragments.manga_list.FragmentManga; import com.santiparra.yomitrack.ui.fragments.manga_list.FragmentManga;
import com.santiparra.yomitrack.ui.fragments.profile.FragmentProfile; import com.santiparra.yomitrack.ui.fragments.profile.FragmentProfile;
@ -32,29 +37,22 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
/*We initialize the values*/
binding = ActivityMainBinding.inflate(getLayoutInflater()); binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
/*Implementation so that the toolbar does not mess with the status bar*/ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
WindowCompat.setDecorFitsSystemWindows(getWindow(),false);
EdgeToEdge.enable(this); EdgeToEdge.enable(this);
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (view,insets) ->{ ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (view, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
view.setPadding(0,systemBars.top,0,systemBars.bottom); view.setPadding(0, systemBars.top, 0, systemBars.bottom);
return insets; return insets;
}); });
ViewCompat.setOnApplyWindowInsetsListener(binding.bottomNavigationView, (view, insets) -> { ViewCompat.setOnApplyWindowInsetsListener(binding.bottomNavigationView, (view, insets) -> {
int bottomInset = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom; int bottomInset = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
// Aplica padding interno para que los íconos no queden pegados al borde
view.setPadding(0, 0, 0, bottomInset); view.setPadding(0, 0, 0, bottomInset);
// Aplica margen negativo externo para que el fondo se expanda visualmente
ViewGroup.LayoutParams params = view.getLayoutParams(); ViewGroup.LayoutParams params = view.getLayoutParams();
if (params instanceof ViewGroup.MarginLayoutParams) { if (params instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) params).bottomMargin = -bottomInset; ((ViewGroup.MarginLayoutParams) params).bottomMargin = -bottomInset;
@ -63,12 +61,12 @@ public class MainActivity extends AppCompatActivity {
return insets; return insets;
}); });
/*Implementation of bottomNav*/ // Fragmento inicial
replaceFragment(new FragmentHome()); replaceFragment(new FragmentHome());
// Navegación inferior
binding.bottomNavigationView.setOnItemSelectedListener(item -> { binding.bottomNavigationView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId(); int itemId = item.getItemId();
if (itemId == R.id.home) { if (itemId == R.id.home) {
replaceFragment(new FragmentHome()); replaceFragment(new FragmentHome());
} else if (itemId == R.id.profile) { } else if (itemId == R.id.profile) {
@ -80,20 +78,35 @@ public class MainActivity extends AppCompatActivity {
} else if (itemId == R.id.browse) { } else if (itemId == R.id.browse) {
replaceFragment(new FragmentBrowse()); replaceFragment(new FragmentBrowse());
} }
return true; return true;
}); });
/*Implementation of Toolbar*/ // Toolbar
toolbar = findViewById(R.id.toolbar); toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setDisplayShowTitleEnabled(false);
// Botón de perfil
ImageView profileIcon = findViewById(R.id.profileIconToolbar);
profileIcon.setOnClickListener(v -> {
new AlertDialog.Builder(MainActivity.this)
.setTitle("Cerrar sesión")
.setMessage("¿Deseas cerrar sesión?")
.setPositiveButton("", (dialog, which) -> {
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
prefs.edit().remove("current_user_id").apply();
startActivity(new Intent(MainActivity.this, LoginActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
})
.setNegativeButton("Cancelar", null)
.show();
});
} }
private void replaceFragment(Fragment fragment){ private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout, fragment); fragmentTransaction.replace(R.id.frame_layout, fragment);
fragmentTransaction.commit(); fragmentTransaction.commit();
} }
} }

View File

@ -1,5 +1,7 @@
package com.santiparra.yomitrack.ui.fragments.anime_list; package com.santiparra.yomitrack.ui.fragments.anime_list;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -8,172 +10,163 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.santiparra.yomitrack.R; import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.AnimeItem; import com.santiparra.yomitrack.api.ApiClient;
import com.santiparra.yomitrack.api.ApiService;
import com.santiparra.yomitrack.db.entities.AnimeEntity;
import com.santiparra.yomitrack.model.adapters.anime_adapter.AnimeAdapter; import com.santiparra.yomitrack.model.adapters.anime_adapter.AnimeAdapter;
import com.santiparra.yomitrack.ui.fragments.addanime.AddAnimeFragment;
import com.santiparra.yomitrack.ui.fragments.editanime.EditAnimeFragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class FragmentAnime extends Fragment { public class FragmentAnime extends Fragment {
private EditText editSearch;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private EditText editTextFilter;
private TextView textViewTitle;
private AnimeAdapter adapter; private AnimeAdapter adapter;
private ApiService api;
private int userId = 1;
private final List<AnimeItem> fullAnimeList = new ArrayList<>(); private ImageButton btnViewCompact, btnViewNormal, btnViewLarge;
private final List<AnimeItem> filteredAnimeList = new ArrayList<>(); private int currentViewType = AnimeAdapter.VIEW_NORMAL;
private String currentStatus = "Watching"; private List<AnimeEntity> animeList = new ArrayList<>();
private int currentViewMode = 0;
private ImageButton buttonGrid, buttonLarge, buttonList; @Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_alist, container, false);
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public void onViewCreated(@NonNull View view,
View view = inflater.inflate(R.layout.fragment_alist, container, false); @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.recyclerViewAnimeList); recyclerView = view.findViewById(R.id.recyclerViewAnime);
editTextFilter = view.findViewById(R.id.editTextFilter); btnViewCompact = view.findViewById(R.id.btnViewCompact);
textViewTitle = view.findViewById(R.id.textViewWatching); btnViewNormal = view.findViewById(R.id.btnViewNormal);
ImageView buttonFilterMenu = view.findViewById(R.id.buttonFilterMenu); btnViewLarge = view.findViewById(R.id.btnViewLarge);
buttonGrid = view.findViewById(R.id.buttonViewGrid); FloatingActionButton fabAdd = view.findViewById(R.id.fabAddAnime);
buttonLarge = view.findViewById(R.id.buttonViewLarge);
buttonList = view.findViewById(R.id.buttonViewList);
buttonGrid.setOnClickListener(v -> setLayoutMode(0)); editSearch = view.findViewById(R.id.editSearch);
buttonLarge.setOnClickListener(v -> setLayoutMode(1)); editSearch.addTextChangedListener(new TextWatcher() {
buttonList.setOnClickListener(v -> setLayoutMode(2)); @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
initSampleAnimeList();
filteredAnimeList.addAll(fullAnimeList);
adapter = new AnimeAdapter(getContext(), filteredAnimeList);
recyclerView.setAdapter(adapter);
adapter.setOnAnimeRemoveListener(anime -> fullAnimeList.remove(anime));
editTextFilter.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void afterTextChanged(Editable s) {}
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
filterAnimeList(s.toString()); filterAnimeList(s.toString());
} }
@Override
public void afterTextChanged(Editable s) {}
}); });
Map<Integer, String> filterMap = new HashMap<>(); api = ApiClient.getClient().create(ApiService.class);
filterMap.put(R.id.filter_all, "All"); SharedPreferences prefs = requireContext().getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
filterMap.put(R.id.filter_watching, "Watching"); userId = prefs.getInt("current_user_id", -1);
filterMap.put(R.id.filter_planning, "Planning");
filterMap.put(R.id.filter_paused, "Paused");
filterMap.put(R.id.filter_dropped, "Dropped");
filterMap.put(R.id.filter_completed, "Completed");
buttonFilterMenu.setOnClickListener(v -> { // Botones de vista
PopupMenu popup = new PopupMenu(requireContext(), buttonFilterMenu); btnViewCompact.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_COMPACT));
popup.inflate(R.menu.filter_list_menu); btnViewNormal.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_NORMAL));
popup.setOnMenuItemClickListener(item -> { btnViewLarge.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_LARGE));
String selectedStatus = filterMap.get(item.getItemId());
if (selectedStatus != null) {
currentStatus = selectedStatus;
textViewTitle.setText(currentStatus);
filterAnimeList(editTextFilter.getText().toString());
return true;
}
return false;
});
popup.show();
});
setLayoutMode(currentViewMode); fabAdd.setOnClickListener(v -> requireActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout, new AddAnimeFragment())
.addToBackStack(null)
.commit());
return view; fetchAnimeList();
} }
private void filterAnimeList(String query) { private void filterAnimeList(String query) {
filteredAnimeList.clear(); List<AnimeEntity> filtered = new ArrayList<>();
int count = 0; for (AnimeEntity anime : animeList) {
for (AnimeItem anime : fullAnimeList) { if (anime.getTitle().toLowerCase().contains(query.toLowerCase())) {
boolean matchesStatus = currentStatus.equals("All") || anime.getStatus().equalsIgnoreCase(currentStatus); filtered.add(anime);
boolean matchesQuery = anime.getTitle().toLowerCase().contains(query.toLowerCase());
if (matchesStatus && matchesQuery) {
filteredAnimeList.add(anime);
count++;
if (count >= 10) {
Toast.makeText(requireContext(), "Mostrando los primeros 10 resultados", Toast.LENGTH_SHORT).show();
break;
}
} }
} }
adapter.notifyDataSetChanged(); adapter.updateList(filtered);
} }
private void setLayoutMode(int mode) { private void fetchAnimeList() {
currentViewMode = mode; api.getAnimeByUser(userId).enqueue(new Callback<List<AnimeEntity>>() {
@Override
public void onResponse(@NonNull Call<List<AnimeEntity>> call,
@NonNull Response<List<AnimeEntity>> response) {
if (response.isSuccessful() && response.body() != null) {
animeList = response.body();
setViewType(currentViewType);
} else {
Toast.makeText(getContext(), "Error al cargar animes", Toast.LENGTH_SHORT).show();
}
}
if (mode == 2) { // Compacta: solo texto @Override
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); public void onFailure(@NonNull Call<List<AnimeEntity>> call, @NonNull Throwable t) {
} else if (mode == 1) { // Imagen grande + info Toast.makeText(getContext(), "Fallo de conexión", Toast.LENGTH_SHORT).show();
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 1)); }
} else if (mode == 0) { // Lista tipo AniList });
}
private void setViewType(int viewType) {
currentViewType = viewType;
if (viewType == AnimeAdapter.VIEW_LARGE) {
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
} else {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
} }
adapter = new AnimeAdapter(getContext(), filteredAnimeList); adapter = new AnimeAdapter(animeList, viewType,
adapter.setViewMode(mode); this::showEditDialog,
adapter.setOnAnimeRemoveListener(anime -> fullAnimeList.remove(anime)); this::deleteAnime);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
int defaultTint = ContextCompat.getColor(requireContext(), R.color.textPrimary);
int activeTint = ContextCompat.getColor(requireContext(), R.color.activeTint);
buttonGrid.setColorFilter(mode == 0 ? activeTint : defaultTint);
buttonLarge.setColorFilter(mode == 1 ? activeTint : defaultTint);
buttonList.setColorFilter(mode == 2 ? activeTint : defaultTint);
} }
private void initSampleAnimeList() { private void showEditDialog(AnimeEntity anime) {
fullAnimeList.clear(); requireActivity().getSupportFragmentManager()
fullAnimeList.add(new AnimeItem("Aharen-san wa Hakarenai Season 2", "https://cdn.example.com/img1.jpg", 5, 12, 8.5, "TV", "Watching")); .beginTransaction()
fullAnimeList.add(new AnimeItem("Anne Shirley", "https://cdn.example.com/img2.jpg", 5, 24, 7.9, "OVA", "Planning")); .replace(R.id.frame_layout, new EditAnimeFragment(anime))
fullAnimeList.add(new AnimeItem("Ballpark de Tsukamaete!", "https://cdn.example.com/img3.jpg", 5, 12, 9.2, "Movie", "Paused")); .addToBackStack(null)
fullAnimeList.add(new AnimeItem("Bleach: TYBW", "https://cdn.example.com/img4.jpg", 8, 13, 9.0, "TV", "Completed")); .commit();
fullAnimeList.add(new AnimeItem("Chainsaw Man", "https://cdn.example.com/img5.jpg", 1, 12, 8.8, "TV", "Dropped"));
fullAnimeList.add(new AnimeItem("Code Geass", "https://cdn.example.com/img6.jpg", 25, 25, 9.5, "TV", "Completed"));
fullAnimeList.add(new AnimeItem("Death Note", "https://cdn.example.com/img7.jpg", 37, 37, 9.6, "TV", "Completed"));
fullAnimeList.add(new AnimeItem("Erased", "https://cdn.example.com/img8.jpg", 12, 12, 9.0, "TV", "Watching"));
fullAnimeList.add(new AnimeItem("Fate/Zero", "https://cdn.example.com/img9.jpg", 13, 13, 9.1, "TV", "Planning"));
fullAnimeList.add(new AnimeItem("Gintama", "https://cdn.example.com/img10.jpg", 201, 201, 9.7, "TV", "Paused"));
} }
@Override private void deleteAnime(AnimeEntity anime) {
public void onSaveInstanceState(@NonNull Bundle outState) { api.deleteAnime(anime.getId()).enqueue(new Callback<String>() {
super.onSaveInstanceState(outState); @Override
outState.putString("currentStatus", currentStatus); public void onResponse(Call<String> call, Response<String> response) {
} if (response.isSuccessful()) {
Toast.makeText(getContext(), "Anime eliminado", Toast.LENGTH_SHORT).show();
fetchAnimeList();
} else {
Toast.makeText(getContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
}
}
@Override @Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) { public void onFailure(Call<String> call, Throwable t) {
super.onViewStateRestored(savedInstanceState); Toast.makeText(getContext(), "Fallo de conexión", Toast.LENGTH_SHORT).show();
if (savedInstanceState != null) { }
currentStatus = savedInstanceState.getString("currentStatus", "Watching"); });
textViewTitle.setText(currentStatus);
filterAnimeList(editTextFilter.getText().toString());
}
} }
} }

View File

@ -1,18 +1,190 @@
package com.santiparra.yomitrack.ui.fragments.browse; package com.santiparra.yomitrack.ui.fragments.browse;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.text.Editable;
import android.text.TextWatcher;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.santiparra.yomitrack.R; import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.ItemModel;
import com.santiparra.yomitrack.model.adapters.browser_section_adapter.BrowseGridAdapter;
import java.util.ArrayList;
import java.util.List;
public class FragmentBrowse extends Fragment { public class FragmentBrowse extends Fragment {
@Override
public View onCreateView(LayoutInflater layoutInflater, ViewGroup container, private Spinner spinnerType;
Bundle saveInstanceState){ private EditText editTextSearch;
return layoutInflater.inflate(R.layout.fragment_browse,container,false); private LinearLayout sectionContainer;
private final List<ItemModel> animeList = new ArrayList<>();
private final List<ItemModel> mangaList = new ArrayList<>();
public FragmentBrowse() {
super(R.layout.fragment_browse);
} }
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
spinnerType = view.findViewById(R.id.spinnerType);
editTextSearch = view.findViewById(R.id.editTextSearch);
sectionContainer = view.findViewById(R.id.sectionContainer);
View statusBarSpacer = view.findViewById(R.id.statusBarSpacer);
int statusBarHeight = getStatusBarHeight();
ViewGroup.LayoutParams params = statusBarSpacer.getLayoutParams();
params.height = statusBarHeight;
statusBarSpacer.setLayoutParams(params);
Spinner spinnerType = view.findViewById(R.id.spinnerType);
ArrayAdapter<CharSequence> spinnerAdapter = ArrayAdapter.createFromResource(
requireContext(), R.array.media_types, R.layout.item_spinner);
spinnerAdapter.setDropDownViewResource(R.layout.item_spinner);
spinnerType.setAdapter(spinnerAdapter);
loadSampleData();
showSections(animeList);
spinnerType.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
String selected = spinnerType.getSelectedItem().toString();
if (selected.equals("Anime")) {
showSections(animeList);
} else {
showSections(mangaList);
}
}
@Override
public void onNothingSelected(android.widget.AdapterView<?> parent) {
}
});
editTextSearch.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String selected = spinnerType.getSelectedItem().toString();
if (selected.equals("Anime")) {
showSections(animeList, s.toString());
} else {
showSections(mangaList, s.toString());
}
}
});
}
private void showSections(List<ItemModel> source) {
showSections(source, "");
}
private void showSections(List<ItemModel> source, String query) {
sectionContainer.removeAllViews();
List<ItemModel> trending = new ArrayList<>();
List<ItemModel> popular = new ArrayList<>();
for (int i = 0; i < source.size(); i++) {
ItemModel item = source.get(i);
if (!query.isEmpty() && !item.getTitle().toLowerCase().contains(query.toLowerCase())) {
continue;
}
if (i % 2 == 0) trending.add(item);
else popular.add(item);
}
if (!trending.isEmpty()) {
addSection("Trending Now", trending);
}
if (!popular.isEmpty()) {
addSection("Popular This Season", popular);
}
}
private void addSection(String title, List<ItemModel> items) {
Context context = requireContext();
LinearLayout sectionLayout = new LinearLayout(context);
sectionLayout.setOrientation(LinearLayout.VERTICAL);
sectionLayout.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
sectionLayout.setPadding(0, 0, 0, 24);
TextView titleView = new TextView(context);
titleView.setText(title);
titleView.setTextSize(18);
titleView.setTypeface(null, Typeface.BOLD);
titleView.setTextColor(ContextCompat.getColor(context, R.color.textPrimary));
titleView.setPadding(0, 0, 0, 8);
sectionLayout.addView(titleView);
RecyclerView recyclerView = new RecyclerView(context);
// 🔥 Establecer altura fija para 2 filas (170dp + 12sp de texto aprox + márgenes)
int itemHeightPx = (int) (170 * context.getResources().getDisplayMetrics().density);
int textHeightPx = (int) (40 * context.getResources().getDisplayMetrics().density);
int totalHeight = (itemHeightPx + textHeightPx + 45) * 2;
recyclerView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, totalHeight)); // <- altura calculada
GridLayoutManager layoutManager = new GridLayoutManager(context, 2, RecyclerView.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(new BrowseGridAdapter(items));
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
recyclerView.setClipToPadding(false);
sectionLayout.addView(recyclerView);
sectionContainer.addView(sectionLayout);
}
private void loadSampleData() {
animeList.clear();
mangaList.clear();
for (int i = 1; i <= 20; i++) {
animeList.add(new ItemModel("Anime " + i, i + "/24", "https://example.com/anime" + i + ".jpg", ItemModel.ContentType.ANIME));
mangaList.add(new ItemModel("Manga " + i, i + "/120", "https://example.com/manga" + i + ".jpg", ItemModel.ContentType.MANGA));
}
}
private int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
} }

View File

@ -22,6 +22,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/**
* Fragmento principal que muestra secciones de anime/manga y actividad reciente.
*/
public class FragmentHome extends Fragment { public class FragmentHome extends Fragment {
private final List<RecentActivityModel> fullRecentActivity = new ArrayList<>(); private final List<RecentActivityModel> fullRecentActivity = new ArrayList<>();
@ -36,13 +39,14 @@ public class FragmentHome extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
// Secciones principales // region Secciones de contenido (Airing, En progreso...)
RecyclerView mainRecyclerView = view.findViewById(R.id.mainRecyclerView); RecyclerView mainRecyclerView = view.findViewById(R.id.mainRecyclerView);
mainRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); mainRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
List<String> sectionTitles = Arrays.asList("Airing", "Anime in Progress", "Manga in Progress"); List<String> sectionTitles = Arrays.asList("Airing", "Anime in Progress", "Manga in Progress");
Map<String, List<ItemModel>> sectionItems = new HashMap<>(); Map<String, List<ItemModel>> sectionItems = new HashMap<>();
// Datos simulados de ejemplo
sectionItems.put("Airing", Arrays.asList( sectionItems.put("Airing", Arrays.asList(
new ItemModel("Naruto", "5/220", "https://i.imgur.com/qzWZbL2.jpg", ItemModel.ContentType.ANIME), new ItemModel("Naruto", "5/220", "https://i.imgur.com/qzWZbL2.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Bleach", "100/366", "https://i.imgur.com/I0d1HyA.jpg", ItemModel.ContentType.ANIME), new ItemModel("Bleach", "100/366", "https://i.imgur.com/I0d1HyA.jpg", ItemModel.ContentType.ANIME),
@ -55,40 +59,26 @@ public class FragmentHome extends Fragment {
sectionItems.put("Anime in Progress", Arrays.asList( sectionItems.put("Anime in Progress", Arrays.asList(
new ItemModel("Attack on Titan", "16/25", "https://i.imgur.com/z4d4kWk.jpg", ItemModel.ContentType.ANIME), new ItemModel("Attack on Titan", "16/25", "https://i.imgur.com/z4d4kWk.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Jujutsu Kaisen", "10/24", "https://i.imgur.com/lWhD6Zc.jpg", ItemModel.ContentType.ANIME), new ItemModel("Jujutsu Kaisen", "10/24", "https://i.imgur.com/lWhD6Zc.jpg", ItemModel.ContentType.ANIME),
new ItemModel("One Piece", "900/1100", "https://i.imgur.com/VgVfG6K.jpg", ItemModel.ContentType.ANIME), new ItemModel("One Piece", "900/1100", "https://i.imgur.com/VgVfG6K.jpg", ItemModel.ContentType.ANIME)
new ItemModel("Boruto", "10/100", "https://i.imgur.com/lWhD6Zc.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Dragon Ball", "80/150", "https://i.imgur.com/z4d4kWk.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Another", "2/12", "https://i.imgur.com/z4d4kWk.jpg", ItemModel.ContentType.ANIME)
)); ));
sectionItems.put("Manga in Progress", Arrays.asList( sectionItems.put("Manga in Progress", Arrays.asList(
new ItemModel("Chainsaw Man", "45/100", "https://i.imgur.com/7tZ0h8R.jpg", ItemModel.ContentType.MANGA), new ItemModel("Chainsaw Man", "45/100", "https://i.imgur.com/7tZ0h8R.jpg", ItemModel.ContentType.MANGA),
new ItemModel("Berserk", "370/380", "https://i.imgur.com/8FJYYHo.jpg", ItemModel.ContentType.MANGA), new ItemModel("Berserk", "370/380", "https://i.imgur.com/8FJYYHo.jpg", ItemModel.ContentType.MANGA)
new ItemModel("One Piece", "900/1100", "https://i.imgur.com/VgVfG6K.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Boruto", "10/100", "https://i.imgur.com/lWhD6Zc.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Dragon Ball", "80/150", "https://i.imgur.com/z4d4kWk.jpg", ItemModel.ContentType.ANIME),
new ItemModel("Another", "2/12", "https://i.imgur.com/z4d4kWk.jpg", ItemModel.ContentType.ANIME)
)); ));
SectionAdapter sectionAdapter = new SectionAdapter(sectionTitles, sectionItems); SectionAdapter sectionAdapter = new SectionAdapter(sectionTitles, sectionItems);
mainRecyclerView.setAdapter(sectionAdapter); mainRecyclerView.setAdapter(sectionAdapter);
// endregion
// Actividades recientes // region Actividad reciente
RecyclerView activityRecyclerView = view.findViewById(R.id.activityRecyclerView); RecyclerView activityRecyclerView = view.findViewById(R.id.activityRecyclerView);
activityRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); activityRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
fullRecentActivity.addAll(Arrays.asList( fullRecentActivity.addAll(Arrays.asList(
new RecentActivityModel("Midca", "Read chapters 1 - 60 of", "Choujun! Choujou Senpai", "4 minutes ago", "https://i.imgur.com/7tZ0h8R.jpg"), new RecentActivityModel("Midca", "Read chapters 1 - 60 of", "Choujun! Choujou Senpai", "4 minutes ago", "https://i.imgur.com/7tZ0h8R.jpg"),
new RecentActivityModel("prtrncyon", "Scored 9/10 on", "Chainsaw Man", "12 hours ago", "https://cdn.myanimelist.net/images/manga/2/253146.jpg"), new RecentActivityModel("prtrncyon", "Scored 9/10 on", "Chainsaw Man", "12 hours ago", "https://cdn.myanimelist.net/images/manga/2/253146.jpg")
new RecentActivityModel("BtwIsSanti", "Watched episode 7 of", "Fate/Zero", "1 day ago", "https://cdn.myanimelist.net/images/anime/5/73245.jpg"), // ... más datos simulados aquí
new RecentActivityModel("Taku", "Watched episode 3 of", "Mob Psycho 100", "2 days ago", "https://i.imgur.com/I0d1HyA.jpg"),
new RecentActivityModel("Maki", "Read chapter 14 of", "Blue Period", "4 days ago", "https://i.imgur.com/z4d4kWk.jpg"),
new RecentActivityModel("Yato", "Scored 10/10 on", "Noragami", "5 days ago", "https://i.imgur.com/8FJYYHo.jpg"),
new RecentActivityModel("Eri", "Completed", "My Dress-Up Darling", "6 days ago", "https://i.imgur.com/qzWZbL2.jpg"),
new RecentActivityModel("Yuji", "Watched episode 6 of", "Jujutsu Kaisen", "7 days ago", "https://i.imgur.com/lWhD6Zc.jpg"),
new RecentActivityModel("Light", "Scored 10/10 on", "Death Note", "8 days ago", "https://i.imgur.com/I0d1HyA.jpg"),
new RecentActivityModel("Luffy", "Watched episode 1000 of", "One Piece", "9 days ago", "https://i.imgur.com/VgVfG6K.jpg"),
new RecentActivityModel("Gon", "Started watching", "Hunter x Hunter", "10 days ago", "https://i.imgur.com/z4d4kWk.jpg")
)); ));
visibleRecentActivity.addAll(fullRecentActivity.subList(0, Math.min(10, fullRecentActivity.size()))); visibleRecentActivity.addAll(fullRecentActivity.subList(0, Math.min(10, fullRecentActivity.size())));
@ -98,6 +88,7 @@ public class FragmentHome extends Fragment {
Button buttonShowMore = view.findViewById(R.id.buttonShowMoreActivity); Button buttonShowMore = view.findViewById(R.id.buttonShowMoreActivity);
Button buttonShowLess = view.findViewById(R.id.buttonShowLessActivity); Button buttonShowLess = view.findViewById(R.id.buttonShowLessActivity);
// Mostrar más/menos actividad
if (fullRecentActivity.size() > 10) { if (fullRecentActivity.size() > 10) {
buttonShowMore.setVisibility(View.VISIBLE); buttonShowMore.setVisibility(View.VISIBLE);
buttonShowLess.setVisibility(View.GONE); buttonShowLess.setVisibility(View.GONE);
@ -121,5 +112,6 @@ public class FragmentHome extends Fragment {
buttonShowMore.setVisibility(View.GONE); buttonShowMore.setVisibility(View.GONE);
buttonShowLess.setVisibility(View.GONE); buttonShowLess.setVisibility(View.GONE);
} }
// endregion
} }
} }

View File

@ -1,174 +1,115 @@
package com.santiparra.yomitrack.ui.fragments.manga_list; package com.santiparra.yomitrack.ui.fragments.manga_list;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.santiparra.yomitrack.R; import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.AnimeItem; import com.santiparra.yomitrack.api.ApiClient;
import com.santiparra.yomitrack.api.ApiService;
import com.santiparra.yomitrack.db.entities.MangaEntity;
import com.santiparra.yomitrack.model.adapters.manga_adapter.MangaAdapter; import com.santiparra.yomitrack.model.adapters.manga_adapter.MangaAdapter;
import com.santiparra.yomitrack.ui.fragments.addmanga.AddMangaFragment;
import com.santiparra.yomitrack.ui.fragments.editmanga.EditMangaFragment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class FragmentManga extends Fragment { public class FragmentManga extends Fragment {
private RecyclerView recyclerView; private RecyclerView recyclerView;
private EditText editTextFilter;
private TextView textViewTitle;
private MangaAdapter adapter; private MangaAdapter adapter;
private ApiService api;
private int currentViewType = MangaAdapter.VIEW_NORMAL;
private int userId = 1; // Deberías usar SharedPreferences si tienes login
private final List<AnimeItem> fullMangaList = new ArrayList<>(); @Nullable
private final List<AnimeItem> filteredMangaList = new ArrayList<>();
private String currentStatus = "Reading";
private int currentViewMode = 0;
private ImageButton buttonGrid, buttonLarge, buttonList;
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_alist, container, false); View view = inflater.inflate(R.layout.fragment_mlist, container, false);
recyclerView = view.findViewById(R.id.recyclerViewAnimeList); recyclerView = view.findViewById(R.id.recyclerViewManga);
editTextFilter = view.findViewById(R.id.editTextFilter); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
textViewTitle = view.findViewById(R.id.textViewWatching);
ImageView buttonFilterMenu = view.findViewById(R.id.buttonFilterMenu);
buttonGrid = view.findViewById(R.id.buttonViewGrid);
buttonLarge = view.findViewById(R.id.buttonViewLarge);
buttonList = view.findViewById(R.id.buttonViewList);
buttonGrid.setOnClickListener(v -> setLayoutMode(0)); ImageView changeViewButton = view.findViewById(R.id.buttonChangeViewType);
buttonLarge.setOnClickListener(v -> setLayoutMode(1)); FloatingActionButton fabAdd = view.findViewById(R.id.fabAddManga);
buttonList.setOnClickListener(v -> setLayoutMode(2));
initSampleMangaList(); api = ApiClient.getClient().create(ApiService.class);
filteredMangaList.addAll(fullMangaList); changeViewButton.setOnClickListener(v -> {
adapter = new MangaAdapter(getContext(), filteredMangaList); currentViewType = (currentViewType + 1) % 3;
recyclerView.setAdapter(adapter); if (adapter != null) adapter.setViewType(currentViewType);
adapter.setOnMangaRemoveListener(manga -> fullMangaList.remove(manga));
editTextFilter.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void afterTextChanged(Editable s) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
filterMangaList(s.toString());
}
}); });
Map<Integer, String> filterMap = new HashMap<>(); fabAdd.setOnClickListener(v -> requireActivity().getSupportFragmentManager()
filterMap.put(R.id.filter_all, "All"); .beginTransaction()
filterMap.put(R.id.filter_watching, "Reading"); .replace(R.id.frame_layout, new AddMangaFragment())
filterMap.put(R.id.filter_planning, "Planning"); .addToBackStack(null)
filterMap.put(R.id.filter_paused, "Paused"); .commit());
filterMap.put(R.id.filter_dropped, "Dropped");
filterMap.put(R.id.filter_completed, "Completed");
buttonFilterMenu.setOnClickListener(v -> { loadMangaList();
PopupMenu popup = new PopupMenu(requireContext(), buttonFilterMenu);
popup.inflate(R.menu.filter_list_menu);
popup.setOnMenuItemClickListener(item -> {
String selectedStatus = filterMap.get(item.getItemId());
if (selectedStatus != null) {
currentStatus = selectedStatus;
textViewTitle.setText(currentStatus);
filterMangaList(editTextFilter.getText().toString());
return true;
}
return false;
});
popup.show();
});
setLayoutMode(currentViewMode);
return view; return view;
} }
private void filterMangaList(String query) { private void loadMangaList() {
filteredMangaList.clear(); api.getMangaByUser(userId).enqueue(new Callback<List<MangaEntity>>() {
int count = 0; @Override
for (AnimeItem manga : fullMangaList) { public void onResponse(Call<List<MangaEntity>> call, Response<List<MangaEntity>> response) {
boolean matchesStatus = currentStatus.equals("All") || manga.getStatus().equalsIgnoreCase(currentStatus); if (response.isSuccessful() && response.body() != null) {
boolean matchesQuery = manga.getTitle().toLowerCase().contains(query.toLowerCase()); adapter = new MangaAdapter(response.body(), currentViewType,
if (matchesStatus && matchesQuery) { FragmentManga.this::showEditDialog,
filteredMangaList.add(manga); FragmentManga.this::deleteManga);
count++; recyclerView.setAdapter(adapter);
if (count >= 10) { } else {
Toast.makeText(requireContext(), "Mostrando los primeros 10 resultados", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Error al cargar la lista", Toast.LENGTH_SHORT).show();
break;
} }
} }
}
adapter.notifyDataSetChanged(); @Override
public void onFailure(Call<List<MangaEntity>> call, Throwable t) {
Toast.makeText(getContext(), "Fallo en la conexión", Toast.LENGTH_SHORT).show();
}
});
} }
private void setLayoutMode(int mode) { private void showEditDialog(MangaEntity manga) {
currentViewMode = mode; requireActivity().getSupportFragmentManager()
.beginTransaction()
if (mode == 2) { .replace(R.id.frame_layout, new EditMangaFragment(manga))
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); .addToBackStack(null)
} else if (mode == 1) { .commit();
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 1));
} else if (mode == 0) {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
adapter = new MangaAdapter(getContext(), filteredMangaList);
adapter.setViewMode(mode);
adapter.setOnMangaRemoveListener(manga -> fullMangaList.remove(manga));
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
int defaultTint = ContextCompat.getColor(requireContext(), R.color.textPrimary);
int activeTint = ContextCompat.getColor(requireContext(), R.color.activeTint);
buttonGrid.setColorFilter(mode == 0 ? activeTint : defaultTint);
buttonLarge.setColorFilter(mode == 1 ? activeTint : defaultTint);
buttonList.setColorFilter(mode == 2 ? activeTint : defaultTint);
} }
private void initSampleMangaList() { private void deleteManga(MangaEntity manga) {
fullMangaList.clear(); api.deleteManga(manga.getId()).enqueue(new Callback<String>() {
fullMangaList.add(new AnimeItem("Chainsaw Man", "https://cdn.example.com/img1.jpg", 45, 100, 8.5, "Manga", "Reading")); @Override
fullMangaList.add(new AnimeItem("Berserk", "https://cdn.example.com/img2.jpg", 370, 380, 9.4, "Manga", "Paused")); public void onResponse(Call<String> call, Response<String> response) {
fullMangaList.add(new AnimeItem("One Piece", "https://cdn.example.com/img3.jpg", 1090, 1200, 9.8, "Manga", "Reading")); if (response.isSuccessful()) {
fullMangaList.add(new AnimeItem("Attack on Titan", "https://cdn.example.com/img4.jpg", 139, 139, 9.5, "Manga", "Completed")); Toast.makeText(getContext(), "Manga eliminado", Toast.LENGTH_SHORT).show();
fullMangaList.add(new AnimeItem("Solo Leveling", "https://cdn.example.com/img5.jpg", 179, 179, 8.9, "Manhwa", "Completed")); loadMangaList();
} } else {
Toast.makeText(getContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
}
}
@Override @Override
public void onSaveInstanceState(@NonNull Bundle outState) { public void onFailure(Call<String> call, Throwable t) {
super.onSaveInstanceState(outState); Toast.makeText(getContext(), "Fallo en la conexión", Toast.LENGTH_SHORT).show();
outState.putString("currentStatus", currentStatus); }
} });
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
currentStatus = savedInstanceState.getString("currentStatus", "Reading");
textViewTitle.setText(currentStatus);
filterMangaList(editTextFilter.getText().toString());
}
} }
} }

View File

@ -1,166 +1,140 @@
package com.santiparra.yomitrack.ui.fragments.profile; package com.santiparra.yomitrack.ui.fragments.profile;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.santiparra.yomitrack.R; import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.UserStats; import com.santiparra.yomitrack.api.ApiClient;
import com.santiparra.yomitrack.utils.StatsHelper; import com.santiparra.yomitrack.api.ApiService;
import com.santiparra.yomitrack.db.entities.AnimeEntity;
import com.santiparra.yomitrack.db.entities.MangaEntity;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class FragmentProfile extends Fragment { public class FragmentProfile extends Fragment {
public FragmentProfile() { private TextView textUsername;
} private LinearLayout animeStatsContainer, mangaStatsContainer;
private ApiService api;
private int userId;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_profile, container, false);
return inflater.inflate(R.layout.fragment_profile, container, false);
textUsername = view.findViewById(R.id.usernameText);
animeStatsContainer = view.findViewById(R.id.animeStatsContainer);
mangaStatsContainer = view.findViewById(R.id.mangaStatsContainer);
api = ApiClient.getClient().create(ApiService.class);
SharedPreferences prefs = requireContext().getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
userId = prefs.getInt("current_user_id", -1);
if (userId == -1) {
textUsername.setText("Invitado");
Toast.makeText(getContext(), "Estadísticas no disponibles en modo invitado", Toast.LENGTH_SHORT).show();
return view;
}
textUsername.setText("Usuario #" + userId);
loadStats();
return view;
} }
@Override private void loadStats() {
public void onViewCreated(View view, Bundle savedInstanceState) { animeStatsContainer.removeAllViews();
super.onViewCreated(view, savedInstanceState); mangaStatsContainer.removeAllViews();
TextView usernameText = view.findViewById(R.id.usernameText); api.getAnimeByUser(userId).enqueue(new Callback<List<AnimeEntity>>() {
TextView descriptionText = view.findViewById(R.id.profileDescriptionText); @Override
TextView seeMore = view.findViewById(R.id.textViewSeeMore); public void onResponse(Call<List<AnimeEntity>> call, Response<List<AnimeEntity>> response) {
LinearLayout statsContainer = view.findViewById(R.id.animeStatsContainer); if (response.isSuccessful() && response.body() != null) {
showStats(animeStatsContainer, "Anime", countByStatus(response.body()));
}
}
usernameText.setText("BtwIsSanti"); @Override
descriptionText.setText("Eiko is my waifu right now. Mai and Mikasa is my second wife. Long live anime."); public void onFailure(Call<List<AnimeEntity>> call, Throwable t) {
Toast.makeText(getContext(), "Error al cargar anime", Toast.LENGTH_SHORT).show();
seeMore.setOnClickListener(v -> {
if (descriptionText.getMaxLines() == 2) {
descriptionText.setMaxLines(Integer.MAX_VALUE);
descriptionText.setEllipsize(null);
seeMore.setText("Ver menos");
} else {
descriptionText.setMaxLines(2);
descriptionText.setEllipsize(TextUtils.TruncateAt.END);
seeMore.setText("Ver más");
} }
}); });
// Cargar estadísticas simuladas api.getMangaByUser(userId).enqueue(new Callback<List<MangaEntity>>() {
List<UserStats> statsList = StatsHelper.getAnimeStats(); @Override
public void onResponse(Call<List<MangaEntity>> call, Response<List<MangaEntity>> response) {
for (UserStats stat : statsList) { if (response.isSuccessful() && response.body() != null) {
View statView = LayoutInflater.from(getContext()) showStats(mangaStatsContainer, "Manga", countByStatus(response.body()));
.inflate(R.layout.item_stat_bar, statsContainer, false); }
TextView label = statView.findViewById(R.id.statLabelFull);
ProgressBar bar = statView.findViewById(R.id.statProgressBar);
label.setText(stat.getCategory() + "" + stat.getCount());
bar.setProgress(stat.getPercentage());
// Aplicar color distinto por categoría
int colorRes = R.color.primary; // default
switch (stat.getCategory()) {
case "Watching":
colorRes = R.color.statWatching;
break;
case "Completed":
colorRes = R.color.statCompleted;
break;
case "On Hold":
colorRes = R.color.statOnHold;
break;
case "Dropped":
colorRes = R.color.statDropped;
break;
case "Plan to Watch":
colorRes = R.color.statPlanToWatch;
break;
} }
bar.setProgressTintList(ContextCompat.getColorStateList(requireContext(), colorRes)); @Override
statsContainer.addView(statView); public void onFailure(Call<List<MangaEntity>> call, Throwable t) {
Toast.makeText(getContext(), "Error al cargar manga", Toast.LENGTH_SHORT).show();
}
});
}
private Map<String, Integer> countByStatus(List<?> items) {
Map<String, Integer> counts = new HashMap<>();
for (Object item : items) {
String status = "";
if (item instanceof AnimeEntity) {
status = ((AnimeEntity) item).getStatus();
} else if (item instanceof MangaEntity) {
status = ((MangaEntity) item).getStatus();
}
counts.put(status, counts.getOrDefault(status, 0) + 1);
} }
return counts;
}
// Agregar tarjetas de actividad (opcional) private void showStats(LinearLayout container, String category, Map<String, Integer> data) {
LinearLayout activityContainer = view.findViewById(R.id.activityContainer); TextView title = new TextView(getContext());
title.setText(category.toUpperCase());
title.setTextSize(18);
title.setPadding(0, 24, 0, 12);
container.addView(title);
if (activityContainer != null) { int total = 0;
// Lista simulada de updates for (int count : data.values()) total += count;
String[] activities = {
"Watched episode 5 of Kakushite! Makina-san!!",
"Watched episode 5 of Chotto dake Ai ga Omo...",
"Watched episode 4 of Go-Toubun no Hanayome",
"Watched episode 3 of Kanojo, Okarishimasu",
"Watched episode 12 of Jujutsu Kaisen",
"Watched episode 8 of Bleach: TYBW",
"Watched episode 1 of Chainsaw Man",
"Watched episode 10 of Dr. Stone",
"Watched episode 9 of Mushoku Tensei",
"Watched episode 11 of Ousama Ranking",
"Watched episode 7 of HUNTER×HUNTER"
};
int limit = Math.min(10, activities.length); for (Map.Entry<String, Integer> entry : data.entrySet()) {
String status = entry.getKey();
int count = entry.getValue();
int percent = (int) ((count / (float) total) * 100);
for (int i = 0; i < limit; i++) { TextView label = new TextView(getContext());
View card = LayoutInflater.from(getContext()) label.setText(status + ": " + count + " (" + percent + "%)");
.inflate(R.layout.item_activity, activityContainer, false); label.setTextSize(16);
container.addView(label);
TextView text = card.findViewById(R.id.activityText); ProgressBar progress = new ProgressBar(getContext(), null, android.R.attr.progressBarStyleHorizontal);
TextView time = card.findViewById(R.id.activityTime); progress.setMax(100);
progress.setProgress(percent);
text.setText(activities[i]); progress.setLayoutParams(new LinearLayout.LayoutParams(
time.setText("16 hours ago"); ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
activityContainer.addView(card); ));
} container.addView(progress);
}
// Estadísticas de manga
LinearLayout mangaStatsContainer = view.findViewById(R.id.mangaStatsContainer);
List<UserStats> mangaStats = StatsHelper.getMangaStats();
for (UserStats stat : mangaStats) {
View statView = LayoutInflater.from(getContext())
.inflate(R.layout.item_stat_bar, mangaStatsContainer, false);
TextView label = statView.findViewById(R.id.statLabelFull);
ProgressBar bar = statView.findViewById(R.id.statProgressBar);
label.setText(stat.getCategory() + "" + stat.getCount());
bar.setProgress(stat.getPercentage());
int colorRes = R.color.primary;
switch (stat.getCategory()) {
case "Reading":
colorRes = R.color.statWatching;
break;
case "Completed":
colorRes = R.color.statCompleted;
break;
case "On Hold":
colorRes = R.color.statOnHold;
break;
case "Dropped":
colorRes = R.color.statDropped;
break;
case "Plan to Read":
colorRes = R.color.statPlanToWatch;
break;
}
bar.setProgressTintList(ContextCompat.getColorStateList(requireContext(), colorRes));
mangaStatsContainer.addView(statView);
} }
} }
} }

View File

@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="@color/primary" /> <!-- Fondo oscuro --> <solid android:color="@color/textPrimary" /> <!-- Fondo oscuro -->
<corners android:radius="12dp" /> <!-- Bordes redondeados --> <corners android:radius="12dp" /> <!-- Bordes redondeados -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 B

View File

@ -8,18 +8,17 @@
android:background="@color/background" android:background="@color/background"
tools:context=".ui.MainActivity"> tools:context=".ui.MainActivity">
<!-- Contenedor para los fragments --> <!-- Contenedor de fragments -->
<FrameLayout <FrameLayout
android:id="@+id/frame_layout" android:id="@+id/frame_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<!-- Toolbar personalizada --> <!-- Toolbar superior -->
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="85dp" android:layout_height="85dp"
android:layout_marginTop="0dp"
android:background="@color/primary"> android:background="@color/primary">
<RelativeLayout <RelativeLayout
@ -28,7 +27,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp"> android:paddingEnd="16dp">
<!-- Logo (izquierda) --> <!-- Logo -->
<ImageView <ImageView
android:id="@+id/logoImage" android:id="@+id/logoImage"
android:layout_width="100dp" android:layout_width="100dp"
@ -38,7 +37,7 @@
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/logo_yomitrack" /> android:src="@drawable/logo_yomitrack" />
<!-- Icono de perfil en la AppBar (circular) --> <!-- Icono de perfil -->
<ImageView <ImageView
android:id="@+id/profileIconToolbar" android:id="@+id/profileIconToolbar"
android:layout_width="48dp" android:layout_width="48dp"
@ -53,7 +52,7 @@
</RelativeLayout> </RelativeLayout>
</androidx.appcompat.widget.Toolbar> </androidx.appcompat.widget.Toolbar>
<!-- BottomNavigationView (fuera del BottomAppBar) --> <!-- Navegación inferior -->
<com.google.android.material.bottomnavigation.BottomNavigationView <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView" android:id="@+id/bottomNavigationView"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,224 +1,143 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@color/background"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="@color/background"
android:fitsSystemWindows="true"
tools:context=".ui.fragments.anime_list.FragmentAnime">
<!-- Parte superior: imagen de fondo + avatar + nombre --> <androidx.constraintlayout.widget.ConstraintLayout
<RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="match_parent">
android:background="@drawable/sample_cover"
android:padding="12dp">
<ImageView <!-- Banner -->
android:id="@+id/imageViewBanner" <RelativeLayout
android:layout_width="match_parent" android:id="@+id/bannerLayout"
android:layout_height="match_parent" android:layout_width="0dp"
android:scaleType="centerCrop" android:layout_height="120dp"
android:src="@drawable/sample_cover" android:background="@drawable/sample_cover"
android:contentDescription="Profile background" /> android:padding="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout <ImageView
android:layout_width="match_parent" android:id="@+id/imageViewBanner"
android:layout_height="match_parent" android:layout_width="match_parent"
android:orientation="horizontal" android:layout_height="match_parent"
android:gravity="bottom" android:scaleType="centerCrop"
android:paddingBottom="16dp"> android:src="@drawable/sample_cover"
android:contentDescription="Banner" />
<ImageView <ImageView
android:id="@+id/imageViewAvatar" android:id="@+id/imageViewAvatar"
android:layout_width="64dp" android:layout_width="48dp"
android:layout_height="64dp" android:layout_height="48dp"
android:layout_marginEnd="12dp" android:layout_alignParentBottom="true"
android:layout_marginStart="12dp"
android:layout_marginBottom="12dp"
android:src="@drawable/ic_profile" android:src="@drawable/ic_profile"
android:background="@drawable/circle_mask" android:background="@drawable/circle_mask"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:clipToOutline="true" android:clipToOutline="true"
android:contentDescription="Profile picture" /> android:contentDescription="Avatar" />
<TextView <TextView
android:id="@+id/textViewUsername" android:id="@+id/textViewUsername"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="BtwIsSanti" android:layout_toEndOf="@id/imageViewAvatar"
android:layout_alignBottom="@id/imageViewAvatar"
android:layout_marginStart="12dp"
android:text="Usuario"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textStyle="bold" android:textStyle="bold"
android:textSize="20sp" android:textSize="18sp" />
android:gravity="center_vertical"/> </RelativeLayout>
<!-- Búsqueda + botones -->
<LinearLayout
android:id="@+id/searchLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
app:layout_constraintTop_toBottomOf="@id/bannerLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<EditText
android:id="@+id/editSearch"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:background="@drawable/edit_text_background"
android:hint="Buscar anime..."
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:textColor="@android:color/black"
android:textSize="14sp"
android:inputType="text" />
<ImageButton
android:id="@+id/btnViewCompact"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_view_compact"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Vista Compacta" />
<ImageButton
android:id="@+id/btnViewNormal"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_view_normal"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Vista Normal" />
<ImageButton
android:id="@+id/btnViewLarge"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_view_large"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Vista Grande" />
</LinearLayout> </LinearLayout>
</RelativeLayout>
<!-- Tabs simulados --> <!-- Lista de animes -->
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent" android:id="@+id/recyclerViewAnime"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@color/primary"
android:padding="8dp">
<TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_height="0dp"
android:gravity="center"
android:text="Overview"
android:textColor="@color/textPrimary"
android:layout_height="0dp"/>
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:text="Anime List"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:layout_height="0dp"/>
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:text="Manga List"
android:textColor="@color/textPrimary"
android:layout_height="0dp"/>
</LinearLayout>
<!-- Campo de búsqueda + botón de filtro + botones de vista -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:weightSum="1">
<EditText
android:id="@+id/editTextFilter"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="0.6"
android:hint="Filter"
android:drawableStart="@android:drawable/ic_menu_search"
android:background="@drawable/edittext_background"
android:padding="8dp" android:padding="8dp"
android:textColor="@color/textPrimary" android:paddingBottom="96dp"
android:inputType="text" android:clipToPadding="false"
android:textSize="14sp" /> android:scrollbars="vertical"
app:layout_constraintTop_toBottomOf="@id/searchLayout"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView <!-- FAB correctamente posicionado -->
android:id="@+id/buttonFilterMenu" <com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="0dp" android:id="@+id/fabAddAnime"
android:layout_height="40dp"
android:layout_weight="0.1"
android:src="@drawable/ic_more_vert"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Filter menu"
android:padding="4dp"
app:tint="@color/textPrimary" />
<ImageButton
android:id="@+id/buttonViewGrid"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="0.1"
android:src="@drawable/ic_view_module"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Grid view"
android:padding="4dp" />
<ImageButton
android:id="@+id/buttonViewList"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="0.1"
android:src="@drawable/ic_view_list"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="List view"
android:padding="4dp" />
<ImageButton
android:id="@+id/buttonViewLarge"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="0.1"
android:src="@drawable/ic_view_comfy"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Large view"
android:padding="4dp" />
</LinearLayout>
<!-- Título del estado actual -->
<TextView
android:id="@+id/textViewWatching"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Watching" android:layout_gravity="bottom|end"
android:textColor="@color/textPrimary" android:layout_marginEnd="16dp"
android:textSize="16sp" android:layout_marginBottom="40dp"
android:textStyle="bold" android:contentDescription="Agregar anime"
android:layout_marginTop="4dp" android:src="@drawable/ic_add"
android:layout_marginBottom="2dp" /> android:tint="@android:color/white"
app:backgroundTint="@color/primary" />
<!-- Encabezado tipo tabla -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:background="@color/primary">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Title"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:textSize="14sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="Score"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textStyle="bold"
android:textSize="14sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="Type"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textStyle="bold"
android:textSize="14sp" />
<TextView
android:layout_width="24dp"
android:layout_height="wrap_content"
android:text="⋮"
android:textColor="@color/textPrimary"
android:gravity="end"
android:textStyle="bold" />
</LinearLayout>
<!-- Lista de anime -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewAnimeList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingBottom="8dp" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,16 +1,69 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:background="@color/background" android:background="@color/background"
tools:context="ui.fragments.browse.FragmentBrowse"> android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp">
<TextView <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:text="Browse" android:orientation="vertical">
android:gravity="center"
android:textSize="30sp"
android:textColor="@color/textPrimary"/>
</FrameLayout> <!-- Espaciador reducido -->
<View
android:id="@+id/statusBarSpacer"
android:layout_width="match_parent"
android:layout_height="12dp" />
<!-- Encabezado -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/textViewBrowse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Browse"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@color/textPrimary"
android:layout_marginEnd="8dp" />
<Spinner
android:id="@+id/spinnerType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="@color/primary" />
</LinearLayout>
<!-- Buscador -->
<EditText
android:id="@+id/editTextSearch"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:hint="Search"
android:drawableStart="@android:drawable/ic_menu_search"
android:background="@drawable/edittext_background"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:textColor="@color/textPrimary"
android:textColorHint="@color/textPrimary" />
<!-- Contenedor de secciones -->
<LinearLayout
android:id="@+id/sectionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="16dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -1,186 +1,111 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@color/background"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/background"
android:fitsSystemWindows="true"
tools:context=".ui.fragments.manga_list.FragmentManga"> tools:context=".ui.fragments.manga_list.FragmentManga">
<!-- Cabecera de usuario (opcional) --> <LinearLayout
<RelativeLayout android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="match_parent">
android:background="@drawable/sample_cover"
android:padding="12dp">
<ImageView <!-- Banner superior reducido -->
android:id="@+id/imageViewBanner" <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="120dp"
android:scaleType="centerCrop" android:background="@drawable/sample_cover"
android:src="@drawable/sample_cover" android:padding="12dp">
android:contentDescription="Profile background" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="bottom"
android:paddingBottom="16dp">
<ImageView <ImageView
android:id="@+id/imageViewAvatar" android:id="@+id/imageViewBanner"
android:layout_width="64dp" android:layout_width="match_parent"
android:layout_height="64dp" android:layout_height="match_parent"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_profile"
android:background="@drawable/circle_mask"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:clipToOutline="true" android:src="@drawable/sample_cover"
android:contentDescription="Profile picture" /> android:contentDescription="Banner" />
<TextView <LinearLayout
android:id="@+id/textViewUsername" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:layout_height="wrap_content" android:orientation="horizontal"
android:text="BtwIsSanti" android:gravity="bottom|start"
android:textColor="@android:color/white" android:paddingBottom="12dp"
android:textStyle="bold" android:paddingStart="12dp"
android:textSize="20sp" android:paddingEnd="12dp"
android:gravity="center_vertical"/> android:layout_alignParentBottom="true">
</LinearLayout>
</RelativeLayout>
<!-- Filtros y modos de vista --> <!-- Avatar -->
<LinearLayout <ImageView
android:layout_width="match_parent" android:id="@+id/imageViewAvatar"
android:layout_height="wrap_content" android:layout_width="48dp"
android:orientation="horizontal" android:layout_height="48dp"
android:gravity="center_vertical" android:src="@drawable/ic_profile"
android:paddingHorizontal="8dp" android:background="@drawable/circle_mask"
android:paddingTop="4dp" android:scaleType="centerCrop"
android:paddingBottom="4dp" android:clipToOutline="true"
android:weightSum="1"> android:contentDescription="Avatar"
android:layout_marginEnd="12dp" />
<EditText <!-- Nombre -->
android:id="@+id/editTextFilter" <TextView
android:layout_width="0dp" android:id="@+id/textViewUsername"
android:layout_height="40dp" android:layout_width="0dp"
android:layout_weight="0.6" android:layout_height="wrap_content"
android:hint="Filter" android:layout_weight="1"
android:drawableStart="@android:drawable/ic_menu_search" android:text="Usuario"
android:background="@drawable/edittext_background" android:textColor="@android:color/white"
android:padding="8dp" android:textStyle="bold"
android:textColor="@color/textPrimary" android:textSize="18sp"
android:inputType="text" android:gravity="center_vertical" />
android:textSize="14sp" />
<ImageView <!-- Botón de cambiar vista -->
android:id="@+id/buttonFilterMenu" <ImageButton
android:layout_width="0dp" android:id="@+id/btnViewCompact"
android:layout_height="40dp" android:layout_width="wrap_content"
android:layout_weight="0.1" android:layout_height="wrap_content"
android:src="@drawable/ic_more_vert" android:src="@drawable/ic_view_compact" />
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Filter menu"
android:padding="4dp"
app:tint="@color/textPrimary" />
<ImageButton <ImageButton
android:id="@+id/buttonViewGrid" android:id="@+id/btnViewNormal"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:layout_weight="0.1" android:src="@drawable/ic_view_normal" />
android:src="@drawable/ic_view_module"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Grid view"
android:padding="4dp" />
<ImageButton <ImageButton
android:id="@+id/buttonViewList" android:id="@+id/btnViewLarge"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:layout_weight="0.1" android:src="@drawable/ic_view_large" />
android:src="@drawable/ic_view_list" </LinearLayout>
android:background="?attr/selectableItemBackgroundBorderless" </RelativeLayout>
android:contentDescription="List view"
android:padding="4dp" />
<ImageButton <!-- Lista scrollable -->
android:id="@+id/buttonViewLarge" <androidx.recyclerview.widget.RecyclerView
android:layout_width="0dp" android:id="@+id/recyclerViewManga"
android:layout_height="40dp" android:layout_width="match_parent"
android:layout_weight="0.1" android:layout_height="0dp"
android:src="@drawable/ic_view_comfy" android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless" android:paddingStart="8dp"
android:contentDescription="Large view" android:paddingEnd="8dp"
android:padding="4dp" /> android:paddingBottom="100dp"
android:clipToPadding="false"
android:scrollbars="vertical" />
</LinearLayout> </LinearLayout>
<!-- Estado actual --> <!-- FAB flotante -->
<TextView <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/textViewWatching" android:id="@+id/fabAddManga"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Reading" android:layout_gravity="bottom|end"
android:textColor="@color/textPrimary" android:layout_margin="24dp"
android:textSize="16sp" android:contentDescription="Agregar manga"
android:textStyle="bold" android:src="@drawable/ic_add"
android:layout_marginTop="4dp" android:tint="@android:color/white"
android:layout_marginBottom="2dp" /> app:backgroundTint="@color/primary" />
<!-- Encabezado tabla --> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:background="@color/primary">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Title"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:textSize="14sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="Score"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textStyle="bold"
android:textSize="14sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="Type"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textStyle="bold"
android:textSize="14sp" />
<TextView
android:layout_width="24dp"
android:layout_height="wrap_content"
android:text="⋮"
android:textColor="@color/textPrimary"
android:gravity="end"
android:textStyle="bold" />
</LinearLayout>
<!-- Lista -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewAnimeList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingBottom="8dp" />
</LinearLayout>

View File

@ -1,74 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="8dp" android:padding="12dp">
android:gravity="center_vertical">
<ImageView <ImageView
android:id="@+id/imageViewCover" android:id="@+id/imageCover"
android:layout_width="40dp" android:layout_width="72dp"
android:layout_height="55dp" android:layout_height="96dp"
android:layout_marginEnd="8dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/sample_anime_cover" android:layout_marginEnd="12dp"
android:background="@drawable/rounded_mask" android:contentDescription="Portada" />
android:clipToOutline="true" />
<LinearLayout <LinearLayout
android:orientation="vertical"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="2" android:layout_weight="1">
android:orientation="vertical">
<TextView <TextView
android:id="@+id/textViewTitle" android:id="@+id/textTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Anime Title"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:maxLines="2"
android:textSize="14sp"
android:ellipsize="end" />
<TextView
android:id="@+id/textViewProgress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="5 / 12" android:text="Título"
android:textColor="@color/textPrimary" android:textStyle="bold"
android:textSize="12sp" /> android:textSize="16sp" />
<TextView
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Estado"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/textScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Score: 80"
android:textSize="14sp" />
<TextView
android:id="@+id/textProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Progreso: 10 eps"
android:textSize="14sp" />
<TextView
android:id="@+id/textType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tipo: TV"
android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/textViewScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="8.5"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textSize="14sp" />
<TextView
android:id="@+id/textViewType"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="TV"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textSize="12sp" />
<ImageView
android:id="@+id/buttonOptions"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_more_vert"
android:contentDescription="Options"
app:tint="@color/textPrimary" />
</LinearLayout> </LinearLayout>

View File

@ -1,26 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="8dp"> android:padding="12dp">
<TextView <TextView
android:id="@+id/textViewTitle" android:id="@+id/textTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Anime Title"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:maxLines="2"
android:textSize="14sp"
android:ellipsize="end" />
<TextView
android:id="@+id/textViewProgress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Progress: 5 / 12" android:text="Título Compacto"
android:textColor="@color/textPrimary" android:textStyle="bold"
android:textSize="12sp" /> android:textSize="14sp" />
<TextView
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Estado"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
</LinearLayout> </LinearLayout>

View File

@ -1,48 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="180dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp"> android:layout_margin="8dp">
<!-- Imagen de fondo -->
<ImageView <ImageView
android:id="@+id/imageViewCover" android:id="@+id/imageCover"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="240dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:background="@drawable/rounded_mask" android:background="@drawable/rectangle_placeholder"
android:clipToOutline="true" android:contentDescription="Portada" />
android:src="@drawable/sample_anime_cover" />
<LinearLayout <!-- Progreso en esquina inferior izquierda -->
<TextView
android:id="@+id/textProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="6dp"
android:text="6/12"
android:textColor="#FF4444"
android:textStyle="bold"
android:textSize="14sp"
android:background="@android:color/transparent" />
<!-- Título con fondo oscuro -->
<TextView
android:id="@+id/textTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:padding="8dp" android:background="#99000000"
android:orientation="horizontal" android:text="Nombre del Anime"
android:gravity="center_vertical" android:textColor="@android:color/white"
android:weightSum="1" android:textSize="14sp"
android:background="#AA000000"> android:padding="6dp"
android:maxLines="2"
android:ellipsize="end" />
<TextView <!-- Indicador de estado (ej: Watching) -->
android:id="@+id/textViewProgress" <View
android:layout_width="0dp" android:id="@+id/statusDot"
android:layout_height="wrap_content" android:layout_width="10dp"
android:layout_weight="0.5" android:layout_height="10dp"
android:text="5 / 12" android:layout_gravity="top|start"
android:textColor="@android:color/white" android:layout_margin="6dp"
android:textSize="14sp" android:background="@drawable/dot_green"
android:textStyle="bold" /> android:visibility="visible" />
<TextView
android:id="@+id/textViewScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="8.5"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold"
android:gravity="end" />
</LinearLayout>
</FrameLayout> </FrameLayout>

View File

@ -1,74 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="8dp" android:padding="12dp">
android:gravity="center_vertical">
<ImageView <ImageView
android:id="@+id/imageViewCover" android:id="@+id/imageCover"
android:layout_width="40dp" android:layout_width="72dp"
android:layout_height="55dp" android:layout_height="96dp"
android:layout_marginEnd="8dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/sample_anime_cover" android:layout_marginEnd="12dp"
android:background="@drawable/rounded_mask" android:contentDescription="Portada" />
android:clipToOutline="true" />
<LinearLayout <LinearLayout
android:orientation="vertical"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="2" android:layout_weight="1">
android:orientation="vertical">
<TextView <TextView
android:id="@+id/textViewTitle" android:id="@+id/textTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Manga Title"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:maxLines="2"
android:textSize="14sp"
android:ellipsize="end" />
<TextView
android:id="@+id/textViewProgress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="45 / 100" android:text="Título"
android:textColor="@color/textPrimary" android:textStyle="bold"
android:textSize="12sp" /> android:textSize="16sp" />
<TextView
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Estado"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/textScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Score: 90"
android:textSize="14sp" />
<TextView
android:id="@+id/textProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Capítulos: 12"
android:textSize="14sp" />
<TextView
android:id="@+id/textType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tipo: Manga"
android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/textViewScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="8.5"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textSize="14sp" />
<TextView
android:id="@+id/textViewType"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="Manga"
android:textColor="@color/textPrimary"
android:gravity="center"
android:textSize="12sp" />
<ImageView
android:id="@+id/buttonOptions"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_more_vert"
android:contentDescription="Options"
app:tint="@color/textPrimary" />
</LinearLayout> </LinearLayout>

View File

@ -1,26 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="8dp"> android:padding="12dp">
<TextView <TextView
android:id="@+id/textViewTitle" android:id="@+id/textTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Manga Title"
android:textColor="@color/textPrimary"
android:textStyle="bold"
android:maxLines="2"
android:textSize="14sp"
android:ellipsize="end" />
<TextView
android:id="@+id/textViewProgress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Progress: 45 / 100" android:text="Título Compacto"
android:textColor="@color/textPrimary" android:textStyle="bold"
android:textSize="12sp" /> android:textSize="14sp" />
<TextView
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Estado"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
</LinearLayout> </LinearLayout>

View File

@ -1,48 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="180dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp"> android:layout_margin="8dp">
<!-- Imagen de fondo -->
<ImageView <ImageView
android:id="@+id/imageViewCover" android:id="@+id/imageCover"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="240dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:background="@drawable/rounded_mask" android:background="@drawable/rectangle_placeholder"
android:clipToOutline="true" android:contentDescription="Portada" />
android:src="@drawable/sample_anime_cover" />
<LinearLayout <!-- Progreso en esquina inferior izquierda -->
<TextView
android:id="@+id/textProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="6dp"
android:text="6/12"
android:textColor="#FF4444"
android:textStyle="bold"
android:textSize="14sp"
android:background="@android:color/transparent" />
<!-- Título con fondo oscuro -->
<TextView
android:id="@+id/textTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:padding="8dp" android:background="#99000000"
android:orientation="horizontal" android:text="Nombre del Manga"
android:gravity="center_vertical" android:textColor="@android:color/white"
android:weightSum="1" android:textSize="14sp"
android:background="#AA000000"> android:padding="6dp"
android:maxLines="2"
android:ellipsize="end" />
<TextView <!-- Indicador de estado (ej: Watching) -->
android:id="@+id/textViewProgress" <View
android:layout_width="0dp" android:id="@+id/statusDot"
android:layout_height="wrap_content" android:layout_width="10dp"
android:layout_weight="0.5" android:layout_height="10dp"
android:text="45 / 100" android:layout_gravity="top|start"
android:textColor="@android:color/white" android:layout_margin="6dp"
android:textSize="14sp" android:background="@drawable/dot_green"
android:textStyle="bold" /> android:visibility="visible" />
<TextView
android:id="@+id/textViewScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="8.5"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold"
android:gravity="end" />
</LinearLayout>
</FrameLayout> </FrameLayout>

View File

@ -6,10 +6,10 @@
<color name="gray">#808080</color> <color name="gray">#808080</color>
<color name="activeTint">#F26D50</color> <!-- estilo AniList --> <color name="activeTint">#F26D50</color> <!-- estilo AniList -->
<!-- Colores por estado --> <!-- Colores por estado anime -->
<color name="statWatching">#3db4f2</color> <color name="status_completed">#4CAF50</color> <!-- verde -->
<color name="statCompleted">#16c98d</color> <color name="status_watching">#2196F3</color> <!-- azul -->
<color name="statOnHold">#f5a623</color> <color name="status_paused">#FFC107</color> <!-- amarillo -->
<color name="statDropped">#f45b69</color> <color name="status_dropped">#F44336</color> <!-- rojo -->
<color name="statPlanToWatch">#c678dd</color> <color name="status_planning">#9E9E9E</color> <!-- gris -->
</resources> </resources>

View File

@ -20,4 +20,35 @@
<item name="imageViewCover" type="id" /> <item name="imageViewCover" type="id" />
<item name="textViewProgress" type="id" /> <item name="textViewProgress" type="id" />
<item name="textViewTitle" type="id" /> <item name="textViewTitle" type="id" />
<item name="buttonFilter" type="id" />
<item name="recyclerViewBrowse" type="id" />
<item name="spinnerType" type="id" />
<item name="textViewBrowse" type="id" />
<item name="editTextSearch" type="id" />
<item name="sectionContainer" type="id" />
<item name="statusBarSpacer" type="id" />
<item name="editTextPasswordRegister" type="id" />
<item name="buttonRegister" type="id" />
<item name="buttonAddItem" type="id" />
<item name="editTextPassword" type="id" />
<item name="buttonLogin" type="id" />
<item name="buttonGuest" type="id" />
<item name="buttonGoRegister" type="id" />
<item name="editTextUsername" type="id" />
<item name="fabAddAnime" type="id" />
<item name="recyclerViewAnime" type="id" />
<item name="fabAddManga" type="id" />
<item name="recyclerViewManga" type="id" />
<item name="imageViewBanner" type="id" />
<item name="imageViewAvatar" type="id" />
<item name="textViewUsername" type="id" />
<item name="buttonChangeViewType" type="id" />
<item name="recyclerSearchResults" type="id" />
<item name="buttonAddAnime" type="id" />
<item name="spinnerStatus" type="id" />
<item name="editTextAnimeTitle" type="id" />
<item name="spinnerEditStatus" type="id" />
<item name="buttonSaveAnime" type="id" />
<item name="statusDot" type="id" />
<item name="editSearch" type="id" />
</resources> </resources>

View File

@ -7,4 +7,39 @@
<string name="profile">\n</string> <string name="profile">\n</string>
<string name="write_a_status">Write a status...</string> <string name="write_a_status">Write a status...</string>
<string name="show_more" /> <string name="show_more" />
<string-array name="media_types">
<item>Anime</item>
<item>Manga</item>
</string-array>
<string-array name="anime_status_array">
<item>Watching</item>
<item>Completed</item>
<item>Paused</item>
<item>Dropped</item>
<item>Planning</item>
</string-array>
<string-array name="anime_type_array">
<item>TV</item>
<item>Movie</item>
<item>OVA</item>
<item>ONA</item>
<item>Special</item>
</string-array>
<string-array name="manga_status_array">
<item>Reading</item>
<item>Completed</item>
<item>Paused</item>
<item>Dropped</item>
<item>Planning</item>
</string-array>
<string-array name="manga_type_array">
<item>Manga</item>
<item>Manhwa</item>
<item>Manhua</item>
<item>Novel</item>
<item>One-shot</item>
</string-array>
</resources> </resources>

View File

@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.9.2" agp = "8.10.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.2.1"
espressoCore = "3.6.1" espressoCore = "3.6.1"
@ -7,6 +7,8 @@ appcompat = "1.7.0"
material = "1.12.0" material = "1.12.0"
activity = "1.10.1" activity = "1.10.1"
constraintlayout = "2.2.1" constraintlayout = "2.2.1"
navigationFragment = "2.9.0"
navigationUi = "2.9.0"
[libraries] [libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
@ -16,6 +18,8 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" }
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }