diff options
author | Clyhtsuriva <aimeric@adjutor.xyz> | 2021-03-14 13:10:10 +0100 |
---|---|---|
committer | Clyhtsuriva <aimeric@adjutor.xyz> | 2021-03-14 13:10:10 +0100 |
commit | dd4ea618de112f527fb96f1aaec7efef75ba9fce (patch) | |
tree | 53cac7d8c627f02dfef171a0388da49864c50887 /app/src | |
parent | f9f079eea806454d49d6088089511d72e1c28f86 (diff) | |
parent | 1d70485121c604300ae371468f2f3c6fbdca395c (diff) |
Merge branch 'feature/search' into develop
Diffstat (limited to 'app/src')
35 files changed, 1682 insertions, 74 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c24711c..4de763c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,14 @@ android:label="Details" android:theme="@style/Theme.Aniki" /> <activity + android:name="xyz.adjutor.aniki.manga.search.DetailSearchMangaActivity" + android:label="Details" + android:theme="@style/Theme.Aniki" /> + <activity + android:name="xyz.adjutor.aniki.anime.search.DetailSearchAnimeActivity" + android:label="Details" + android:theme="@style/Theme.Aniki" /> + <activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/Theme.Aniki.NoActionBar"> diff --git a/app/src/main/java/xyz/adjutor/aniki/HomePage.kt b/app/src/main/java/xyz/adjutor/aniki/HomePage.kt index b7c1d0e..46dac33 100644 --- a/app/src/main/java/xyz/adjutor/aniki/HomePage.kt +++ b/app/src/main/java/xyz/adjutor/aniki/HomePage.kt @@ -27,5 +27,11 @@ class HomePage : Fragment() { view.findViewById<Button>(R.id.button_top_anime).setOnClickListener { findNavController().navigate(R.id.action_HomePage_to_TopAnimePage) } + view.findViewById<Button>(R.id.button_search_manga).setOnClickListener { + findNavController().navigate(R.id.action_HomePage_to_SearchMangaPage) + } + view.findViewById<Button>(R.id.button_search_anime).setOnClickListener { + findNavController().navigate(R.id.action_HomePage_to_SearchAnimePage) + } } }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/AnimeApi.kt b/app/src/main/java/xyz/adjutor/aniki/anime/AnimeApi.kt index 028097a..52ca2ba 100644 --- a/app/src/main/java/xyz/adjutor/aniki/anime/AnimeApi.kt +++ b/app/src/main/java/xyz/adjutor/aniki/anime/AnimeApi.kt @@ -7,6 +7,6 @@ import retrofit2.http.Path interface AnimeApi { @GET("v3/anime/{id}") - fun getAnimeData(@Path("id") id: String): Call<RestAnimeResponse> + fun getAnimeData(@Path("id") id: String): Call<AnimeResponse> }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/RestAnimeResponse.kt b/app/src/main/java/xyz/adjutor/aniki/anime/AnimeResponse.kt index b449b31..9e279e1 100644 --- a/app/src/main/java/xyz/adjutor/aniki/anime/RestAnimeResponse.kt +++ b/app/src/main/java/xyz/adjutor/aniki/anime/AnimeResponse.kt @@ -2,11 +2,15 @@ package xyz.adjutor.aniki.anime import com.google.gson.annotations.SerializedName -class RestAnimeResponse { //only kept the infos I didn't have and that were interesting to me. +class AnimeResponse { //only kept the infos I didn't have and that were interesting to me. @SerializedName("mal_id") var mal_id: Int? = null + @SerializedName("rank") + var rank: Int? = null //added for the search feature (detail) + @SerializedName("synopsis") var synopsis: String? = null + } diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/search/DetailSearchAnimeActivity.kt b/app/src/main/java/xyz/adjutor/aniki/anime/search/DetailSearchAnimeActivity.kt new file mode 100644 index 0000000..2f4a6c2 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/anime/search/DetailSearchAnimeActivity.kt @@ -0,0 +1,156 @@ +package xyz.adjutor.aniki.anime.search + +import android.os.Bundle +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.google.gson.GsonBuilder +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import xyz.adjutor.aniki.R +import xyz.adjutor.aniki.anime.AnimeApi +import xyz.adjutor.aniki.anime.AnimeResponse + +class DetailSearchAnimeActivity : AppCompatActivity() { + + private var baseUrl = "https://api.jikan.moe/" + private val gson = GsonBuilder() + .setLenient() + .create() + + //used in the list + private val intentAnimeImageUrl = "theanimeimageurl" + private val intentAnimeTitle = "theanimetitle" + private val intentAnimeScore = "theanimescore" + + //only used for the detail + private val intentAnimeId = "theanimeid" + private val intentAnimeUrl = "theanimeurl" + private val intentAnimeEpisodes = "theanimeepisodes" + private val intentAnimeStartDate = "theanimestartdate" + private val intentAnimeEndDate = "theanimeenddate" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_detail_search_anime) + + val animeImageUrl = intent.getStringExtra(intentAnimeImageUrl) + val animeTitle = intent.getStringExtra(intentAnimeTitle) + val animeScore = intent.getStringExtra(intentAnimeScore) + + val animeId = intent.getStringExtra(intentAnimeId) + val animeUrl = intent.getStringExtra(intentAnimeUrl) + val animeEpisodes = intent.getStringExtra(intentAnimeEpisodes) + val animeStartDate = intent.getStringExtra(intentAnimeStartDate) + val animeEndDate = intent.getStringExtra(intentAnimeEndDate) + + + val ivImage: ImageView = findViewById(R.id.iv_detail_image) + val tvTitle: TextView = findViewById(R.id.tv_detail_title) + val tvScore: TextView = findViewById(R.id.tv_detail_score) + + val tvId: TextView = findViewById(R.id.tv_detail_id) + val tvUrl: TextView = findViewById(R.id.tv_url) + val tvEpisodes: TextView = findViewById(R.id.tv_episodes) + val tvStartDate: TextView = findViewById(R.id.tv_start_date) + val tvEndDate: TextView = findViewById(R.id.tv_end_date) + + Glide + .with(this) + .load(animeImageUrl) + .apply(RequestOptions().override(400)) + .into(ivImage) + tvTitle.text = animeTitle + tvScore.text = animeScore + + + tvId.text = animeId + tvUrl.text = animeUrl + + //using null as a string because it has been converted to a string before + tvEpisodes.text = if (animeEpisodes != "null") { + animeEpisodes + } else { + fieldIsNull() + } + + tvStartDate.text = splitDate(animeStartDate!!) + + tvEndDate.text = if (animeEndDate != "null") { + splitDate(animeEndDate!!) + } else { + fieldIsNull() + } + + makeApiCall(baseUrl, animeId.toString()) + + } + + private fun splitDate(animeDate: String): CharSequence { + val delimiter = "T" + return animeDate + .split(delimiter) //split between the date and the time + .toTypedArray()[0] //convert it to an array and take the first string + + } + + private fun makeApiCall( + BASE_URL: String, + animeId: String + ) { //we take the rest of the data that we need from the internet + + val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + + val service = retrofit.create(AnimeApi::class.java) + val call = service.getAnimeData(animeId) //based on the id + + call.enqueue(object : Callback<AnimeResponse> { + override fun onResponse( + call: Call<AnimeResponse>, + response: Response<AnimeResponse> + ) { + if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty + + val anime = response.body() //getting the AnimeResponse fields + showDetail(anime!!) + + } else { + showError("API ERROR : is not successful") + } + } + + override fun onFailure(call: Call<AnimeResponse>, t: Throwable) { + showError("API ERROR : onFailure") + } + + }) + } + + private fun showDetail(anime: AnimeResponse) { + //elements from AnimeResponse + val tvSynopsis: TextView = findViewById(R.id.tv_synopsis) + val tvRank: TextView = findViewById(R.id.tv_detail_rank) + + tvSynopsis.text = anime.synopsis.toString() + + tvRank.text = anime.rank.toString() + + } + + fun showError(text: String) { + Toast.makeText(this, text, Toast.LENGTH_LONG).show() + } + + private fun fieldIsNull(): String { + return "Unknown" + } +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnime.kt b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnime.kt new file mode 100644 index 0000000..ad7b7eb --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnime.kt @@ -0,0 +1,31 @@ +package xyz.adjutor.aniki.anime.search + +import com.google.gson.annotations.SerializedName + +class SearchAnime { + + @SerializedName("mal_id") + var mal_id: Int? = null + + @SerializedName("url") + var url: String? = null + + @SerializedName("image_url") + var image_url: String? = null + + @SerializedName("title") + var title: String? = null + + @SerializedName("episodes") + var episodes: Int? = null + + @SerializedName("score") + var score: Float? = null + + @SerializedName("start_date") //we'll maybe remove this later + var start_date: String? = null + + @SerializedName("end_date") //we'll maybe remove this later + var end_date: String? = null + +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeAdapter.kt b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeAdapter.kt new file mode 100644 index 0000000..78718dd --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeAdapter.kt @@ -0,0 +1,81 @@ +package xyz.adjutor.aniki.anime.search + +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import xyz.adjutor.aniki.R + +class SearchAnimeAdapter(private val animeList: List<SearchAnime>) : + RecyclerView.Adapter<SearchAnimeAdapter.AnimeViewHolder>() { + + // Describes an item view and its place within the RecyclerView + class AnimeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val animeTitle: TextView = itemView.findViewById(R.id.tv_title) + val animeRank: TextView = itemView.findViewById(R.id.tv_rank) + val animeScore: TextView = itemView.findViewById(R.id.tv_score) + val animeImage: ImageView = itemView.findViewById(R.id.iv_image) + val cardview: CardView = itemView.findViewById(R.id.cv_cardView) + } + + // Returns a new ViewHolder + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimeViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_layout, parent, false) + + return AnimeViewHolder(view) + } + + // Returns size of data list + override fun getItemCount(): Int { + return animeList.size + } + + // Displays data at a certain position + override fun onBindViewHolder(holder: AnimeViewHolder, position: Int) { + val currentAnime: SearchAnime = animeList[position] + holder.animeTitle.text = currentAnime.title + holder.animeRank.text = "" //the rank isn't supplied by this API + holder.animeScore.text = currentAnime.score.toString() + val image: String = currentAnime.image_url.toString() + Glide + .with(holder.itemView.context) + .load(image) + .apply(RequestOptions().override(400)) + .into(holder.animeImage) + + //when you click on a selected cardview, some datas are sent to the other activity + holder.cardview.setOnClickListener { + val currentAnimeId = "theanimeid" + val currentAnimeUrl = "theanimeurl" + val currentAnimeImageUrl = "theanimeimageurl" + val currentAnimeTitle = "theanimetitle" + val currentAnimeEpisodes = "theanimeepisodes" + val currentAnimeScore = "theanimescore" + val currentAnimeStartDate = "theanimestartdate" + val currentAnimeEndDate = "theanimeenddate" + + //intent is used to pass data to another activity + + val intent: Intent = + Intent(holder.itemView.context, DetailSearchAnimeActivity::class.java).apply { + putExtra(currentAnimeId, currentAnime.mal_id.toString()) + putExtra(currentAnimeUrl, currentAnime.url.toString()) + putExtra(currentAnimeImageUrl, currentAnime.image_url.toString()) + putExtra(currentAnimeTitle, currentAnime.title) + putExtra(currentAnimeEpisodes, currentAnime.episodes.toString()) + putExtra(currentAnimeScore, currentAnime.score.toString()) + putExtra(currentAnimeStartDate, currentAnime.start_date) + putExtra(currentAnimeEndDate, currentAnime.end_date.toString()) + } + holder.itemView.context.startActivity(intent) + } + + } +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeApi.kt b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeApi.kt new file mode 100644 index 0000000..5512636 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeApi.kt @@ -0,0 +1,12 @@ +package xyz.adjutor.aniki.anime.search + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface SearchAnimeApi { + + @GET("v3/search/anime") + fun getSearchAnimeData(@Query("q") q: String): Call<SearchAnimeResponse> + +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimePage.kt b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimePage.kt new file mode 100644 index 0000000..c1596c3 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimePage.kt @@ -0,0 +1,119 @@ +package xyz.adjutor.aniki.anime.search + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.Button +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputEditText +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import xyz.adjutor.aniki.MainActivity +import xyz.adjutor.aniki.R + +class SearchAnimePage : Fragment() { + + val gson: Gson = GsonBuilder() + .setLenient() + .create() + private var baseUrl = "https://api.jikan.moe/" //the api's base url + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + + return inflater.inflate(R.layout.search_anime_page, container, false) + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //button to return to the home page + view.findViewById<Button>(R.id.button_home).setOnClickListener { + findNavController().navigate(R.id.action_SearchAnimePage_to_HomePage) + } + + view.findViewById<Button>(R.id.button_query).setOnClickListener{ + val userInput = view.findViewById<TextInputEditText>(R.id.tiet_query).text.toString() + hideKeyboard() + makeApiCall(view, baseUrl, userInput) + } + } + + private fun hideKeyboard() { + val activity = activity as MainActivity + + val view = activity.currentFocus + if (view != null) { + val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(view.windowToken, 0) + } + } + + //display the recyclerview + fun showList(view: View, animeList: List<SearchAnime>) { + val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view) + recyclerView.setHasFixedSize(true) + recyclerView.layoutManager = LinearLayoutManager(view.context) + recyclerView.adapter = SearchAnimeAdapter(animeList) + (recyclerView.adapter as SearchAnimeAdapter).notifyDataSetChanged() + } + + private fun makeApiCall(view: View, BASE_URL: String, query: String) { + + val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + + val service = retrofit.create(SearchAnimeApi::class.java) + val call = service.getSearchAnimeData(q = query) //fate is an exemple, we'll have to replace it by the user input. + + call.enqueue(object : Callback<SearchAnimeResponse> { + override fun onResponse( + call: Call<SearchAnimeResponse>, + response: Response<SearchAnimeResponse> + ) { + if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty + + val animeList: List<SearchAnime> = response.body()!! + .getResults() //getting the "search" field containing our list of SearchAnimes + + showList( + view, + animeList + ) //calling the method in charge of displaying on the recyclerview + + } else { + showError() //a snackbar + } + } + + override fun onFailure(call: Call<SearchAnimeResponse>, t: Throwable) { + showError() + } + + }) + } + + private fun showError() { + Snackbar.make(requireView(), "API ERROR : Verify your internet connection or your query.", Snackbar.LENGTH_LONG) + .setAction("Action", null).show() + } + +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeResponse.kt b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeResponse.kt new file mode 100644 index 0000000..92100ba --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/anime/search/SearchAnimeResponse.kt @@ -0,0 +1,13 @@ +package xyz.adjutor.aniki.anime.search + +import com.google.gson.annotations.SerializedName + +class SearchAnimeResponse { //only kept the infos I didn't have and that were interesting to me. + + @SerializedName("results") + private lateinit var results: List<SearchAnime> + + fun getResults(): List<SearchAnime> { + return results + } +} diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/DetailTopAnimeActivity.kt b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/DetailTopAnimeActivity.kt index acbbd4d..661937f 100644 --- a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/DetailTopAnimeActivity.kt +++ b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/DetailTopAnimeActivity.kt @@ -18,13 +18,13 @@ import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import xyz.adjutor.aniki.R import xyz.adjutor.aniki.anime.AnimeApi -import xyz.adjutor.aniki.anime.RestAnimeResponse +import xyz.adjutor.aniki.anime.AnimeResponse import java.lang.reflect.Type class DetailTopAnimeActivity : AppCompatActivity() { private var baseUrl = "https://api.jikan.moe/" - var sharedPreferences: SharedPreferences? = null + private lateinit var sharedPreferences: SharedPreferences private val gson = GsonBuilder() .setLenient() .create() @@ -95,7 +95,7 @@ class DetailTopAnimeActivity : AppCompatActivity() { tvUrl.text = animeUrl - val anime: RestAnimeResponse? = getDataFromCache(animeId.toString()) + val anime: AnimeResponse? = getDataFromCache(animeId.toString()) if (anime != null) { showDetail(anime) } else { @@ -105,13 +105,13 @@ class DetailTopAnimeActivity : AppCompatActivity() { } - private fun getDataFromCache(animeId: String): RestAnimeResponse? { - val jsonAnime: String? = sharedPreferences?.getString(animeId, null) + private fun getDataFromCache(animeId: String): AnimeResponse? { + val jsonAnime: String? = sharedPreferences.getString(animeId, null) return if (jsonAnime == null) { null } else { - val type: Type = object : TypeToken<RestAnimeResponse>() {}.type + val type: Type = object : TypeToken<AnimeResponse>() {}.type gson.fromJson(jsonAnime, type) } } @@ -126,14 +126,14 @@ class DetailTopAnimeActivity : AppCompatActivity() { val service = retrofit.create(AnimeApi::class.java) val call = service.getAnimeData(animeId) //based on the id - call.enqueue(object : Callback<RestAnimeResponse> { + call.enqueue(object : Callback<AnimeResponse> { override fun onResponse( - call: Call<RestAnimeResponse>, - response: Response<RestAnimeResponse> + call: Call<AnimeResponse>, + response: Response<AnimeResponse> ) { if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty - val anime = response.body() //getting the RestAnimeResponse fields + val anime = response.body() //getting the AnimeResponse fields saveList(anime) showDetail(anime!!) @@ -142,15 +142,15 @@ class DetailTopAnimeActivity : AppCompatActivity() { } } - override fun onFailure(call: Call<RestAnimeResponse>, t: Throwable) { + override fun onFailure(call: Call<AnimeResponse>, t: Throwable) { showError("API ERROR : onFailure") } }) } - private fun showDetail(anime: RestAnimeResponse) { - //elements from RestAnimeResponse + private fun showDetail(anime: AnimeResponse) { + //elements from AnimeResponse val tvSynopsis: TextView = findViewById(R.id.tv_synopsis) tvSynopsis.text = anime.synopsis.toString() @@ -165,12 +165,12 @@ class DetailTopAnimeActivity : AppCompatActivity() { return "Unknown" } - fun saveList(anime: RestAnimeResponse?) { + fun saveList(anime: AnimeResponse?) { val jsonString: String = gson.toJson(anime) sharedPreferences - ?.edit() - ?.putString(anime?.mal_id.toString(), jsonString) - ?.apply() + .edit() + .putString(anime?.mal_id.toString(), jsonString) + .apply() } }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimeApi.kt b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimeApi.kt index a5b3b59..8e44e77 100644 --- a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimeApi.kt +++ b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimeApi.kt @@ -6,6 +6,6 @@ import retrofit2.http.GET interface TopAnimeApi { @GET("v3/top/anime") - fun getTopAnimeData(): Call<RestTopAnimeResponse> + fun getTopAnimeData(): Call<TopAnimeResponse> }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimePage.kt b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimePage.kt index ccae2fa..d62e5b2 100644 --- a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimePage.kt +++ b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimePage.kt @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.snackbar.Snackbar +import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken import retrofit2.Call @@ -25,11 +26,11 @@ import java.lang.reflect.Type class TopAnimePage : Fragment() { - var sharedPreferences: SharedPreferences? = null - val gson = GsonBuilder() + private lateinit var sharedPreferences: SharedPreferences + val gson: Gson = GsonBuilder() .setLenient() .create() - var base_url = "https://api.jikan.moe/" //the api's base url + var baseUrl = "https://api.jikan.moe/" //the api's base url override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -44,7 +45,7 @@ class TopAnimePage : Fragment() { if (animeList != null) { showList(view, animeList) } else { - makeApiCall(view, base_url) + makeApiCall(view, baseUrl) } return view @@ -52,14 +53,14 @@ class TopAnimePage : Fragment() { private fun getDataFromCache(): List<TopAnime>? { //the value of the animeList json, if nothing is found, return null - val jsonAnime: String? = sharedPreferences?.getString("jsonAnimeList", null) + val jsonAnime: String? = sharedPreferences.getString("jsonAnimeList", null) //if it's null, well, return null - if (jsonAnime == null) { - return null + return if (jsonAnime == null) { + null } else { //else deserialize the list and return it val listType: Type = object : TypeToken<List<TopAnime>>() {}.type - return gson.fromJson(jsonAnime, listType) + gson.fromJson(jsonAnime, listType) } } @@ -72,7 +73,7 @@ class TopAnimePage : Fragment() { } fun updateList() { - makeApiCall(view, base_url) + makeApiCall(view, baseUrl) Snackbar.make(requireView(), "Data refreshed", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } @@ -91,6 +92,7 @@ class TopAnimePage : Fragment() { recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(view.context) recyclerView.adapter = TopAnimeAdapter(animeList) + (recyclerView.adapter as TopAnimeAdapter).notifyDataSetChanged() } private fun makeApiCall(view: View, BASE_URL: String) { @@ -103,10 +105,10 @@ class TopAnimePage : Fragment() { val service = retrofit.create(TopAnimeApi::class.java) val call = service.getTopAnimeData() - call.enqueue(object : Callback<RestTopAnimeResponse> { + call.enqueue(object : Callback<TopAnimeResponse> { override fun onResponse( - call: Call<RestTopAnimeResponse>, - response: Response<RestTopAnimeResponse> + call: Call<TopAnimeResponse>, + response: Response<TopAnimeResponse> ) { if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty @@ -123,7 +125,7 @@ class TopAnimePage : Fragment() { } } - override fun onFailure(call: Call<RestTopAnimeResponse>, t: Throwable) { + override fun onFailure(call: Call<TopAnimeResponse>, t: Throwable) { showError() } @@ -134,9 +136,9 @@ class TopAnimePage : Fragment() { val jsonString: String = gson.toJson(animeList) sharedPreferences - ?.edit() - ?.putString("jsonAnimeList", jsonString) - ?.apply() + .edit() + .putString("jsonAnimeList", jsonString) + .apply() } private fun showError() { diff --git a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/RestTopAnimeResponse.kt b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimeResponse.kt index edc11cf..2fe69d6 100644 --- a/app/src/main/java/xyz/adjutor/aniki/anime/topanime/RestTopAnimeResponse.kt +++ b/app/src/main/java/xyz/adjutor/aniki/anime/topanime/TopAnimeResponse.kt @@ -2,7 +2,7 @@ package xyz.adjutor.aniki.anime.topanime import com.google.gson.annotations.SerializedName -class RestTopAnimeResponse { +class TopAnimeResponse { @SerializedName("top") var top: List<TopAnime>? = null diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/MangaApi.kt b/app/src/main/java/xyz/adjutor/aniki/manga/MangaApi.kt index 419d510..f1b51a4 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/MangaApi.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/MangaApi.kt @@ -7,6 +7,6 @@ import retrofit2.http.Path interface MangaApi { @GET("v3/manga/{id}") - fun getMangaData(@Path("id") id: String): Call<RestMangaResponse> + fun getMangaData(@Path("id") id: String): Call<MangaResponse> }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/RestMangaResponse.kt b/app/src/main/java/xyz/adjutor/aniki/manga/MangaResponse.kt index 10df7a5..263b93c 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/RestMangaResponse.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/MangaResponse.kt @@ -2,7 +2,7 @@ package xyz.adjutor.aniki.manga import com.google.gson.annotations.SerializedName -class RestMangaResponse { //only kept the infos I didn't have and that were interesting to me. +class MangaResponse { //only kept the infos I didn't have and that were interesting to me. @SerializedName("mal_id") var mal_id: Int? = null @@ -13,6 +13,9 @@ class RestMangaResponse { //only kept the infos I didn't have and that were inte @SerializedName("synopsis") var synopsis: String? = null + @SerializedName("rank") + var rank: Int? = null //added for the search feature (detail) + @SerializedName("background") var background: String? = null //a bit of background story about the manga diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/search/DetailSearchMangaActivity.kt b/app/src/main/java/xyz/adjutor/aniki/manga/search/DetailSearchMangaActivity.kt new file mode 100644 index 0000000..4ae01f8 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/manga/search/DetailSearchMangaActivity.kt @@ -0,0 +1,169 @@ +package xyz.adjutor.aniki.manga.search + +import android.os.Bundle +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.google.gson.GsonBuilder +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import xyz.adjutor.aniki.R +import xyz.adjutor.aniki.manga.MangaApi +import xyz.adjutor.aniki.manga.MangaResponse + +class DetailSearchMangaActivity : AppCompatActivity() { + + private var baseUrl = "https://api.jikan.moe/" + private val gson = GsonBuilder() + .setLenient() + .create() + + //used in the list + private val intentMangaImageUrl = "themangaimageurl" + private val intentMangaTitle = "themangatitle" + private val intentMangaScore = "themangascore" + + //only used for the detail + private val intentMangaId = "themangaid" + private val intentMangaUrl = "themangaurl" + private val intentMangaChapters = "themangachapters" + private val intentMangaVolumes = "themangavolumes" + private val intentMangaStartDate = "themangastartdate" + private val intentMangaEndDate = "themangaenddate" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_detail_search_manga) + + val mangaImageUrl = intent.getStringExtra(intentMangaImageUrl) + val mangaTitle = intent.getStringExtra(intentMangaTitle) + val mangaScore = intent.getStringExtra(intentMangaScore) + + val mangaId = intent.getStringExtra(intentMangaId) + val mangaUrl = intent.getStringExtra(intentMangaUrl) + val mangaChapters = intent.getStringExtra(intentMangaChapters) + val mangaVolumes = intent.getStringExtra(intentMangaVolumes) + val mangaStartDate = intent.getStringExtra(intentMangaStartDate) + val mangaEndDate = intent.getStringExtra(intentMangaEndDate) + + + val ivImage: ImageView = findViewById(R.id.iv_detail_image) + val tvTitle: TextView = findViewById(R.id.tv_detail_title) + val tvScore: TextView = findViewById(R.id.tv_detail_score) + + val tvId: TextView = findViewById(R.id.tv_detail_id) + val tvUrl: TextView = findViewById(R.id.tv_url) + val tvChapters: TextView = findViewById(R.id.tv_chapters) + val tvVolumes: TextView = findViewById(R.id.tv_volumes) + val tvStartDate: TextView = findViewById(R.id.tv_start_date) + val tvEndDate: TextView = findViewById(R.id.tv_end_date) + + Glide + .with(this) + .load(mangaImageUrl) + .apply(RequestOptions().override(400)) + .into(ivImage) + tvTitle.text = mangaTitle + tvScore.text = mangaScore + + + tvId.text = mangaId + tvUrl.text = mangaUrl + + //using null as a string because it has been converted to a string before + tvChapters.text = if (mangaChapters != "null") { + mangaChapters + } else { + fieldIsNull() + } + + tvVolumes.text = if (mangaVolumes != "null") { + mangaVolumes + } else { + fieldIsNull() + } + + tvStartDate.text = splitDate(mangaStartDate!!) + + tvEndDate.text = if (mangaEndDate != "null") { + splitDate(mangaEndDate!!) + } else { + fieldIsNull() + } + + makeApiCall(baseUrl, mangaId.toString()) + + } + + private fun splitDate(mangaDate: String): CharSequence { + val delimiter = "T" + return mangaDate + .split(delimiter) //split between the date and the time + .toTypedArray()[0] //convert it to an array and take the first string + + } + + private fun makeApiCall(BASE_URL: String, mangaId: String) { + + val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + + val service = retrofit.create(MangaApi::class.java) + val call = service.getMangaData(mangaId) //based on the id + + call.enqueue(object : Callback<MangaResponse> { + override fun onResponse( + call: Call<MangaResponse>, + response: Response<MangaResponse> + ) { + if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty + + val manga = response.body() //getting the MangaResponse fields + showDetail(manga!!) + + } else { + showError("API ERROR : is not successful") + } + } + + override fun onFailure(call: Call<MangaResponse>, t: Throwable) { + showError("API ERROR : onFailure") + } + + }) + } + + private fun showDetail(manga: MangaResponse) { + //elements from MangaResponse + val tvSynopsis: TextView = findViewById(R.id.tv_synopsis) + val tvRank: TextView = findViewById(R.id.tv_detail_rank) + val tvBackground: TextView = findViewById(R.id.tv_background) + + tvSynopsis.text = manga.synopsis.toString() + + tvRank.text = manga.rank.toString() + + tvBackground.text = if (manga.background != null) { + manga.background.toString() + } else { + fieldIsNull() + } + + } + + fun showError(text: String) { + Toast.makeText(this, text, Toast.LENGTH_LONG).show() + } + + private fun fieldIsNull(): String { + return "Unknown" + } +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchManga.kt b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchManga.kt new file mode 100644 index 0000000..518a0a6 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchManga.kt @@ -0,0 +1,34 @@ +package xyz.adjutor.aniki.manga.search + +import com.google.gson.annotations.SerializedName + +class SearchManga { + + @SerializedName("mal_id") + var mal_id: Int? = null + + @SerializedName("url") + var url: String? = null + + @SerializedName("image_url") + var image_url: String? = null + + @SerializedName("title") + var title: String? = null + + @SerializedName("chapters") + var chapters: Int? = null + + @SerializedName("volumes") + var volumes: Int? = null + + @SerializedName("score") + var score: Float? = null + + @SerializedName("start_date") //we'll maybe remove this later + var start_date: String? = null + + @SerializedName("end_date") //we'll maybe remove this later + var end_date: String? = null + +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaAdapter.kt b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaAdapter.kt new file mode 100644 index 0000000..fb95c9c --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaAdapter.kt @@ -0,0 +1,83 @@ +package xyz.adjutor.aniki.manga.search + +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import xyz.adjutor.aniki.R + +class SearchMangaAdapter(private val mangaList: List<SearchManga>) : + RecyclerView.Adapter<SearchMangaAdapter.MangaViewHolder>() { + + // Describes an item view and its place within the RecyclerView + class MangaViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val mangaTitle: TextView = itemView.findViewById(R.id.tv_title) + val mangaRank: TextView = itemView.findViewById(R.id.tv_rank) + val mangaScore: TextView = itemView.findViewById(R.id.tv_score) + val mangaImage: ImageView = itemView.findViewById(R.id.iv_image) + val cardview: CardView = itemView.findViewById(R.id.cv_cardView) + } + + // Returns a new ViewHolder + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MangaViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_layout, parent, false) + + return MangaViewHolder(view) + } + + // Returns size of data list + override fun getItemCount(): Int { + return mangaList.size + } + + // Displays data at a certain position + override fun onBindViewHolder(holder: MangaViewHolder, position: Int) { + val currentManga: SearchManga = mangaList[position] + holder.mangaTitle.text = currentManga.title + holder.mangaRank.text = "" //the rank isn't supplied by this API + holder.mangaScore.text = currentManga.score.toString() + val image: String = currentManga.image_url.toString() + Glide + .with(holder.itemView.context) + .load(image) + .apply(RequestOptions().override(400)) + .into(holder.mangaImage) + + //when you click on a selected cardview, some datas are sent to the other activity + holder.cardview.setOnClickListener { + val currentMangaId = "themangaid" + val currentMangaUrl = "themangaurl" + val currentMangaImageUrl = "themangaimageurl" + val currentMangaTitle = "themangatitle" + val currentMangaChapters = "themangachapters" + val currentMangaVolumes = "themangavolumes" + val currentMangaScore = "themangascore" + val currentMangaStartDate = "themangastartdate" + val currentMangaEndDate = "themangaenddate" + + //intent is used to pass data to another activity + + val intent: Intent = + Intent(holder.itemView.context, DetailSearchMangaActivity::class.java).apply { + putExtra(currentMangaId, currentManga.mal_id.toString()) + putExtra(currentMangaUrl, currentManga.url.toString()) + putExtra(currentMangaImageUrl, currentManga.image_url.toString()) + putExtra(currentMangaTitle, currentManga.title) + putExtra(currentMangaChapters, currentManga.chapters.toString()) + putExtra(currentMangaVolumes, currentManga.volumes.toString()) + putExtra(currentMangaScore, currentManga.score.toString()) + putExtra(currentMangaStartDate, currentManga.start_date) + putExtra(currentMangaEndDate, currentManga.end_date.toString()) + } + holder.itemView.context.startActivity(intent) + } + + } +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaApi.kt b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaApi.kt new file mode 100644 index 0000000..6f3e63d --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaApi.kt @@ -0,0 +1,12 @@ +package xyz.adjutor.aniki.manga.search + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface SearchMangaApi { + + @GET("v3/search/manga") + fun getSearchMangaData(@Query("q") q: String): Call<SearchMangaResponse> + +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaPage.kt b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaPage.kt new file mode 100644 index 0000000..f79db36 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaPage.kt @@ -0,0 +1,120 @@ +package xyz.adjutor.aniki.manga.search + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.Button +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputEditText +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import xyz.adjutor.aniki.MainActivity +import xyz.adjutor.aniki.R + +class SearchMangaPage : Fragment() { + + val gson: Gson = GsonBuilder() + .setLenient() + .create() + private var baseUrl = "https://api.jikan.moe/" //the api's base url + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + + return inflater.inflate(R.layout.search_manga_page, container, false) + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //button to return to the home page + view.findViewById<Button>(R.id.button_home).setOnClickListener { + findNavController().navigate(R.id.action_SearchMangaPage_to_HomePage) + } + + view.findViewById<Button>(R.id.button_query).setOnClickListener{ + val userInput = view.findViewById<TextInputEditText>(R.id.tiet_query).text.toString() + hideKeyboard() + makeApiCall(view, baseUrl, userInput) + } + } + + private fun hideKeyboard() { + val activity = activity as MainActivity + + val view = activity.currentFocus + if (view != null) { + val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(view.windowToken, 0) + } + } + + + //display the recyclerview + fun showList(view: View, mangaList: List<SearchManga>) { + val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view) + recyclerView.setHasFixedSize(true) + recyclerView.layoutManager = LinearLayoutManager(view.context) + recyclerView.adapter = SearchMangaAdapter(mangaList) + (recyclerView.adapter as SearchMangaAdapter).notifyDataSetChanged() + } + + private fun makeApiCall(view: View, BASE_URL: String, query: String) { + + val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + + val service = retrofit.create(SearchMangaApi::class.java) + val call = service.getSearchMangaData(q = query) //fate is an exemple, we'll have to replace it by the user input. + + call.enqueue(object : Callback<SearchMangaResponse> { + override fun onResponse( + call: Call<SearchMangaResponse>, + response: Response<SearchMangaResponse> + ) { + if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty + + val mangaList: List<SearchManga> = response.body()!! + .getResults() //getting the "search" field containing our list of SearchMangas + + showList( + view, + mangaList + ) //calling the method in charge of displaying on the recyclerview + + } else { + showError() //a snackbar + } + } + + override fun onFailure(call: Call<SearchMangaResponse>, t: Throwable) { + showError() + } + + }) + } + + private fun showError() { + Snackbar.make(requireView(), "API ERROR : Verify your internet connection or your query.", Snackbar.LENGTH_LONG) + .setAction("Action", null).show() + } + +}
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaResponse.kt b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaResponse.kt new file mode 100644 index 0000000..7721258 --- /dev/null +++ b/app/src/main/java/xyz/adjutor/aniki/manga/search/SearchMangaResponse.kt @@ -0,0 +1,13 @@ +package xyz.adjutor.aniki.manga.search + +import com.google.gson.annotations.SerializedName + +class SearchMangaResponse { //only kept the infos I didn't have and that were interesting to me. + + @SerializedName("results") + private lateinit var results: List<SearchManga> + + fun getResults(): List<SearchManga> { + return results + } +} diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/DetailTopMangaActivity.kt b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/DetailTopMangaActivity.kt index c7b6317..6d89437 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/DetailTopMangaActivity.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/DetailTopMangaActivity.kt @@ -18,23 +18,25 @@ import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import xyz.adjutor.aniki.R import xyz.adjutor.aniki.manga.MangaApi -import xyz.adjutor.aniki.manga.RestMangaResponse +import xyz.adjutor.aniki.manga.MangaResponse import java.lang.reflect.Type class DetailTopMangaActivity : AppCompatActivity() { private var baseUrl = "https://api.jikan.moe/" - var sharedPreferences: SharedPreferences? = null + private lateinit var sharedPreferences: SharedPreferences private val gson = GsonBuilder() .setLenient() .create() - private val intentMangaId = "themangaid" + //used in the list private val intentMangaTitle = "themangatitle" private val intentMangaRank = "themangarank" private val intentMangaScore = "themangascore" private val intentMangaImageUrl = "themangaimageurl" + //only used for the detail + private val intentMangaId = "themangaid" private val intentMangaVolumes = "themangavolumes" private val intentMangaStartDate = "themangastartdate" private val intentMangaEndDate = "themangaenddate" @@ -46,29 +48,28 @@ class DetailTopMangaActivity : AppCompatActivity() { sharedPreferences = this.getSharedPreferences("sp_manga", Context.MODE_PRIVATE) - val mangaId = intent.getStringExtra(intentMangaId) val mangaTitle = intent.getStringExtra(intentMangaTitle) val mangaRank = intent.getStringExtra(intentMangaRank) val mangaScore = intent.getStringExtra(intentMangaScore) val mangaImageUrl = intent.getStringExtra(intentMangaImageUrl) + val mangaId = intent.getStringExtra(intentMangaId) val mangaVolumes = intent.getStringExtra(intentMangaVolumes) val mangaStartDate = intent.getStringExtra(intentMangaStartDate) val mangaEndDate = intent.getStringExtra(intentMangaEndDate) val mangaUrl = intent.getStringExtra(intentMangaUrl) - val tvId: TextView = findViewById(R.id.tv_detail_id) val tvTitle: TextView = findViewById(R.id.tv_detail_title) val tvRank: TextView = findViewById(R.id.tv_detail_rank) val tvScore: TextView = findViewById(R.id.tv_detail_score) val ivImage: ImageView = findViewById(R.id.iv_detail_image) + val tvId: TextView = findViewById(R.id.tv_detail_id) val tvVolumes: TextView = findViewById(R.id.tv_volumes) val tvStartDate: TextView = findViewById(R.id.tv_start_date) val tvEndDate: TextView = findViewById(R.id.tv_end_date) val tvUrl: TextView = findViewById(R.id.tv_url) - tvId.text = mangaId tvTitle.text = mangaTitle tvRank.text = mangaRank tvScore.text = mangaScore @@ -78,6 +79,8 @@ class DetailTopMangaActivity : AppCompatActivity() { .apply(RequestOptions().override(400)) .into(ivImage) + tvId.text = mangaId + //using null as a string because it has been converted to a string before tvVolumes.text = if (mangaVolumes != "null") { mangaVolumes @@ -95,7 +98,7 @@ class DetailTopMangaActivity : AppCompatActivity() { tvUrl.text = mangaUrl - val manga: RestMangaResponse? = getDataFromCache(mangaId.toString()) + val manga: MangaResponse? = getDataFromCache(mangaId.toString()) if (manga != null) { showDetail(manga) } else { @@ -105,13 +108,13 @@ class DetailTopMangaActivity : AppCompatActivity() { } - private fun getDataFromCache(mangaId: String): RestMangaResponse? { - val jsonManga: String? = sharedPreferences?.getString(mangaId, null) + private fun getDataFromCache(mangaId: String): MangaResponse? { + val jsonManga: String? = sharedPreferences.getString(mangaId, null) return if (jsonManga == null) { null } else { - val type: Type = object : TypeToken<RestMangaResponse>() {}.type + val type: Type = object : TypeToken<MangaResponse>() {}.type gson.fromJson(jsonManga, type) } } @@ -126,14 +129,14 @@ class DetailTopMangaActivity : AppCompatActivity() { val service = retrofit.create(MangaApi::class.java) val call = service.getMangaData(mangaId) //based on the id - call.enqueue(object : Callback<RestMangaResponse> { + call.enqueue(object : Callback<MangaResponse> { override fun onResponse( - call: Call<RestMangaResponse>, - response: Response<RestMangaResponse> + call: Call<MangaResponse>, + response: Response<MangaResponse> ) { if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty - val manga = response.body() //getting the RestMangaResponse fields + val manga = response.body() //getting the MangaResponse fields saveList(manga) showDetail(manga!!) @@ -142,15 +145,15 @@ class DetailTopMangaActivity : AppCompatActivity() { } } - override fun onFailure(call: Call<RestMangaResponse>, t: Throwable) { + override fun onFailure(call: Call<MangaResponse>, t: Throwable) { showError("API ERROR : onFailure") } }) } - private fun showDetail(manga: RestMangaResponse) { - //elements from RestMangaResponse + private fun showDetail(manga: MangaResponse) { + //elements from MangaResponse val tvChapters: TextView = findViewById(R.id.tv_chapters) val tvSynopsis: TextView = findViewById(R.id.tv_synopsis) val tvBackground: TextView = findViewById(R.id.tv_background) @@ -179,12 +182,12 @@ class DetailTopMangaActivity : AppCompatActivity() { return "Unknown" } - fun saveList(manga: RestMangaResponse?) { + fun saveList(manga: MangaResponse?) { val jsonString: String = gson.toJson(manga) sharedPreferences - ?.edit() - ?.putString(manga?.mal_id.toString(), jsonString) - ?.apply() + .edit() + .putString(manga?.mal_id.toString(), jsonString) + .apply() } }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaAdapter.kt b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaAdapter.kt index 626f34e..d0c56f7 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaAdapter.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaAdapter.kt @@ -62,6 +62,7 @@ class TopMangaAdapter(private val mangaList: List<TopManga>) : val currentMangaEndDate = "themangaenddate" val currentMangaUrl = "themangaurl" + //intent is used to pass data to another activity val intent: Intent = Intent(holder.itemView.context, DetailTopMangaActivity::class.java).apply { putExtra(currentMangaId, currentManga.mal_id.toString()) diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaApi.kt b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaApi.kt index 278c3e1..629fe02 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaApi.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaApi.kt @@ -6,6 +6,6 @@ import retrofit2.http.GET interface TopMangaApi { @GET("v3/top/manga") - fun getTopMangaData(): Call<RestTopMangaResponse> + fun getTopMangaData(): Call<TopMangaResponse> }
\ No newline at end of file diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaPage.kt b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaPage.kt index ce9b1c6..b35bcdd 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaPage.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaPage.kt @@ -26,7 +26,7 @@ import java.lang.reflect.Type class TopMangaPage : Fragment() { - var sharedPreferences: SharedPreferences? = null + private lateinit var sharedPreferences: SharedPreferences val gson: Gson = GsonBuilder() .setLenient() .create() @@ -53,7 +53,7 @@ class TopMangaPage : Fragment() { private fun getDataFromCache(): List<TopManga>? { //the value of the mangaList json, if nothing is found, return null - val jsonManga: String? = sharedPreferences?.getString("jsonMangaList", null) + val jsonManga: String? = sharedPreferences.getString("jsonMangaList", null) //if it's null, well, return null return if (jsonManga == null) { @@ -92,6 +92,7 @@ class TopMangaPage : Fragment() { recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(view.context) recyclerView.adapter = TopMangaAdapter(mangaList) + (recyclerView.adapter as TopMangaAdapter).notifyDataSetChanged() } private fun makeApiCall(view: View, BASE_URL: String) { @@ -104,10 +105,10 @@ class TopMangaPage : Fragment() { val service = retrofit.create(TopMangaApi::class.java) val call = service.getTopMangaData() - call.enqueue(object : Callback<RestTopMangaResponse> { + call.enqueue(object : Callback<TopMangaResponse> { override fun onResponse( - call: Call<RestTopMangaResponse>, - response: Response<RestTopMangaResponse> + call: Call<TopMangaResponse>, + response: Response<TopMangaResponse> ) { if (response.isSuccessful && response.body() != null) { //if the code returned is >= 200 and < 300 AND the the body ain't empty @@ -124,7 +125,7 @@ class TopMangaPage : Fragment() { } } - override fun onFailure(call: Call<RestTopMangaResponse>, t: Throwable) { + override fun onFailure(call: Call<TopMangaResponse>, t: Throwable) { showError() } @@ -135,9 +136,9 @@ class TopMangaPage : Fragment() { val jsonString: String = gson.toJson(mangaList) sharedPreferences - ?.edit() - ?.putString("jsonMangaList", jsonString) - ?.apply() + .edit() + .putString("jsonMangaList", jsonString) + .apply() } private fun showError() { diff --git a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/RestTopMangaResponse.kt b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaResponse.kt index 87c5cfb..8577050 100644 --- a/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/RestTopMangaResponse.kt +++ b/app/src/main/java/xyz/adjutor/aniki/manga/topmanga/TopMangaResponse.kt @@ -2,12 +2,12 @@ package xyz.adjutor.aniki.manga.topmanga import com.google.gson.annotations.SerializedName -class RestTopMangaResponse { +class TopMangaResponse { @SerializedName("top") - var top: List<TopManga>? = null + private lateinit var top: List<TopManga> fun getResults(): List<TopManga> { - return top!! + return top } }
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_detail_search_anime.xml b/app/src/main/res/layout/activity_detail_search_anime.xml new file mode 100644 index 0000000..90f2459 --- /dev/null +++ b/app/src/main/res/layout/activity_detail_search_anime.xml @@ -0,0 +1,259 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/very_dark_purple"> + + <androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:context=".anime.searchanime.DetailSearchAnimeActivity"> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_header" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toTopOf="parent"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:text="@string/text_mal_id" + android:textColor="@color/strong_pink" + app:layout_constraintEnd_toStartOf="@id/tv_detail_id" /> + + <TextView + android:id="@+id/tv_detail_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/id" + android:textColor="@color/slightly_desaturated_magenta" + android:textIsSelectable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/iv_detail_image" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/iv_detail_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/image" + android:src="@mipmap/ic_launcher" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_detail_title" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_marginHorizontal="10sp" + android:fontFamily="@font/bangers" + android:text="@string/title" + android:textAlignment="center" + android:textColor="@color/strong_pink" + android:textIsSelectable="true" + android:textSize="30sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/iv_detail_image" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_rank" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_detail_rank" + app:layout_constraintEnd_toStartOf="@id/tv_detail_rank" + app:layout_constraintTop_toTopOf="@id/tv_detail_rank" /> + + <TextView + android:id="@+id/tv_detail_rank" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/rank" + android:textColor="@color/slightly_desaturated_magenta" + android:textSize="20sp" + app:layout_constraintBottom_toTopOf="@id/tv_detail_title" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_score" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_detail_score" + app:layout_constraintEnd_toStartOf="@id/tv_detail_score" + app:layout_constraintTop_toTopOf="@id/tv_detail_score" /> + + <TextView + android:id="@+id/tv_detail_score" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/score" + android:textColor="@color/slightly_desaturated_magenta" + android:textSize="20sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_detail_title" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_more_infos" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_header"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + <TextView + android:id="@+id/tv_text_episodes" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_episodes" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_episodes" + app:layout_constraintEnd_toStartOf="@id/tv_episodes" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/tv_episodes" /> + + <TextView + android:id="@+id/tv_episodes" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/episodes" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/tv_text_episodes" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_text_start_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_start_date" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_start_date" + app:layout_constraintEnd_toStartOf="@id/tv_start_date" + app:layout_constraintTop_toTopOf="@id/tv_start_date" /> + + <TextView + android:id="@+id/tv_start_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/start_date" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_text_end_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_end_date" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_end_date" + app:layout_constraintEnd_toStartOf="@id/tv_end_date" + app:layout_constraintTop_toTopOf="@id/tv_end_date" /> + + <TextView + android:id="@+id/tv_end_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/end_date" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_start_date" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_synopsis" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_more_infos"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + <TextView + android:id="@+id/tv_text_synopsis" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_synopsis" + android:textColor="@color/strong_pink" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_synopsis" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/slightly_desaturated_magenta" + android:textIsSelectable="true" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_text_synopsis" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_synopsis"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + + <TextView + android:id="@+id/tv_text_url" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_url" + android:textColor="@color/strong_pink" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_url" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:autoLink="all" + android:text="@string/url" + android:textColor="@color/slightly_desaturated_magenta" + android:textColorLink="@color/very_light_magenta" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_text_url" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + </androidx.constraintlayout.widget.ConstraintLayout> +</ScrollView>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_detail_search_manga.xml b/app/src/main/res/layout/activity_detail_search_manga.xml new file mode 100644 index 0000000..75cad74 --- /dev/null +++ b/app/src/main/res/layout/activity_detail_search_manga.xml @@ -0,0 +1,313 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/very_dark_purple"> + + <androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:context=".manga.searchmanga.DetailSearchMangaActivity"> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_header" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toTopOf="parent"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:text="@string/text_mal_id" + android:textColor="@color/strong_pink" + app:layout_constraintEnd_toStartOf="@id/tv_detail_id" /> + + <TextView + android:id="@+id/tv_detail_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/id" + android:textColor="@color/slightly_desaturated_magenta" + android:textIsSelectable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/iv_detail_image" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/iv_detail_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/image" + android:src="@mipmap/ic_launcher" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_detail_title" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_marginHorizontal="10sp" + android:fontFamily="@font/bangers" + android:text="@string/title" + android:textAlignment="center" + android:textColor="@color/strong_pink" + android:textIsSelectable="true" + android:textSize="30sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/iv_detail_image" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_rank" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_detail_rank" + app:layout_constraintEnd_toStartOf="@id/tv_detail_rank" + app:layout_constraintTop_toTopOf="@id/tv_detail_rank" /> + + <TextView + android:id="@+id/tv_detail_rank" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/rank" + android:textColor="@color/slightly_desaturated_magenta" + android:textSize="20sp" + app:layout_constraintBottom_toTopOf="@id/tv_detail_title" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_score" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_detail_score" + app:layout_constraintEnd_toStartOf="@id/tv_detail_score" + app:layout_constraintTop_toTopOf="@id/tv_detail_score" /> + + <TextView + android:id="@+id/tv_detail_score" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/score" + android:textColor="@color/slightly_desaturated_magenta" + android:textSize="20sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_detail_title" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_more_infos" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_header"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + <TextView + android:id="@+id/tv_text_volumes" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_volumes" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_volumes" + app:layout_constraintEnd_toStartOf="@id/tv_volumes" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/tv_volumes" /> + + <TextView + android:id="@+id/tv_volumes" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/volumes" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintStart_toEndOf="@id/tv_text_volumes" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_text_chapters" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_chapters" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_chapters" + app:layout_constraintEnd_toStartOf="@id/tv_chapters" + app:layout_constraintTop_toTopOf="@id/tv_chapters" /> + + <TextView + android:id="@+id/tv_chapters" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/chapters" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/tv_text_chapters" + app:layout_constraintTop_toBottomOf="@id/tv_volumes" /> + + <TextView + android:id="@+id/tv_text_start_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_start_date" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_start_date" + app:layout_constraintEnd_toStartOf="@id/tv_start_date" + app:layout_constraintTop_toTopOf="@id/tv_start_date" /> + + <TextView + android:id="@+id/tv_start_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/start_date" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_text_end_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_end_date" + android:textColor="@color/strong_pink" + app:layout_constraintBottom_toBottomOf="@id/tv_end_date" + app:layout_constraintEnd_toStartOf="@id/tv_end_date" + app:layout_constraintTop_toTopOf="@id/tv_end_date" /> + + <TextView + android:id="@+id/tv_end_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/end_date" + android:textColor="@color/slightly_desaturated_magenta" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_start_date" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_synopsis" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_more_infos"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + <TextView + android:id="@+id/tv_text_synopsis" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_synopsis" + android:textColor="@color/strong_pink" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_synopsis" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/slightly_desaturated_magenta" + android:textIsSelectable="true" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_text_synopsis" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/cv_background" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_synopsis"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + + <TextView + android:id="@+id/tv_text_background" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_background" + android:textColor="@color/strong_pink" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_background" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/slightly_desaturated_magenta" + android:textIsSelectable="true" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_text_background" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="4sp" + app:cardBackgroundColor="@color/black" + app:layout_constraintTop_toBottomOf="@id/cv_background"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp"> + + + <TextView + android:id="@+id/tv_text_url" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/text_url" + android:textColor="@color/strong_pink" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/tv_url" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:autoLink="all" + android:text="@string/url" + android:textColor="@color/slightly_desaturated_magenta" + android:textColorLink="@color/very_light_magenta" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_text_url" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.cardview.widget.CardView> + + </androidx.constraintlayout.widget.ConstraintLayout> +</ScrollView>
\ No newline at end of file diff --git a/app/src/main/res/layout/home_page.xml b/app/src/main/res/layout/home_page.xml index 6456dfc..be80747 100644 --- a/app/src/main/res/layout/home_page.xml +++ b/app/src/main/res/layout/home_page.xml @@ -40,4 +40,25 @@ app:layout_constraintStart_toEndOf="@id/button_top_manga" app:layout_constraintTop_toBottomOf="@id/tv_title" /> + <Button + android:id="@+id/button_search_manga" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/search_manga" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/button_top_manga" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/button_search_anime"/> + + <Button + android:id="@+id/button_search_anime" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/search_anime" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/button_top_anime" + app:layout_constraintStart_toEndOf="@id/button_search_manga" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/search_anime_page.xml b/app/src/main/res/layout/search_anime_page.xml new file mode 100644 index 0000000..631ebde --- /dev/null +++ b/app/src/main/res/layout/search_anime_page.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/very_dark_purple" + tools:context=".anime.search.SearchAnimePage"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/tiet_query" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:hint="@string/hint_query" + android:textColor="@color/very_light_magenta" + android:textColorHint="@color/grayish_magenta" + android:textStyle="bold" + app:layout_constraintBottom_toTopOf="@id/recycler_view" + app:layout_constraintEnd_toStartOf="@id/button_query" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <Button + android:id="@+id/button_query" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/recycler_view" + app:layout_constraintStart_toEndOf="@id/tiet_query" + android:text="@string/search" + /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recycler_view" + android:layout_width="0dp" + android:layout_height="0dp" + android:scrollbars="vertical" + app:layout_constraintBottom_toTopOf="@+id/button_home" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tiet_query" + tools:listitem="@layout/item_layout" /> + + <Button + android:id="@+id/button_home" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/home" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/search_manga_page.xml b/app/src/main/res/layout/search_manga_page.xml new file mode 100644 index 0000000..02a62ff --- /dev/null +++ b/app/src/main/res/layout/search_manga_page.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/very_dark_purple" + tools:context=".manga.search.SearchMangaPage"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/tiet_query" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:hint="@string/hint_query" + android:textColor="@color/very_light_magenta" + android:textColorHint="@color/grayish_magenta" + android:textStyle="bold" + app:layout_constraintBottom_toTopOf="@id/recycler_view" + app:layout_constraintEnd_toStartOf="@id/button_query" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <Button + android:id="@+id/button_query" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/recycler_view" + app:layout_constraintStart_toEndOf="@id/tiet_query" + android:text="@string/search" + /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recycler_view" + android:layout_width="0dp" + android:layout_height="0dp" + android:scrollbars="vertical" + app:layout_constraintBottom_toTopOf="@+id/button_home" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tiet_query" + tools:listitem="@layout/item_layout" /> + + <Button + android:id="@+id/button_home" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/home" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 4ceeab2..89cbc89 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -17,6 +17,12 @@ <action android:id="@+id/action_HomePage_to_TopAnimePage" app:destination="@id/TopAnimePage" /> + <action + android:id="@+id/action_HomePage_to_SearchMangaPage" + app:destination="@id/SearchMangaPage" /> + <action + android:id="@+id/action_HomePage_to_SearchAnimePage" + app:destination="@id/SearchAnimePage" /> </fragment> @@ -40,5 +46,25 @@ android:id="@+id/action_TopAnimePage_to_HomePage" app:destination="@id/HomePage" /> </fragment> + <fragment + android:id="@+id/SearchMangaPage" + android:name="xyz.adjutor.aniki.manga.search.SearchMangaPage" + android:label="@string/search_manga_page_label" + tools:layout="@layout/search_manga_page"> + + <action + android:id="@+id/action_SearchMangaPage_to_HomePage" + app:destination="@id/HomePage" /> + </fragment> + <fragment + android:id="@+id/SearchAnimePage" + android:name="xyz.adjutor.aniki.anime.search.SearchAnimePage" + android:label="@string/search_anime_page_label" + tools:layout="@layout/search_anime_page"> + + <action + android:id="@+id/action_SearchAnimePage_to_HomePage" + app:destination="@id/HomePage" /> + </fragment> </navigation>
\ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index ae0a497..caca611 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -9,5 +9,6 @@ <color name="very_dark_purple">#09022A</color> <color name="very_light_magenta">#FF70FF</color> <color name="strong_pink">#D52C70</color> - <color name="slightly_desaturated_magenta">#c583b6</color> + <color name="slightly_desaturated_magenta">#C583B6</color> + <color name="grayish_magenta">#C9A6C9</color> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb9520a..0c56edc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,4 +36,11 @@ <string name="text_episodes">"Episodes "</string> <string name="episodes">0000</string> + <string name="search_manga_page_label">Search Manga Page</string> + <string name="search_manga">Search Manga</string> + <string name="search">Search in the database</string> + <string name="search_anime">Search Anime</string> + <string name="search_anime_page_label">Search Anime Page</string> + <string name="hint_query">Your query …</string> + </resources>
\ No newline at end of file |