Skip to content

Commit ca81218

Browse files
feat(details): add landscape view and music videos
1 parent 10ec899 commit ca81218

File tree

12 files changed

+511
-14
lines changed

12 files changed

+511
-14
lines changed

.idea/deploymentTargetDropDown.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/java/com/luka/anidroid/activity/AnimeDetailsActivity.java

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import android.os.Build;
1515
import android.os.Bundle;
1616
import android.os.Environment;
17+
import android.os.Handler;
18+
import android.os.Looper;
1719
import android.provider.CalendarContract;
1820
import android.provider.MediaStore;
1921
import android.provider.Settings;
@@ -31,27 +33,47 @@
3133
import androidx.core.app.ActivityCompat;
3234
import androidx.core.content.ContextCompat;
3335
import androidx.core.content.FileProvider;
36+
import androidx.recyclerview.widget.LinearLayoutManager;
37+
import androidx.recyclerview.widget.RecyclerView;
3438

3539
import com.bumptech.glide.Glide;
3640
import com.bumptech.glide.request.target.CustomTarget;
3741
import com.bumptech.glide.request.transition.Transition;
42+
import com.fasterxml.jackson.databind.JsonNode;
43+
import com.fasterxml.jackson.databind.ObjectMapper;
3844
import com.luka.anidroid.R;
45+
import com.luka.anidroid.adapter.MusicVideoAdapter;
3946
import com.luka.anidroid.manager.FavoritesManager;
4047
import com.luka.anidroid.model.Anime;
48+
import com.luka.anidroid.model.MusicVideo;
49+
50+
import org.json.JSONArray;
51+
import org.json.JSONException;
4152

4253
import java.io.File;
4354
import java.io.FileNotFoundException;
4455
import java.io.FileOutputStream;
4556
import java.io.IOException;
4657
import java.io.OutputStream;
58+
import java.util.ArrayList;
59+
import java.util.List;
60+
import java.util.concurrent.ExecutorService;
61+
import java.util.concurrent.Executors;
4762
import java.util.concurrent.TimeUnit;
4863

64+
import okhttp3.OkHttpClient;
65+
import okhttp3.Request;
66+
import okhttp3.Response;
67+
4968
public class AnimeDetailsActivity extends AppCompatActivity {
5069

5170
private FavoritesManager favoritesManager;
5271
private Anime anime;
5372
private Button favoriteButton;
54-
73+
List<MusicVideo> musicVideos;
74+
OkHttpClient client = new OkHttpClient();
75+
ObjectMapper objectMapper = new ObjectMapper();
76+
MusicVideoAdapter musicVideoAdapter;
5577
private static final int WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 1;
5678
private static final int WRITE_CALENDAR_PERMISSION_REQUEST_CODE = 2;
5779

@@ -155,6 +177,12 @@ public void onClick(View v) {
155177
});
156178
TextView episodesTextView = findViewById(R.id.anime_episodes);
157179
favoriteButton = findViewById(R.id.btn_favorite);
180+
TextView durationTextView = findViewById(R.id.anime_duration);
181+
TextView popularityTextView = findViewById(R.id.anime_popularity);
182+
TextView titleNativeTextView = findViewById(R.id.anime_title_native);
183+
TextView seasonTextView = findViewById(R.id.anime_season);
184+
TextView statusTextView = findViewById(R.id.anime_status);
185+
TextView typeTextView = findViewById(R.id.anime_type);
158186

159187
if (anime.getImageUrl() != null && !anime.getImageUrl().isEmpty()) {
160188
Glide.with(this)
@@ -168,10 +196,16 @@ public void onClick(View v) {
168196
.into(imageView);
169197
}
170198
titleTextView.setText(anime.getTitle());
171-
descriptionTextView.setText(anime.getDescription());
172-
scoreTextView.setText(String.valueOf(anime.getAverageScore()));
173-
broadcastDayTextView.setText(anime.getBroadcastDay());
174-
episodesTextView.setText(String.valueOf(anime.getEpisodes()));
199+
descriptionTextView.setText("Description: " + anime.getDescription());
200+
scoreTextView.setText("Score: " + String.valueOf(anime.getAverageScore()));
201+
broadcastDayTextView.setText("Broadcast day: " + anime.getBroadcastDay());
202+
episodesTextView.setText("Number of episodes: " + String.valueOf(anime.getEpisodes()));
203+
durationTextView.setText("Duration: " + anime.getDuration());
204+
popularityTextView.setText("Popularity: " + String.valueOf(anime.getPopularity()));
205+
titleNativeTextView.setText("Title (native): " + anime.getTitleNative());
206+
seasonTextView.setText("Season: " + anime.getSeason());
207+
statusTextView.setText("Status: " + anime.getStatus());
208+
typeTextView.setText("Type: " + anime.getType());
175209

176210
favoriteButton.setOnClickListener(v -> {
177211
if (favoritesManager.isFavorite(anime)) {
@@ -185,6 +219,65 @@ public void onClick(View v) {
185219
});
186220

187221
updateFavoriteButton();
222+
223+
RecyclerView musicVideoRecyclerView = findViewById(R.id.music_video_recycler_view);
224+
musicVideoRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
225+
musicVideos = new ArrayList<>();
226+
fetchMusicVideos(anime.getId());
227+
musicVideoAdapter = new MusicVideoAdapter(musicVideos, this);
228+
musicVideoRecyclerView.setAdapter(musicVideoAdapter);
229+
}
230+
231+
private void fetchMusicVideos(int id) {
232+
ExecutorService executor = Executors.newSingleThreadExecutor();
233+
Handler handler = new Handler(Looper.getMainLooper());
234+
235+
executor.execute(() -> {
236+
try {
237+
Request request = new Request.Builder()
238+
.url("https://api.jikan.moe/v4/anime/" + id + "/videos")
239+
.build();
240+
241+
Response response = client.newCall(request).execute();
242+
String responseBody = response.body().string();
243+
244+
// if the response is not successful, throw an exception
245+
if (!response.isSuccessful()) {
246+
throw new IOException("Unexpected code " + response);
247+
}
248+
Log.d("HomeFragment", "Response: " + responseBody);
249+
250+
JsonNode root = objectMapper.readTree(responseBody);
251+
List<MusicVideo> newAnimeList = new ArrayList<>();
252+
253+
JsonNode data = root.get("data");
254+
Log.d("HomeFragment", "Data: " + data.toString());
255+
Log.d("HomeFragment", "Music videos: " + data.get("music_videos").toString());
256+
JsonNode musicVideosNode = data.get("music_videos");
257+
258+
for (JsonNode animeVideoNode : musicVideosNode) {
259+
MusicVideo animeMusicVideo = new MusicVideo();
260+
animeMusicVideo.setTitle(animeVideoNode.get("title").asText());
261+
animeMusicVideo.setImageUrl(animeVideoNode.get("video").get("images").get("maximum_image_url").asText());
262+
animeMusicVideo.setVideoUrl(animeVideoNode.get("video").get("embed_url").asText());
263+
newAnimeList.add(animeMusicVideo);
264+
}
265+
266+
handler.post(() -> {
267+
musicVideos.clear();
268+
musicVideos.addAll(newAnimeList);
269+
Log.d("HomeFragment", "Number of anime in the newlist: " + newAnimeList.size());
270+
Log.d("HomeFragment", "Number of anime in the list: " + musicVideos.size());
271+
musicVideoAdapter.notifyDataSetChanged();
272+
});
273+
274+
} catch (IOException e) {
275+
e.printStackTrace();
276+
handler.post(() -> {
277+
Toast.makeText(this, "Failed to fetch music videos", Toast.LENGTH_SHORT).show();
278+
});
279+
}
280+
});
188281
}
189282
private void updateFavoriteButton() {
190283
if (favoritesManager.isFavorite(anime)) {

app/src/main/java/com/luka/anidroid/activity/MainActivity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ protected void onCreate(Bundle savedInstanceState) {
8181
return true;
8282
});
8383

84+
lastThemeChangeTime = System.currentTimeMillis();
85+
8486
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
8587
if (sensorManager != null) {
8688
Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.luka.anidroid.adapter;
2+
3+
import android.content.Context;
4+
import android.content.Intent;
5+
import android.net.Uri;
6+
import android.view.LayoutInflater;
7+
import android.view.View;
8+
import android.view.ViewGroup;
9+
import android.widget.ImageView;
10+
import android.widget.TextView;
11+
12+
import androidx.annotation.NonNull;
13+
import androidx.recyclerview.widget.RecyclerView;
14+
15+
import com.bumptech.glide.Glide;
16+
import com.luka.anidroid.R;
17+
import com.luka.anidroid.model.MusicVideo;
18+
19+
import java.util.List;
20+
21+
public class MusicVideoAdapter extends RecyclerView.Adapter<MusicVideoAdapter.MusicVideoViewHolder> {
22+
private List<MusicVideo> musicVideos;
23+
private Context context;
24+
25+
public MusicVideoAdapter(List<MusicVideo> musicVideos, Context context) {
26+
this.musicVideos = musicVideos;
27+
this.context = context;
28+
}
29+
30+
@NonNull
31+
@Override
32+
public MusicVideoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
33+
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_music_video, parent, false);
34+
return new MusicVideoViewHolder(view);
35+
}
36+
37+
@Override
38+
public void onBindViewHolder(@NonNull MusicVideoViewHolder holder, int position) {
39+
MusicVideo musicVideo = musicVideos.get(position);
40+
holder.bind(musicVideo);
41+
}
42+
43+
@Override
44+
public int getItemCount() {
45+
return musicVideos.size();
46+
}
47+
48+
class MusicVideoViewHolder extends RecyclerView.ViewHolder {
49+
ImageView imageView;
50+
TextView textView;
51+
52+
MusicVideoViewHolder(@NonNull View itemView) {
53+
super(itemView);
54+
imageView = itemView.findViewById(R.id.image_view);
55+
textView = itemView.findViewById(R.id.text_view);
56+
}
57+
58+
void bind(MusicVideo musicVideo) {
59+
textView.setText(musicVideo.getTitle());
60+
Glide.with(context)
61+
.load(musicVideo.getImageUrl())
62+
.placeholder(R.drawable.woman)
63+
.error(R.drawable.ic_launcher_foreground)
64+
.into(imageView);
65+
66+
itemView.setOnClickListener(v -> {
67+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(musicVideo.getVideoUrl()));
68+
context.startActivity(intent);
69+
});
70+
}
71+
}
72+
}

app/src/main/java/com/luka/anidroid/fragment/HomeFragment.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ private void fetchAnimeData(List<Anime> animeList, int page) {
128128
anime.setBroadcastDay(animeNode.get("broadcast").get("day").asText());
129129
anime.setBroadcastDay(anime.getBroadcastDay().substring(0, anime.getBroadcastDay().length() - 1));
130130
anime.setUrl(animeNode.get("url").asText());
131+
132+
anime.setDuration(animeNode.get("duration").asText());
133+
anime.setPopularity(animeNode.get("popularity").asInt());
134+
anime.setTitleNative(animeNode.get("title_japanese").asText());
135+
anime.setTitleRomaji(animeNode.get("title_english").asText());
136+
anime.setSeason(animeNode.get("season").asText());
137+
anime.setStatus(animeNode.get("status").asText());
138+
anime.setType(animeNode.get("type").asText());
131139
newAnimeList.add(anime);
132140
}
133141

app/src/main/java/com/luka/anidroid/fragment/SearchFragment.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import androidx.appcompat.widget.SearchView;
1313
import androidx.fragment.app.Fragment;
14+
import androidx.lifecycle.ViewModelProvider;
1415
import androidx.recyclerview.widget.LinearLayoutManager;
1516
import androidx.recyclerview.widget.RecyclerView;
1617

@@ -19,6 +20,7 @@
1920
import com.luka.anidroid.R;
2021
import com.luka.anidroid.adapter.AnimeAdapter;
2122
import com.luka.anidroid.model.Anime;
23+
import com.luka.anidroid.model.SearchViewModel;
2224

2325
import org.json.JSONArray;
2426
import org.json.JSONException;
@@ -38,6 +40,7 @@ public class SearchFragment extends Fragment {
3840
private SearchView searchView;
3941
private RecyclerView recyclerView;
4042
private AnimeAdapter animeAdapter;
43+
private SearchViewModel searchViewModel;
4144

4245
OkHttpClient client = new OkHttpClient();
4346
ObjectMapper objectMapper = new ObjectMapper();
@@ -50,6 +53,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
5053
View view = inflater.inflate(R.layout.activity_search, container, false);
5154
recyclerView = view.findViewById(R.id.recycler_view);
5255

56+
searchViewModel = new ViewModelProvider(requireActivity()).get(SearchViewModel.class);
57+
5358
animeList = new ArrayList<>();
5459
animeAdapter = new AnimeAdapter(animeList);
5560
recyclerView.setAdapter(animeAdapter);
@@ -122,6 +127,14 @@ private void searchAnime(String query) {
122127
anime.setBroadcastDay(animeNode.get("broadcast").get("day").asText());
123128
anime.setBroadcastDay(anime.getBroadcastDay().substring(0, anime.getBroadcastDay().length() - 1));
124129
anime.setUrl(animeNode.get("url").asText());
130+
131+
anime.setDuration(animeNode.get("duration").asText());
132+
anime.setPopularity(animeNode.get("popularity").asInt());
133+
anime.setTitleNative(animeNode.get("title_japanese").asText());
134+
anime.setTitleRomaji(animeNode.get("title_english").asText());
135+
anime.setSeason(animeNode.get("season").asText());
136+
anime.setStatus(animeNode.get("status").asText());
137+
anime.setType(animeNode.get("type").asText());
125138
newAnimeList.add(anime);
126139
}
127140

@@ -130,7 +143,7 @@ private void searchAnime(String query) {
130143
animeList.addAll(newAnimeList);
131144
Log.d("HomeFragment", "Number of anime in the newlist: " + newAnimeList.size());
132145
Log.d("HomeFragment", "Number of anime in the list: " + animeList.size());
133-
//animeAdapter.notifyDataSetChanged();
146+
searchViewModel.setSearchResults(animeList);
134147
animeAdapter.notifyDataSetChanged();
135148
});
136149

@@ -142,4 +155,15 @@ private void searchAnime(String query) {
142155
}
143156
});
144157
}
158+
159+
@Override
160+
public void onResume() {
161+
super.onResume();
162+
163+
// When the fragment is resumed, retrieve the search results from the ViewModel
164+
List<Anime> savedSearchResults = searchViewModel.getSearchResults().getValue();
165+
if (savedSearchResults != null) {
166+
animeAdapter.updateData(savedSearchResults);
167+
}
168+
}
145169
}

app/src/main/java/com/luka/anidroid/model/Anime.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public void setUrl(String url) {
3131
private int id;
3232
private String url;
3333
private double averageScore;
34-
private int duration;
34+
private String duration;
3535
private Date startDate;
3636
private Date endDate;
3737
private int episodes;
@@ -79,7 +79,7 @@ public void setAiring(boolean airing) {
7979
public Anime() {
8080
}
8181

82-
public Anime(boolean isFavourite, int id, int averageScore, int duration, Date startDate, Date endDate, int episodes, int popularity, int seasonYear, String title, String titleNative, String titleRomaji, String format, String season, String status, String type, List<String> genres, List<String> tags, String imageUrl, String description) {
82+
public Anime(boolean isFavourite, int id, int averageScore, String duration, Date startDate, Date endDate, int episodes, int popularity, int seasonYear, String title, String titleNative, String titleRomaji, String format, String season, String status, String type, List<String> genres, List<String> tags, String imageUrl, String description) {
8383
this.isFavourite = isFavourite;
8484
this.id = id;
8585
this.averageScore = averageScore;
@@ -106,7 +106,7 @@ protected Anime(Parcel in) {
106106
id = in.readInt();
107107
url = in.readString();
108108
averageScore = in.readInt();
109-
duration = in.readInt();
109+
duration = in.readString();
110110
episodes = in.readInt();
111111
popularity = in.readInt();
112112
seasonYear = in.readInt();
@@ -139,11 +139,11 @@ public void setAverageScore(double averageScore) {
139139
this.averageScore = averageScore;
140140
}
141141

142-
public int getDuration() {
142+
public String getDuration() {
143143
return duration;
144144
}
145145

146-
public void setDuration(int duration) {
146+
public void setDuration(String duration) {
147147
this.duration = duration;
148148
}
149149

0 commit comments

Comments
 (0)