add watchlist to home fragment
This commit is contained in:
parent
f100b4abf3
commit
d5d70e49d2
|
@ -211,6 +211,30 @@ object Crunchyroll {
|
||||||
} ?: NoneSearchResult
|
} ?: NoneSearchResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of series objects.
|
||||||
|
* Note: episode objects are currently not supported
|
||||||
|
*
|
||||||
|
* @param objects The object IDs as list of Strings
|
||||||
|
* @return A Collection of Panels
|
||||||
|
*/
|
||||||
|
suspend fun objects(objects: List<String>): Collection {
|
||||||
|
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}"
|
||||||
|
val parameters = listOf(
|
||||||
|
"locale" to locale,
|
||||||
|
"Signature" to signature,
|
||||||
|
"Policy" to policy,
|
||||||
|
"Key-Pair-Id" to keyPairID
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = request(episodesEndpoint, parameters)
|
||||||
|
println(result.component1()?.obj()?.toString())
|
||||||
|
|
||||||
|
return result.component1()?.obj()?.let {
|
||||||
|
json.decodeFromString(it.toString())
|
||||||
|
} ?: NoneCollection
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* series id == crunchyroll id?
|
* series id == crunchyroll id?
|
||||||
*/
|
*/
|
||||||
|
@ -273,7 +297,7 @@ object Crunchyroll {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional media functions: watchlist, playhead
|
* Additional media functions: watchlist (series), playhead
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -283,10 +307,10 @@ object Crunchyroll {
|
||||||
* @return Boolean: ture if it was found, else false
|
* @return Boolean: ture if it was found, else false
|
||||||
*/
|
*/
|
||||||
suspend fun isWatchlist(seriesId: String): Boolean {
|
suspend fun isWatchlist(seriesId: String): Boolean {
|
||||||
val watchlistEndpoint = "/content/v1/watchlist/$accountID/$seriesId"
|
val watchlistSeriesEndpoint = "/content/v1/watchlist/$accountID/$seriesId"
|
||||||
val parameters = listOf("locale" to locale)
|
val parameters = listOf("locale" to locale)
|
||||||
|
|
||||||
val result = request(watchlistEndpoint, parameters)
|
val result = request(watchlistSeriesEndpoint, parameters)
|
||||||
// if needed implement parsing
|
// if needed implement parsing
|
||||||
|
|
||||||
return result.component1()?.obj()?.has(seriesId) ?: false
|
return result.component1()?.obj()?.has(seriesId) ?: false
|
||||||
|
@ -298,14 +322,14 @@ object Crunchyroll {
|
||||||
* @param seriesId The crunchyroll series id of the media to check
|
* @param seriesId The crunchyroll series id of the media to check
|
||||||
*/
|
*/
|
||||||
suspend fun postWatchlist(seriesId: String) {
|
suspend fun postWatchlist(seriesId: String) {
|
||||||
val watchlistEndpoint = "/content/v1/watchlist/$accountID"
|
val watchlistPostEndpoint = "/content/v1/watchlist/$accountID"
|
||||||
val parameters = listOf("locale" to locale)
|
val parameters = listOf("locale" to locale)
|
||||||
|
|
||||||
val json = buildJsonObject {
|
val json = buildJsonObject {
|
||||||
put("content_id", seriesId)
|
put("content_id", seriesId)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestPost(watchlistEndpoint, parameters, json.toString())
|
requestPost(watchlistPostEndpoint, parameters, json.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -314,10 +338,10 @@ object Crunchyroll {
|
||||||
* @param seriesId The crunchyroll series id of the media to check
|
* @param seriesId The crunchyroll series id of the media to check
|
||||||
*/
|
*/
|
||||||
suspend fun deleteWatchlist(seriesId: String) {
|
suspend fun deleteWatchlist(seriesId: String) {
|
||||||
val watchlistEndpoint = "/content/v1/watchlist/$accountID/$seriesId"
|
val watchlistDeleteEndpoint = "/content/v1/watchlist/$accountID/$seriesId"
|
||||||
val parameters = listOf("locale" to locale)
|
val parameters = listOf("locale" to locale)
|
||||||
|
|
||||||
requestDelete(watchlistEndpoint, parameters)
|
requestDelete(watchlistDeleteEndpoint, parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -327,4 +351,21 @@ object Crunchyroll {
|
||||||
// implement
|
// implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listing functions: watchlist (list), up_next_account
|
||||||
|
*/
|
||||||
|
|
||||||
|
suspend fun watchlist(n: Int = 20): Watchlist {
|
||||||
|
val watchlistEndpoint = "/content/v1/$accountID/watchlist"
|
||||||
|
val parameters = listOf("locale" to locale, "n" to n)
|
||||||
|
|
||||||
|
val watchlistResult = request(watchlistEndpoint, parameters)
|
||||||
|
val list: ContinueWatchingList = watchlistResult.component1()?.obj()?.let {
|
||||||
|
json.decodeFromString(it.toString())
|
||||||
|
} ?: NoneContinueWatchingList
|
||||||
|
|
||||||
|
val objects = list.items.map{ it.panel.episodeMetadata.seriesId }
|
||||||
|
return objects(objects)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,18 @@ enum class SortBy(val str: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search data type
|
* search, browse, watchlist data types (all collections)
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Collection(
|
||||||
|
@SerialName("total") val total: Int,
|
||||||
|
@SerialName("items") val items: List<Item>
|
||||||
|
)
|
||||||
|
|
||||||
|
typealias SearchCollection = Collection
|
||||||
|
typealias BrowseResult = Collection
|
||||||
|
typealias Watchlist = Collection
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SearchResult(
|
data class SearchResult(
|
||||||
@SerialName("total") val total: Int,
|
@SerialName("total") val total: Int,
|
||||||
|
@ -24,17 +34,21 @@ data class SearchResult(
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SearchCollection(
|
data class ContinueWatchingList(
|
||||||
@SerialName("type") val type: String,
|
@SerialName("total") val total: Int,
|
||||||
@SerialName("items") val items: List<Item>
|
@SerialName("items") val items: List<ContinueWatchingItem>
|
||||||
)
|
)
|
||||||
|
|
||||||
val NoneSearchResult = SearchResult(0, emptyList())
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class BrowseResult(val total: Int, val items: List<Item>)
|
data class ContinueWatchingItem(
|
||||||
|
@SerialName("panel") val panel: EpisodePanel,
|
||||||
|
@SerialName("new") val new: Boolean,
|
||||||
|
@SerialName("new_content") val newContent: Boolean,
|
||||||
|
@SerialName("is_favorite") val isFavorite: Boolean,
|
||||||
|
@SerialName("never_watched") val neverWatched: Boolean,
|
||||||
|
@SerialName("completion_status") val completionStatus: Boolean,
|
||||||
|
@SerialName("playhead") val playhead: Int,
|
||||||
|
)
|
||||||
|
|
||||||
// the data class Item is used in browse and search
|
// the data class Item is used in browse and search
|
||||||
// TODO rename to MediaPanel
|
// TODO rename to MediaPanel
|
||||||
|
@ -49,8 +63,17 @@ data class Item(
|
||||||
// TODO metadata etc.
|
// TODO metadata etc.
|
||||||
)
|
)
|
||||||
|
|
||||||
val NoneItem = Item("", "", "", "", "", Images(listOf(), listOf()))
|
// EpisodePanel is used in ContinueWatchingItem
|
||||||
val NoneBrowseResult = BrowseResult(0, listOf())
|
@Serializable
|
||||||
|
data class EpisodePanel(
|
||||||
|
@SerialName("id") val id: String,
|
||||||
|
@SerialName("title") val title: String,
|
||||||
|
@SerialName("type") val type: String,
|
||||||
|
@SerialName("channel_id") val channelId: String,
|
||||||
|
@SerialName("description") val description: String,
|
||||||
|
@SerialName("images") val images: Thumbnail,
|
||||||
|
@SerialName("episode_metadata") val episodeMetadata: EpisodeMetadata,
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<List<Poster>>)
|
data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<List<Poster>>)
|
||||||
|
@ -59,6 +82,18 @@ data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<Lis
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Poster(val height: Int, val width: Int, val source: String, val type: String)
|
data class Poster(val height: Int, val width: Int, val source: String, val type: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class EpisodeMetadata(
|
||||||
|
@SerialName("series_id") val seriesId: String,
|
||||||
|
@SerialName("series_title") val seriesTitle: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
val NoneItem = Item("", "", "", "", "", Images(emptyList(), emptyList()))
|
||||||
|
val NoneCollection = Collection(0, emptyList())
|
||||||
|
val NoneSearchResult = SearchResult(0, emptyList())
|
||||||
|
val NoneBrowseResult = BrowseResult(0, emptyList())
|
||||||
|
val NoneContinueWatchingList = ContinueWatchingList(0, emptyList())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Series data type
|
* Series data type
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -44,8 +44,6 @@ import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity
|
||||||
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
||||||
import org.mosad.teapod.ui.components.LoginDialog
|
import org.mosad.teapod.ui.components.LoginDialog
|
||||||
import org.mosad.teapod.util.DataTypes
|
import org.mosad.teapod.util.DataTypes
|
||||||
import org.mosad.teapod.util.MetaDBController
|
|
||||||
import org.mosad.teapod.util.StorageController
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListener {
|
class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListener {
|
||||||
|
@ -139,7 +137,6 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
|
||||||
// load all saved stuff here
|
// load all saved stuff here
|
||||||
Preferences.load(this)
|
Preferences.load(this)
|
||||||
EncryptedPreferences.readCredentials(this)
|
EncryptedPreferences.readCredentials(this)
|
||||||
StorageController.load(this)
|
|
||||||
|
|
||||||
// show onboarding TODO rework
|
// show onboarding TODO rework
|
||||||
if (EncryptedPreferences.password.isEmpty()) {
|
if (EncryptedPreferences.password.isEmpty()) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.mosad.teapod.preferences.Preferences
|
||||||
import org.mosad.teapod.ui.activity.main.MainActivity
|
import org.mosad.teapod.ui.activity.main.MainActivity
|
||||||
import org.mosad.teapod.ui.components.LoginDialog
|
import org.mosad.teapod.ui.components.LoginDialog
|
||||||
import org.mosad.teapod.util.DataTypes.Theme
|
import org.mosad.teapod.util.DataTypes.Theme
|
||||||
import org.mosad.teapod.util.StorageController
|
|
||||||
import org.mosad.teapod.util.showFragment
|
import org.mosad.teapod.util.showFragment
|
||||||
|
|
||||||
class AccountFragment : Fragment() {
|
class AccountFragment : Fragment() {
|
||||||
|
@ -32,7 +31,7 @@ class AccountFragment : Fragment() {
|
||||||
private val getUriExport = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
private val getUriExport = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
result.data?.data?.also { uri ->
|
result.data?.data?.also { uri ->
|
||||||
StorageController.exportMyList(requireContext(), uri)
|
//StorageController.exportMyList(requireContext(), uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,13 +39,13 @@ class AccountFragment : Fragment() {
|
||||||
private val getUriImport = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
private val getUriImport = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
result.data?.data?.also { uri ->
|
result.data?.data?.also { uri ->
|
||||||
val success = StorageController.importMyList(requireContext(), uri)
|
// val success = StorageController.importMyList(requireContext(), uri)
|
||||||
if (success == 0) {
|
// if (success == 0) {
|
||||||
Toast.makeText(
|
// Toast.makeText(
|
||||||
context, getString(R.string.import_data_success),
|
// context, getString(R.string.import_data_success),
|
||||||
Toast.LENGTH_SHORT
|
// Toast.LENGTH_SHORT
|
||||||
).show()
|
// ).show()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,21 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.joinAll
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.R
|
|
||||||
import org.mosad.teapod.databinding.FragmentHomeBinding
|
import org.mosad.teapod.databinding.FragmentHomeBinding
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
|
||||||
import org.mosad.teapod.util.ItemMedia
|
import org.mosad.teapod.util.ItemMedia
|
||||||
import org.mosad.teapod.util.StorageController
|
|
||||||
import org.mosad.teapod.util.adapter.MediaItemAdapter
|
import org.mosad.teapod.util.adapter.MediaItemAdapter
|
||||||
import org.mosad.teapod.util.decoration.MediaItemDecoration
|
import org.mosad.teapod.util.decoration.MediaItemDecoration
|
||||||
import org.mosad.teapod.util.setDrawableTop
|
|
||||||
import org.mosad.teapod.util.showFragment
|
import org.mosad.teapod.util.showFragment
|
||||||
|
|
||||||
class HomeFragment : Fragment() {
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentHomeBinding
|
private lateinit var binding: FragmentHomeBinding
|
||||||
private lateinit var adapterMyList: MediaItemAdapter
|
private lateinit var adapterWatchlist: MediaItemAdapter
|
||||||
private lateinit var adapterNewEpisodes: MediaItemAdapter
|
private lateinit var adapterNewEpisodes: MediaItemAdapter
|
||||||
private lateinit var adapterNewSimulcasts: MediaItemAdapter
|
|
||||||
private lateinit var adapterNewTitles: MediaItemAdapter
|
private lateinit var adapterNewTitles: MediaItemAdapter
|
||||||
private lateinit var adapterTopTen: MediaItemAdapter
|
private lateinit var adapterTopTen: MediaItemAdapter
|
||||||
|
|
||||||
|
@ -61,33 +60,37 @@ class HomeFragment : Fragment() {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initRecyclerViews() {
|
/**
|
||||||
binding.recyclerMyList.addItemDecoration(MediaItemDecoration(9))
|
* Suspend, since adapters need to be initialized before we can initialize the actions.
|
||||||
|
*/
|
||||||
|
private suspend fun initRecyclerViews() {
|
||||||
|
binding.recyclerWatchlist.addItemDecoration(MediaItemDecoration(9))
|
||||||
binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9))
|
binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9))
|
||||||
binding.recyclerNewSimulcasts.addItemDecoration(MediaItemDecoration(9))
|
|
||||||
binding.recyclerNewTitles.addItemDecoration(MediaItemDecoration(9))
|
binding.recyclerNewTitles.addItemDecoration(MediaItemDecoration(9))
|
||||||
binding.recyclerTopTen.addItemDecoration(MediaItemDecoration(9))
|
binding.recyclerTopTen.addItemDecoration(MediaItemDecoration(9))
|
||||||
|
|
||||||
// my list
|
val asyncJobList = arrayListOf<Job>()
|
||||||
adapterMyList = MediaItemAdapter(mapMyListToItemMedia())
|
|
||||||
binding.recyclerMyList.adapter = adapterMyList
|
|
||||||
|
|
||||||
// TODO
|
// watchlist
|
||||||
// new episodes
|
val watchlistJob = lifecycleScope.launch {
|
||||||
|
adapterWatchlist = MediaItemAdapter(mapMyListToItemMedia())
|
||||||
|
binding.recyclerWatchlist.adapter = adapterWatchlist
|
||||||
|
}
|
||||||
|
asyncJobList.add(watchlistJob)
|
||||||
|
|
||||||
|
// new episodes TODO replace with continue watching
|
||||||
// adapterNewEpisodes = MediaItemAdapter(AoDParser.newEpisodesList)
|
// adapterNewEpisodes = MediaItemAdapter(AoDParser.newEpisodesList)
|
||||||
// binding.recyclerNewEpisodes.adapter = adapterNewEpisodes
|
// binding.recyclerNewEpisodes.adapter = adapterNewEpisodes
|
||||||
|
|
||||||
// new simulcasts
|
// new titles TODO
|
||||||
// adapterNewSimulcasts = MediaItemAdapter(AoDParser.newSimulcastsList)
|
|
||||||
// binding.recyclerNewSimulcasts.adapter = adapterNewSimulcasts
|
|
||||||
|
|
||||||
// new titles
|
|
||||||
// adapterNewTitles = MediaItemAdapter(AoDParser.newTitlesList)
|
// adapterNewTitles = MediaItemAdapter(AoDParser.newTitlesList)
|
||||||
// binding.recyclerNewTitles.adapter = adapterNewTitles
|
// binding.recyclerNewTitles.adapter = adapterNewTitles
|
||||||
|
|
||||||
// top ten
|
// top ten TODO
|
||||||
// adapterTopTen = MediaItemAdapter(AoDParser.topTenList)
|
// adapterTopTen = MediaItemAdapter(AoDParser.topTenList)
|
||||||
// binding.recyclerTopTen.adapter = adapterTopTen
|
// binding.recyclerTopTen.adapter = adapterTopTen
|
||||||
|
|
||||||
|
asyncJobList.joinAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initActions() {
|
private fun initActions() {
|
||||||
|
@ -103,23 +106,23 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textHighlightMyList.setOnClickListener {
|
binding.textHighlightMyList.setOnClickListener {
|
||||||
if (StorageController.myList.contains(0)) {
|
// TODO implement if needed
|
||||||
StorageController.myList.remove(0)
|
|
||||||
binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_add_24)
|
|
||||||
} else {
|
|
||||||
StorageController.myList.add(0)
|
|
||||||
binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_check_24)
|
|
||||||
}
|
|
||||||
StorageController.saveMyList(requireContext())
|
|
||||||
|
|
||||||
updateMyListMedia() // update my list, since it has changed
|
// if (StorageController.myList.contains(0)) {
|
||||||
|
// StorageController.myList.remove(0)
|
||||||
|
// binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_add_24)
|
||||||
|
// } else {
|
||||||
|
// StorageController.myList.add(0)
|
||||||
|
// binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_check_24)
|
||||||
|
// }
|
||||||
|
// StorageController.saveMyList(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textHighlightInfo.setOnClickListener {
|
binding.textHighlightInfo.setOnClickListener {
|
||||||
activity?.showFragment(MediaFragment(""))
|
activity?.showFragment(MediaFragment(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
adapterMyList.onItemClick = { id, _ ->
|
adapterWatchlist.onItemClick = { id, _ ->
|
||||||
activity?.showFragment(MediaFragment("")) //(mediaId))
|
activity?.showFragment(MediaFragment("")) //(mediaId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,27 +143,10 @@ class HomeFragment : Fragment() {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private suspend fun mapMyListToItemMedia(): List<ItemMedia> {
|
||||||
* update my media list
|
return Crunchyroll.watchlist(50).items.map {
|
||||||
* TODO
|
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
||||||
* * auto call when StorageController.myList is changed
|
|
||||||
* * only update actual change and not all data (performance)
|
|
||||||
*/
|
|
||||||
fun updateMyListMedia() {
|
|
||||||
//adapterMyList.updateMediaList(mapMyListToItemMedia())
|
|
||||||
//adapterMyList.notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapMyListToItemMedia(): List<ItemMedia> {
|
|
||||||
return emptyList()
|
|
||||||
// return StorageController.myList.mapNotNull { elementId ->
|
|
||||||
// AoDParser.guiMediaList.firstOrNull { it.id == elementId.toString() }.also {
|
|
||||||
// // it the my list entry wasn't found in itemMediaList Log it
|
|
||||||
// if (it == null) {
|
|
||||||
// Log.w(javaClass.name, "The element with the id $elementId was not found.")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -29,6 +29,9 @@ import kotlinx.coroutines.*
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO remove gson usage
|
||||||
|
*/
|
||||||
class MetaDBController {
|
class MetaDBController {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
package org.mosad.teapod.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import android.util.Log
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.JsonParser
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileReader
|
|
||||||
import java.io.FileWriter
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This controller contains the logic for permanently saved data.
|
|
||||||
* On load, it loads the saved files into the variables
|
|
||||||
*/
|
|
||||||
object StorageController {
|
|
||||||
|
|
||||||
private const val fileNameMyList = "my_list.json"
|
|
||||||
|
|
||||||
val myList = ArrayList<Int>() // a list of saved mediaIds
|
|
||||||
|
|
||||||
fun load(context: Context) {
|
|
||||||
loadMyList(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadMyList(context: Context) {
|
|
||||||
val file = File(context.filesDir, fileNameMyList)
|
|
||||||
|
|
||||||
if (!file.exists()) runBlocking { saveMyList(context).join() }
|
|
||||||
|
|
||||||
try {
|
|
||||||
myList.clear()
|
|
||||||
myList.addAll(JsonParser.parseString(file.readText()).asJsonArray.map { it.asInt }.distinct())
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
myList.clear()
|
|
||||||
Log.e(javaClass.name, "Parsing of My-List failed.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveMyList(context: Context): Job {
|
|
||||||
val file = File(context.filesDir, fileNameMyList)
|
|
||||||
|
|
||||||
return CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
file.writeText(Gson().toJson(myList.distinct()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exportMyList(context: Context, uri: Uri) {
|
|
||||||
try {
|
|
||||||
context.contentResolver.openFileDescriptor(uri, "w")?.use {
|
|
||||||
FileWriter(it.fileDescriptor).use { writer ->
|
|
||||||
writer.write(Gson().toJson(myList.distinct()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
Log.e(javaClass.name, "Exporting my list failed.", ex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* import my list from a (previously exported) json file
|
|
||||||
* @param context the current context
|
|
||||||
* @param uri the uri of the selected file
|
|
||||||
* @return 0 if import was successfull, else 1
|
|
||||||
*/
|
|
||||||
fun importMyList(context: Context, uri: Uri): Int {
|
|
||||||
try {
|
|
||||||
val text = context.contentResolver.openFileDescriptor(uri, "r")?.use {
|
|
||||||
FileReader(it.fileDescriptor).use { reader ->
|
|
||||||
reader.readText()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
myList.clear()
|
|
||||||
myList.addAll(JsonParser.parseString(text).asJsonArray.map { it.asInt }.distinct())
|
|
||||||
|
|
||||||
// after the list has been imported also save it
|
|
||||||
saveMyList(context)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
myList.clear()
|
|
||||||
Log.e(javaClass.name, "Importing my list failed.", ex)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -108,14 +108,14 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/linear_my_list"
|
android:id="@+id/linear_watchlist"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="7dp">
|
android:paddingBottom="7dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_my_list"
|
android:id="@+id/text_watchlist"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="10dp"
|
android:paddingStart="10dp"
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recycler_my_list"
|
android:id="@+id/recycler_watchlist"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
@ -163,34 +163,6 @@
|
||||||
tools:listitem="@layout/item_media" />
|
tools:listitem="@layout/item_media" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/linear_new_simulcasts"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingBottom="7dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_new_simulcasts"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="10dp"
|
|
||||||
android:paddingTop="15dp"
|
|
||||||
android:paddingEnd="5dp"
|
|
||||||
android:paddingBottom="5dp"
|
|
||||||
android:text="@string/new_simulcasts"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recycler_new_simulcasts"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:listitem="@layout/item_media" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/linear_new_titles"
|
android:id="@+id/linear_new_titles"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
Loading…
Reference in New Issue