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>
<SelectionState runConfigName="app">
<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">
<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>
</Target>
</DropdownSelection>

View File

@ -41,11 +41,26 @@ dependencies {
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
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*/
implementation("com.github.bumptech.glide:glide: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"
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" />
<application
@ -14,16 +14,29 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.YomiTrack"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<!-- PANTALLA INICIAL: SplashActivity -->
<activity
android:name=".ui.MainActivity"
android:name=".ui.fragments.login.SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</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>
</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 {
private String title;
private String progress;
private String imageUrl;
private ContentType contentType; // Anime o Manga
private ContentType contentType;
public enum ContentType {
ANIME,
MANGA
}
// Constructor completo
public ItemModel(String title, String progress, String imageUrl, ContentType contentType) {
this.title = title;
this.progress = progress;
@ -20,7 +22,6 @@ public class ItemModel {
this.contentType = contentType;
}
// Getters
public String getTitle() {
return title;
}
@ -37,7 +38,6 @@ public class ItemModel {
return contentType;
}
// Setters (opcional si quieres mutar los datos después)
public void setTitle(String title) {
this.title = title;
}

View File

@ -1,7 +1,14 @@
package com.santiparra.yomitrack.model;
/**
* Modelo de actividad reciente para mostrar acciones de usuario.
*/
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) {
this.user = user;

View File

@ -1,5 +1,9 @@
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 {
private String category;
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.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
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;
public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHolder> {
private final List<AnimeItem> animeList;
private final Context context;
private int viewMode = 0;
public static final int VIEW_NORMAL = 0;
public static final int VIEW_COMPACT = 1;
public static final int VIEW_LARGE = 2;
public interface OnAnimeRemoveListener {
void onAnimeRemoved(AnimeItem anime);
private List<AnimeEntity> animeList;
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 void setOnAnimeRemoveListener(OnAnimeRemoveListener listener) {
this.removeListener = listener;
}
public void setViewMode(int mode) {
this.viewMode = mode;
}
public AnimeAdapter(Context context, List<AnimeItem> animeList) {
this.context = context;
public AnimeAdapter(List<AnimeEntity> animeList, int viewType,
OnAnimeClickListener onEditClick,
OnAnimeClickListener onLongClick) {
this.animeList = animeList;
this.viewType = viewType;
this.onEditClick = onEditClick;
this.onLongClick = onLongClick;
}
public void setViewType(int viewType) {
this.viewType = viewType;
notifyDataSetChanged();
}
@NonNull
@Override
public AnimeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
int layoutId;
switch (viewMode) {
case 1:
layoutId = R.layout.item_anime_large; // solo imagen con score y episodios
break;
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);
int layout = R.layout.item_anime;
if (viewType == VIEW_COMPACT) layout = R.layout.item_anime_compact;
else if (viewType == VIEW_LARGE) layout = R.layout.item_anime_large;
View view = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new AnimeViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AnimeViewHolder holder, int position) {
AnimeItem anime = animeList.get(position);
AnimeEntity anime = animeList.get(position);
if (holder.title != null)
holder.title.setText(anime.getTitle());
String title = anime.getTitle() != null ? anime.getTitle() : "Sin título";
String status = anime.getStatus() != null ? anime.getStatus() : "";
String type = anime.getType() != null ? anime.getType() : "";
String imageUrl = anime.getImageUrl();
if (holder.progress != null)
holder.progress.setText("Progress: " + anime.getWatchedEpisodes() + "/" + anime.getTotalEpisodes());
if (holder.textTitle != null) holder.textTitle.setText(title);
if (holder.score != null)
holder.score.setText(String.valueOf(anime.getScore()));
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.textStatus != null) {
String statusText = status + (type.isEmpty() ? "" : "" + type);
holder.textStatus.setText(statusText);
}
if (holder.buttonOptions != null) {
holder.buttonOptions.setOnClickListener(v -> {
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: " + 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.textProgress != null) {
String progress = anime.getProgress() + " eps";
holder.textProgress.setText(progress);
}
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
public int getItemCount() {
return animeList.size();
return animeList != null ? animeList.size() : 0;
}
static class AnimeViewHolder extends RecyclerView.ViewHolder {
TextView title, progress, score, type;
ImageView cover, buttonOptions;
@Override
public int getItemViewType(int position) {
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);
title = itemView.findViewById(R.id.textViewTitle);
progress = itemView.findViewById(R.id.textViewProgress);
score = itemView.findViewById(R.id.textViewScore);
type = itemView.findViewById(R.id.textViewType);
cover = itemView.findViewById(R.id.imageViewCover);
buttonOptions = itemView.findViewById(R.id.buttonOptions);
imageCover = itemView.findViewById(R.id.imageCover);
textTitle = itemView.findViewById(R.id.textTitle);
textStatus = itemView.findViewById(R.id.textStatus);
textProgress = itemView.findViewById(R.id.textProgress);
textScore = itemView.findViewById(R.id.textScore);
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> {
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_ANIME_MANGA = 1;

View File

@ -1,136 +1,162 @@
// MangaAdapter.java actualizado con statusDot dinámico
package com.santiparra.yomitrack.model.adapters.manga_adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.AnimeItem;
import com.santiparra.yomitrack.db.entities.MangaEntity;
import java.util.List;
public class MangaAdapter extends RecyclerView.Adapter<MangaAdapter.MangaViewHolder> {
private final List<AnimeItem> mangaList;
private final Context context;
private int viewMode = 0;
public static final int VIEW_NORMAL = 0;
public static final int VIEW_COMPACT = 1;
public static final int VIEW_LARGE = 2;
public interface OnMangaRemoveListener {
void onMangaRemoved(AnimeItem manga);
}
private List<MangaEntity> mangaList;
private int viewType;
private final OnMangaClickListener onEditClick;
private final OnMangaClickListener onLongClick;
private OnMangaRemoveListener removeListener;
public void setOnMangaRemoveListener(OnMangaRemoveListener listener) {
this.removeListener = listener;
}
public void setViewMode(int mode) {
this.viewMode = mode;
}
public MangaAdapter(Context context, List<AnimeItem> mangaList) {
this.context = context;
public MangaAdapter(List<MangaEntity> mangaList, int viewType,
OnMangaClickListener onEditClick,
OnMangaClickListener onLongClick) {
this.mangaList = mangaList;
this.viewType = viewType;
this.onEditClick = onEditClick;
this.onLongClick = onLongClick;
}
public void setViewType(int viewType) {
this.viewType = viewType;
notifyDataSetChanged();
}
@NonNull
@Override
public MangaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
int layoutId;
switch (viewMode) {
case 1:
layoutId = R.layout.item_anime_large;
break;
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);
int layout = R.layout.item_manga;
if (viewType == VIEW_COMPACT) layout = R.layout.item_manga_compact;
else if (viewType == VIEW_LARGE) layout = R.layout.item_manga_large;
View view = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new MangaViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MangaViewHolder holder, int position) {
AnimeItem manga = mangaList.get(position);
MangaEntity manga = mangaList.get(position);
if (holder.title != null)
holder.title.setText(manga.getTitle());
String title = manga.getTitle() != null ? manga.getTitle() : "Sin título";
String status = manga.getStatus() != null ? manga.getStatus() : "";
String type = manga.getType() != null ? manga.getType() : "";
String imageUrl = manga.getImageUrl();
if (holder.progress != null)
holder.progress.setText("Progress: " + manga.getWatchedEpisodes() + "/" + manga.getTotalEpisodes());
if (holder.textTitle != null) holder.textTitle.setText(title);
if (holder.score != null)
holder.score.setText(String.valueOf(manga.getScore()));
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.textStatus != null) {
String statusText = status + (type.isEmpty() ? "" : "" + type);
holder.textStatus.setText(statusText);
}
if (holder.buttonOptions != null) {
holder.buttonOptions.setOnClickListener(v -> {
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.textProgress != null) {
holder.textProgress.setText(manga.getProgress() + " caps");
}
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
public int getItemCount() {
return mangaList.size();
return mangaList != null ? mangaList.size() : 0;
}
static class MangaViewHolder extends RecyclerView.ViewHolder {
TextView title, progress, score, type;
ImageView cover, buttonOptions;
@Override
public int getItemViewType(int position) {
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);
title = itemView.findViewById(R.id.textViewTitle);
progress = itemView.findViewById(R.id.textViewProgress);
score = itemView.findViewById(R.id.textViewScore);
type = itemView.findViewById(R.id.textViewType);
cover = itemView.findViewById(R.id.imageViewCover);
buttonOptions = itemView.findViewById(R.id.buttonOptions);
imageCover = itemView.findViewById(R.id.imageCover);
textTitle = itemView.findViewById(R.id.textTitle);
textStatus = itemView.findViewById(R.id.textStatus);
textProgress = itemView.findViewById(R.id.textProgress);
textScore = itemView.findViewById(R.id.textScore);
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;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.ImageView;
import android.view.ViewGroup;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
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.browse.FragmentBrowse;
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.profile.FragmentProfile;
@ -32,29 +37,22 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
/*We initialize the values*/
binding = ActivityMainBinding.inflate(getLayoutInflater());
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);
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (view,insets) ->{
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (view, insets) -> {
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;
});
ViewCompat.setOnApplyWindowInsetsListener(binding.bottomNavigationView, (view, insets) -> {
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);
// Aplica margen negativo externo para que el fondo se expanda visualmente
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) params).bottomMargin = -bottomInset;
@ -63,12 +61,12 @@ public class MainActivity extends AppCompatActivity {
return insets;
});
/*Implementation of bottomNav*/
// Fragmento inicial
replaceFragment(new FragmentHome());
// Navegación inferior
binding.bottomNavigationView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.home) {
replaceFragment(new FragmentHome());
} else if (itemId == R.id.profile) {
@ -80,17 +78,32 @@ public class MainActivity extends AppCompatActivity {
} else if (itemId == R.id.browse) {
replaceFragment(new FragmentBrowse());
}
return true;
});
/*Implementation of Toolbar*/
// Toolbar
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
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();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout, fragment);

View File

@ -1,5 +1,7 @@
package com.santiparra.yomitrack.ui.fragments.anime_list;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
@ -8,172 +10,163 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
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.ui.fragments.addanime.AddAnimeFragment;
import com.santiparra.yomitrack.ui.fragments.editanime.EditAnimeFragment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class FragmentAnime extends Fragment {
private EditText editSearch;
private RecyclerView recyclerView;
private EditText editTextFilter;
private TextView textViewTitle;
private AnimeAdapter adapter;
private ApiService api;
private int userId = 1;
private final List<AnimeItem> fullAnimeList = new ArrayList<>();
private final List<AnimeItem> filteredAnimeList = new ArrayList<>();
private String currentStatus = "Watching";
private int currentViewMode = 0;
private ImageButton btnViewCompact, btnViewNormal, btnViewLarge;
private int currentViewType = AnimeAdapter.VIEW_NORMAL;
private List<AnimeEntity> animeList = new ArrayList<>();
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
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_alist, container, false);
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.recyclerViewAnimeList);
editTextFilter = view.findViewById(R.id.editTextFilter);
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);
recyclerView = view.findViewById(R.id.recyclerViewAnime);
btnViewCompact = view.findViewById(R.id.btnViewCompact);
btnViewNormal = view.findViewById(R.id.btnViewNormal);
btnViewLarge = view.findViewById(R.id.btnViewLarge);
FloatingActionButton fabAdd = view.findViewById(R.id.fabAddAnime);
buttonGrid.setOnClickListener(v -> setLayoutMode(0));
buttonLarge.setOnClickListener(v -> setLayoutMode(1));
buttonList.setOnClickListener(v -> setLayoutMode(2));
editSearch = view.findViewById(R.id.editSearch);
editSearch.addTextChangedListener(new TextWatcher() {
@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
public void onTextChanged(CharSequence s, int start, int before, int count) {
filterAnimeList(s.toString());
}
@Override
public void afterTextChanged(Editable s) {}
});
Map<Integer, String> filterMap = new HashMap<>();
filterMap.put(R.id.filter_all, "All");
filterMap.put(R.id.filter_watching, "Watching");
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");
api = ApiClient.getClient().create(ApiService.class);
SharedPreferences prefs = requireContext().getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
userId = prefs.getInt("current_user_id", -1);
buttonFilterMenu.setOnClickListener(v -> {
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);
filterAnimeList(editTextFilter.getText().toString());
return true;
}
return false;
});
popup.show();
});
// Botones de vista
btnViewCompact.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_COMPACT));
btnViewNormal.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_NORMAL));
btnViewLarge.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_LARGE));
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) {
filteredAnimeList.clear();
int count = 0;
for (AnimeItem anime : fullAnimeList) {
boolean matchesStatus = currentStatus.equals("All") || anime.getStatus().equalsIgnoreCase(currentStatus);
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;
}
List<AnimeEntity> filtered = new ArrayList<>();
for (AnimeEntity anime : animeList) {
if (anime.getTitle().toLowerCase().contains(query.toLowerCase())) {
filtered.add(anime);
}
}
adapter.notifyDataSetChanged();
adapter.updateList(filtered);
}
private void setLayoutMode(int mode) {
currentViewMode = mode;
private void fetchAnimeList() {
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
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
} else if (mode == 1) { // Imagen grande + info
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 1));
} else if (mode == 0) { // Lista tipo AniList
@Override
public void onFailure(@NonNull Call<List<AnimeEntity>> call, @NonNull Throwable t) {
Toast.makeText(getContext(), "Fallo de conexión", Toast.LENGTH_SHORT).show();
}
});
}
private void setViewType(int viewType) {
currentViewType = viewType;
if (viewType == AnimeAdapter.VIEW_LARGE) {
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
} else {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
adapter = new AnimeAdapter(getContext(), filteredAnimeList);
adapter.setViewMode(mode);
adapter.setOnAnimeRemoveListener(anime -> fullAnimeList.remove(anime));
adapter = new AnimeAdapter(animeList, viewType,
this::showEditDialog,
this::deleteAnime);
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() {
fullAnimeList.clear();
fullAnimeList.add(new AnimeItem("Aharen-san wa Hakarenai Season 2", "https://cdn.example.com/img1.jpg", 5, 12, 8.5, "TV", "Watching"));
fullAnimeList.add(new AnimeItem("Anne Shirley", "https://cdn.example.com/img2.jpg", 5, 24, 7.9, "OVA", "Planning"));
fullAnimeList.add(new AnimeItem("Ballpark de Tsukamaete!", "https://cdn.example.com/img3.jpg", 5, 12, 9.2, "Movie", "Paused"));
fullAnimeList.add(new AnimeItem("Bleach: TYBW", "https://cdn.example.com/img4.jpg", 8, 13, 9.0, "TV", "Completed"));
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"));
private void showEditDialog(AnimeEntity anime) {
requireActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout, new EditAnimeFragment(anime))
.addToBackStack(null)
.commit();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("currentStatus", currentStatus);
}
private void deleteAnime(AnimeEntity anime) {
api.deleteAnime(anime.getId()).enqueue(new Callback<String>() {
@Override
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
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
currentStatus = savedInstanceState.getString("currentStatus", "Watching");
textViewTitle.setText(currentStatus);
filterAnimeList(editTextFilter.getText().toString());
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(getContext(), "Fallo de conexión", Toast.LENGTH_SHORT).show();
}
});
}
}

View File

@ -1,18 +1,190 @@
package com.santiparra.yomitrack.ui.fragments.browse;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
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.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.ItemModel;
import com.santiparra.yomitrack.model.adapters.browser_section_adapter.BrowseGridAdapter;
import java.util.ArrayList;
import java.util.List;
public class FragmentBrowse extends Fragment {
@Override
public View onCreateView(LayoutInflater layoutInflater, ViewGroup container,
Bundle saveInstanceState){
return layoutInflater.inflate(R.layout.fragment_browse,container,false);
private Spinner spinnerType;
private EditText editTextSearch;
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.Map;
/**
* Fragmento principal que muestra secciones de anime/manga y actividad reciente.
*/
public class FragmentHome extends Fragment {
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) {
super.onViewCreated(view, savedInstanceState);
// Secciones principales
// region Secciones de contenido (Airing, En progreso...)
RecyclerView mainRecyclerView = view.findViewById(R.id.mainRecyclerView);
mainRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
List<String> sectionTitles = Arrays.asList("Airing", "Anime in Progress", "Manga in Progress");
Map<String, List<ItemModel>> sectionItems = new HashMap<>();
// Datos simulados de ejemplo
sectionItems.put("Airing", Arrays.asList(
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),
@ -55,40 +59,26 @@ public class FragmentHome extends Fragment {
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("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("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)
new ItemModel("One Piece", "900/1100", "https://i.imgur.com/VgVfG6K.jpg", ItemModel.ContentType.ANIME)
));
sectionItems.put("Manga in Progress", Arrays.asList(
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("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)
new ItemModel("Berserk", "370/380", "https://i.imgur.com/8FJYYHo.jpg", ItemModel.ContentType.MANGA)
));
SectionAdapter sectionAdapter = new SectionAdapter(sectionTitles, sectionItems);
mainRecyclerView.setAdapter(sectionAdapter);
// endregion
// Actividades recientes
// region Actividad reciente
RecyclerView activityRecyclerView = view.findViewById(R.id.activityRecyclerView);
activityRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
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("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"),
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")
new RecentActivityModel("prtrncyon", "Scored 9/10 on", "Chainsaw Man", "12 hours ago", "https://cdn.myanimelist.net/images/manga/2/253146.jpg")
// ... más datos simulados aquí
));
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 buttonShowLess = view.findViewById(R.id.buttonShowLessActivity);
// Mostrar más/menos actividad
if (fullRecentActivity.size() > 10) {
buttonShowMore.setVisibility(View.VISIBLE);
buttonShowLess.setVisibility(View.GONE);
@ -121,5 +112,6 @@ public class FragmentHome extends Fragment {
buttonShowMore.setVisibility(View.GONE);
buttonShowLess.setVisibility(View.GONE);
}
// endregion
}
}

View File

@ -1,174 +1,115 @@
package com.santiparra.yomitrack.ui.fragments.manga_list;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
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.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.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class FragmentManga extends Fragment {
private RecyclerView recyclerView;
private EditText editTextFilter;
private TextView textViewTitle;
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<>();
private final List<AnimeItem> filteredMangaList = new ArrayList<>();
private String currentStatus = "Reading";
private int currentViewMode = 0;
private ImageButton buttonGrid, buttonLarge, buttonList;
@Nullable
@Override
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);
editTextFilter = view.findViewById(R.id.editTextFilter);
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);
recyclerView = view.findViewById(R.id.recyclerViewManga);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
buttonGrid.setOnClickListener(v -> setLayoutMode(0));
buttonLarge.setOnClickListener(v -> setLayoutMode(1));
buttonList.setOnClickListener(v -> setLayoutMode(2));
ImageView changeViewButton = view.findViewById(R.id.buttonChangeViewType);
FloatingActionButton fabAdd = view.findViewById(R.id.fabAddManga);
initSampleMangaList();
api = ApiClient.getClient().create(ApiService.class);
filteredMangaList.addAll(fullMangaList);
adapter = new MangaAdapter(getContext(), filteredMangaList);
recyclerView.setAdapter(adapter);
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());
}
changeViewButton.setOnClickListener(v -> {
currentViewType = (currentViewType + 1) % 3;
if (adapter != null) adapter.setViewType(currentViewType);
});
Map<Integer, String> filterMap = new HashMap<>();
filterMap.put(R.id.filter_all, "All");
filterMap.put(R.id.filter_watching, "Reading");
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");
fabAdd.setOnClickListener(v -> requireActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout, new AddMangaFragment())
.addToBackStack(null)
.commit());
buttonFilterMenu.setOnClickListener(v -> {
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);
loadMangaList();
return view;
}
private void filterMangaList(String query) {
filteredMangaList.clear();
int count = 0;
for (AnimeItem manga : fullMangaList) {
boolean matchesStatus = currentStatus.equals("All") || manga.getStatus().equalsIgnoreCase(currentStatus);
boolean matchesQuery = manga.getTitle().toLowerCase().contains(query.toLowerCase());
if (matchesStatus && matchesQuery) {
filteredMangaList.add(manga);
count++;
if (count >= 10) {
Toast.makeText(requireContext(), "Mostrando los primeros 10 resultados", Toast.LENGTH_SHORT).show();
break;
private void loadMangaList() {
api.getMangaByUser(userId).enqueue(new Callback<List<MangaEntity>>() {
@Override
public void onResponse(Call<List<MangaEntity>> call, Response<List<MangaEntity>> response) {
if (response.isSuccessful() && response.body() != null) {
adapter = new MangaAdapter(response.body(), currentViewType,
FragmentManga.this::showEditDialog,
FragmentManga.this::deleteManga);
recyclerView.setAdapter(adapter);
} else {
Toast.makeText(getContext(), "Error al cargar la lista", Toast.LENGTH_SHORT).show();
}
}
}
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) {
currentViewMode = mode;
if (mode == 2) {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
} else if (mode == 1) {
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 showEditDialog(MangaEntity manga) {
requireActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout, new EditMangaFragment(manga))
.addToBackStack(null)
.commit();
}
private void initSampleMangaList() {
fullMangaList.clear();
fullMangaList.add(new AnimeItem("Chainsaw Man", "https://cdn.example.com/img1.jpg", 45, 100, 8.5, "Manga", "Reading"));
fullMangaList.add(new AnimeItem("Berserk", "https://cdn.example.com/img2.jpg", 370, 380, 9.4, "Manga", "Paused"));
fullMangaList.add(new AnimeItem("One Piece", "https://cdn.example.com/img3.jpg", 1090, 1200, 9.8, "Manga", "Reading"));
fullMangaList.add(new AnimeItem("Attack on Titan", "https://cdn.example.com/img4.jpg", 139, 139, 9.5, "Manga", "Completed"));
fullMangaList.add(new AnimeItem("Solo Leveling", "https://cdn.example.com/img5.jpg", 179, 179, 8.9, "Manhwa", "Completed"));
}
private void deleteManga(MangaEntity manga) {
api.deleteManga(manga.getId()).enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Toast.makeText(getContext(), "Manga eliminado", Toast.LENGTH_SHORT).show();
loadMangaList();
} else {
Toast.makeText(getContext(), "Error al eliminar", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
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());
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(getContext(), "Fallo en la conexión", Toast.LENGTH_SHORT).show();
}
});
}
}

View File

@ -1,166 +1,140 @@
package com.santiparra.yomitrack.ui.fragments.profile;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.santiparra.yomitrack.R;
import com.santiparra.yomitrack.model.UserStats;
import com.santiparra.yomitrack.utils.StatsHelper;
import com.santiparra.yomitrack.api.ApiClient;
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.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class FragmentProfile extends Fragment {
public FragmentProfile() {
}
private TextView textUsername;
private LinearLayout animeStatsContainer, mangaStatsContainer;
private ApiService api;
private int userId;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_profile, container, false);
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = 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
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
private void loadStats() {
animeStatsContainer.removeAllViews();
mangaStatsContainer.removeAllViews();
TextView usernameText = view.findViewById(R.id.usernameText);
TextView descriptionText = view.findViewById(R.id.profileDescriptionText);
TextView seeMore = view.findViewById(R.id.textViewSeeMore);
LinearLayout statsContainer = view.findViewById(R.id.animeStatsContainer);
api.getAnimeByUser(userId).enqueue(new Callback<List<AnimeEntity>>() {
@Override
public void onResponse(Call<List<AnimeEntity>> call, Response<List<AnimeEntity>> response) {
if (response.isSuccessful() && response.body() != null) {
showStats(animeStatsContainer, "Anime", countByStatus(response.body()));
}
}
usernameText.setText("BtwIsSanti");
descriptionText.setText("Eiko is my waifu right now. Mai and Mikasa is my second wife. Long live anime.");
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");
@Override
public void onFailure(Call<List<AnimeEntity>> call, Throwable t) {
Toast.makeText(getContext(), "Error al cargar anime", Toast.LENGTH_SHORT).show();
}
});
// Cargar estadísticas simuladas
List<UserStats> statsList = StatsHelper.getAnimeStats();
for (UserStats stat : statsList) {
View statView = LayoutInflater.from(getContext())
.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;
api.getMangaByUser(userId).enqueue(new Callback<List<MangaEntity>>() {
@Override
public void onResponse(Call<List<MangaEntity>> call, Response<List<MangaEntity>> response) {
if (response.isSuccessful() && response.body() != null) {
showStats(mangaStatsContainer, "Manga", countByStatus(response.body()));
}
}
bar.setProgressTintList(ContextCompat.getColorStateList(requireContext(), colorRes));
statsContainer.addView(statView);
@Override
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)
LinearLayout activityContainer = view.findViewById(R.id.activityContainer);
private void showStats(LinearLayout container, String category, Map<String, Integer> data) {
TextView title = new TextView(getContext());
title.setText(category.toUpperCase());
title.setTextSize(18);
title.setPadding(0, 24, 0, 12);
container.addView(title);
if (activityContainer != null) {
// Lista simulada de updates
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 total = 0;
for (int count : data.values()) total += count;
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++) {
View card = LayoutInflater.from(getContext())
.inflate(R.layout.item_activity, activityContainer, false);
TextView label = new TextView(getContext());
label.setText(status + ": " + count + " (" + percent + "%)");
label.setTextSize(16);
container.addView(label);
TextView text = card.findViewById(R.id.activityText);
TextView time = card.findViewById(R.id.activityTime);
text.setText(activities[i]);
time.setText("16 hours ago");
activityContainer.addView(card);
}
}
// 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);
ProgressBar progress = new ProgressBar(getContext(), null, android.R.attr.progressBarStyleHorizontal);
progress.setMax(100);
progress.setProgress(percent);
progress.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
container.addView(progress);
}
}
}

View File

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

View File

@ -1,224 +1,143 @@
<?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:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@color/background"
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 -->
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/sample_cover"
android:padding="12dp">
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageViewBanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/sample_cover"
android:contentDescription="Profile background" />
<!-- Banner -->
<RelativeLayout
android:id="@+id/bannerLayout"
android:layout_width="0dp"
android:layout_height="120dp"
android:background="@drawable/sample_cover"
android:padding="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="bottom"
android:paddingBottom="16dp">
<ImageView
android:id="@+id/imageViewBanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/sample_cover"
android:contentDescription="Banner" />
<ImageView
android:id="@+id/imageViewAvatar"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="12dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="12dp"
android:layout_marginBottom="12dp"
android:src="@drawable/ic_profile"
android:background="@drawable/circle_mask"
android:scaleType="centerCrop"
android:clipToOutline="true"
android:contentDescription="Profile picture" />
android:contentDescription="Avatar" />
<TextView
android:id="@+id/textViewUsername"
android:layout_width="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:textStyle="bold"
android:textSize="20sp"
android:gravity="center_vertical"/>
android:textSize="18sp" />
</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>
</RelativeLayout>
<!-- Tabs simulados -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@color/primary"
android:padding="8dp">
<TextView
<!-- Lista de animes -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewAnime"
android:layout_width="0dp"
android:layout_weight="1"
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:layout_height="0dp"
android:padding="8dp"
android:textColor="@color/textPrimary"
android:inputType="text"
android:textSize="14sp" />
android:paddingBottom="96dp"
android:clipToPadding="false"
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
android:id="@+id/buttonFilterMenu"
android:layout_width="0dp"
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"
<!-- FAB correctamente posicionado -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAddAnime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Watching"
android:textColor="@color/textPrimary"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginTop="4dp"
android:layout_marginBottom="2dp" />
<!-- 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>
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
android:layout_marginBottom="40dp"
android:contentDescription="Agregar anime"
android:src="@drawable/ic_add"
android:tint="@android:color/white"
app:backgroundTint="@color/primary" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,16 +1,69 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true"
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_height="match_parent"
android:text="Browse"
android:gravity="center"
android:textSize="30sp"
android:textColor="@color/textPrimary"/>
android:layout_height="wrap_content"
android:orientation="vertical">
</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:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@color/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background"
android:fitsSystemWindows="true"
tools:context=".ui.fragments.manga_list.FragmentManga">
<!-- Cabecera de usuario (opcional) -->
<RelativeLayout
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/sample_cover"
android:padding="12dp">
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageViewBanner"
<!-- Banner superior reducido -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/sample_cover"
android:contentDescription="Profile background" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="bottom"
android:paddingBottom="16dp">
android:layout_height="120dp"
android:background="@drawable/sample_cover"
android:padding="12dp">
<ImageView
android:id="@+id/imageViewAvatar"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_profile"
android:background="@drawable/circle_mask"
android:id="@+id/imageViewBanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:clipToOutline="true"
android:contentDescription="Profile picture" />
android:src="@drawable/sample_cover"
android:contentDescription="Banner" />
<TextView
android:id="@+id/textViewUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BtwIsSanti"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="20sp"
android:gravity="center_vertical"/>
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="bottom|start"
android:paddingBottom="12dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:layout_alignParentBottom="true">
<!-- Filtros y modos 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">
<!-- Avatar -->
<ImageView
android:id="@+id/imageViewAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_profile"
android:background="@drawable/circle_mask"
android:scaleType="centerCrop"
android:clipToOutline="true"
android:contentDescription="Avatar"
android:layout_marginEnd="12dp" />
<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:textColor="@color/textPrimary"
android:inputType="text"
android:textSize="14sp" />
<!-- Nombre -->
<TextView
android:id="@+id/textViewUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Usuario"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="18sp"
android:gravity="center_vertical" />
<ImageView
android:id="@+id/buttonFilterMenu"
android:layout_width="0dp"
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" />
<!-- Botón de cambiar vista -->
<ImageButton
android:id="@+id/btnViewCompact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_view_compact" />
<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/btnViewNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_view_normal" />
<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/btnViewLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_view_large" />
</LinearLayout>
</RelativeLayout>
<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" />
<!-- Lista scrollable -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewManga"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingBottom="100dp"
android:clipToPadding="false"
android:scrollbars="vertical" />
</LinearLayout>
<!-- Estado actual -->
<TextView
android:id="@+id/textViewWatching"
<!-- FAB flotante -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAddManga"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reading"
android:textColor="@color/textPrimary"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginTop="4dp"
android:layout_marginBottom="2dp" />
android:layout_gravity="bottom|end"
android:layout_margin="24dp"
android:contentDescription="Agregar manga"
android:src="@drawable/ic_add"
android:tint="@android:color/white"
app:backgroundTint="@color/primary" />
<!-- Encabezado 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 -->
<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,74 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<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_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center_vertical">
android:padding="12dp">
<ImageView
android:id="@+id/imageViewCover"
android:layout_width="40dp"
android:layout_height="55dp"
android:layout_marginEnd="8dp"
android:id="@+id/imageCover"
android:layout_width="72dp"
android:layout_height="96dp"
android:scaleType="centerCrop"
android:src="@drawable/sample_anime_cover"
android:background="@drawable/rounded_mask"
android:clipToOutline="true" />
android:layout_marginEnd="12dp"
android:contentDescription="Portada" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">
android:layout_weight="1">
<TextView
android:id="@+id/textViewTitle"
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:id="@+id/textTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="5 / 12"
android:textColor="@color/textPrimary"
android:textSize="12sp" />
android:text="Título"
android:textStyle="bold"
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>
<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>

View File

@ -1,26 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
android:padding="12dp">
<TextView
android:id="@+id/textViewTitle"
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:id="@+id/textTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Progress: 5 / 12"
android:textColor="@color/textPrimary"
android:textSize="12sp" />
android:text="Título Compacto"
android:textStyle="bold"
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>

View File

@ -1,48 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="180dp"
android:layout_height="wrap_content"
android:padding="8dp">
android:layout_margin="8dp">
<!-- Imagen de fondo -->
<ImageView
android:id="@+id/imageViewCover"
android:id="@+id/imageCover"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_height="240dp"
android:scaleType="centerCrop"
android:background="@drawable/rounded_mask"
android:clipToOutline="true"
android:src="@drawable/sample_anime_cover" />
android:background="@drawable/rectangle_placeholder"
android:contentDescription="Portada" />
<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_height="wrap_content"
android:layout_gravity="bottom"
android:padding="8dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:weightSum="1"
android:background="#AA000000">
android:background="#99000000"
android:text="Nombre del Anime"
android:textColor="@android:color/white"
android:textSize="14sp"
android:padding="6dp"
android:maxLines="2"
android:ellipsize="end" />
<TextView
android:id="@+id/textViewProgress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="5 / 12"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold" />
<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>
<!-- Indicador de estado (ej: Watching) -->
<View
android:id="@+id/statusDot"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="top|start"
android:layout_margin="6dp"
android:background="@drawable/dot_green"
android:visibility="visible" />
</FrameLayout>

View File

@ -1,74 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<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_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center_vertical">
android:padding="12dp">
<ImageView
android:id="@+id/imageViewCover"
android:layout_width="40dp"
android:layout_height="55dp"
android:layout_marginEnd="8dp"
android:id="@+id/imageCover"
android:layout_width="72dp"
android:layout_height="96dp"
android:scaleType="centerCrop"
android:src="@drawable/sample_anime_cover"
android:background="@drawable/rounded_mask"
android:clipToOutline="true" />
android:layout_marginEnd="12dp"
android:contentDescription="Portada" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">
android:layout_weight="1">
<TextView
android:id="@+id/textViewTitle"
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:id="@+id/textTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="45 / 100"
android:textColor="@color/textPrimary"
android:textSize="12sp" />
android:text="Título"
android:textStyle="bold"
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>
<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>

View File

@ -1,26 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
android:padding="12dp">
<TextView
android:id="@+id/textViewTitle"
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:id="@+id/textTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Progress: 45 / 100"
android:textColor="@color/textPrimary"
android:textSize="12sp" />
android:text="Título Compacto"
android:textStyle="bold"
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>

View File

@ -1,48 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="180dp"
android:layout_height="wrap_content"
android:padding="8dp">
android:layout_margin="8dp">
<!-- Imagen de fondo -->
<ImageView
android:id="@+id/imageViewCover"
android:id="@+id/imageCover"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_height="240dp"
android:scaleType="centerCrop"
android:background="@drawable/rounded_mask"
android:clipToOutline="true"
android:src="@drawable/sample_anime_cover" />
android:background="@drawable/rectangle_placeholder"
android:contentDescription="Portada" />
<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_height="wrap_content"
android:layout_gravity="bottom"
android:padding="8dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:weightSum="1"
android:background="#AA000000">
android:background="#99000000"
android:text="Nombre del Manga"
android:textColor="@android:color/white"
android:textSize="14sp"
android:padding="6dp"
android:maxLines="2"
android:ellipsize="end" />
<TextView
android:id="@+id/textViewProgress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="45 / 100"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold" />
<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>
<!-- Indicador de estado (ej: Watching) -->
<View
android:id="@+id/statusDot"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="top|start"
android:layout_margin="6dp"
android:background="@drawable/dot_green"
android:visibility="visible" />
</FrameLayout>

View File

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

View File

@ -20,4 +20,35 @@
<item name="imageViewCover" type="id" />
<item name="textViewProgress" 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>

View File

@ -7,4 +7,39 @@
<string name="profile">\n</string>
<string name="write_a_status">Write a status...</string>
<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>

View File

@ -1,5 +1,5 @@
[versions]
agp = "8.9.2"
agp = "8.10.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
@ -7,6 +7,8 @@ appcompat = "1.7.0"
material = "1.12.0"
activity = "1.10.1"
constraintlayout = "2.2.1"
navigationFragment = "2.9.0"
navigationUi = "2.9.0"
[libraries]
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" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
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]
android-application = { id = "com.android.application", version.ref = "agp" }