Compare commits

...

27 Commits

Author SHA1 Message Date
0fd7cc964f
update gradle wrapper, agp, kotlin and libraries
* kotline 1.9.22 -> 2.0.20
* ktor 2.3.6 -> 3.0.0
* agp 8.3.0 -> 8.7.1
2024-10-29 22:07:06 +01:00
b07a6fd407
update gradle wrapper, agp, kotlin and libraries 2024-03-03 21:21:12 +01:00
7d661712f7
update to kotlin 1.9.0 2023-11-15 15:50:13 +01:00
8fcf047e99
update agp and libraries
* agp 8.1.2 -> 8.1.3
* ktor 2.3.4 -> 2.3.6
* androidx.core 1.10.1 -> 1.12.0
* androidx.navigation 2.6.0 -> 2.7.5
* androidx.lifecycle 2.6.1 -> 2.6.1
* com.google.android.material 1.9.0 -> 1.10.0
* gradle wrapper 8.2.1 -> 8.4
2023-11-15 15:37:44 +01:00
17dbe945e5
mograte MyListFragment to use a simple ViewModel
fixes crashes in MyListFragment if the User closes the fragment with a loading job still running, part of #56
2023-10-15 18:44:22 +02:00
5f609d4c33
update agp and libraries
* agp 8.1.0 -> 8.1.2
* ktor 2.3.2 -> 2.3.4
* kotlinx-serialization-json 1.5.1 -> 1.6.0
2023-09-29 23:47:47 +02:00
6515f657d0
partially revert c448b44fc4 2023-08-11 15:07:33 +02:00
c448b44fc4
updatelibraries and agp
* agp 8.0.2 -> 8.1.0
* kotlinx-coroutines-android 1.7.2 -> 1.7.3
* navigation-fragment-ktx 2.6.0 -> 2.7.0
* navigation-ui-ktx 2.6.0 -> 2.7.0
2023-08-11 14:58:17 +02:00
88ebc378d3
add changelog for beta3; update gradle wrapper to 8.2.1 2023-08-11 14:41:55 +02:00
1a012cba7d
add support for dedicated subtitle and audio language settings 2023-07-21 21:42:55 +02:00
59a457430e
migrate more Crunchyroll API endpoints to v2 2023-07-21 17:22:45 +02:00
0662d656ac
update libraries, agp and kotlin
* kotlin 1.8.10 -> 1.8.22
* kotlinx-coroutines-android 1.6.4 -> 1.7.2
* kotlinx-serialization-json 1.5.0 -> 1.5.1
* core-ktx 1.10.0 -> 1.10.1
* core-splashscreen 1.0.0 -> 1.0.1
* navigation-fragment-ktx 2.5.3 -> 2.6.0
* navigation-ui-ktx 2.5.3 -> 2.6.0
* security-crypto 1.1.0-alpha05 -> 1.1.0-alpha06
* material 1.8.0 -> 1.9.0
* ktor 2.2.4 -> 2.3.2
* exo-player 2.18.5 -> 2.18.7
* agp 8.0.0 -> 8.0.2
2023-07-21 11:43:38 +02:00
3549a3d2a7
migrate Crunchyroll.objects() to new v2 endpoint
fixes #71
2023-07-21 11:39:48 +02:00
c89ae54929
fix typo in changelog for 1.1.0-beta2 2023-04-16 16:51:59 +02:00
3aa03783a9
add changelogs for 1.1.0-beta2 2023-04-16 16:49:09 +02:00
4bceacf75c
make versions in DataTypes -> Episodes -> Episode nullable since it is in fact nullable 2023-04-16 16:24:28 +02:00
cf02bee7d4
minor fixes
* fix episode count in MediaFragement
* fix tmdb language tag
* update media type detection to use the episode field as episodeNumber may be messinging from certain episodes of tv shows
2023-04-16 13:49:22 +02:00
01d026cc7f
update libraries, agp and gradle
* core-ktx 1.9.0 -> 1.10.0
* lifecycle-runtime-ktx 2.5.1 -> 2.6.1
* lifecycle-viewmodel-ktx 2.5.1 -> 2.6.1
* glide 4.15.0 -> 4.15.1
* exoplayer 2.18.3 -> 2.18.5
* agp 7.4.2 -> 8.0.0
* gradle wrapper 7.6 -> 8.1
2023-04-16 00:06:40 +02:00
7580093649 Merge pull request 'migrate to material 3' (#70) from feature/material-3 into develop
Reviewed-on: #70
2023-04-15 23:51:08 +02:00
f266731115
remove old theme definition 2023-04-15 23:50:40 +02:00
a6a23c8560
fix onboarding colors for light/dark theme 2023-04-15 23:46:13 +02:00
2cb05de810
fix theme selection dialog to work with system theme also use system as new default 2023-04-15 22:48:59 +02:00
5cf4527a92
clean up color and theme definitions
also use separate theme definition for light/dark
2023-04-15 22:35:19 +02:00
14ad34138c
fix onboarding fragments and bottom sheet login 2023-04-15 22:02:49 +02:00
47e1f6bd49
initial migration to material 3 2023-03-29 16:16:31 +02:00
fdcb76e26e
update glide and kotlinx-serialization
* glide 4.14.2 -> 4.15.0
* kotlinx-serialization 1.4.1 -> 1.5.0
2023-03-01 17:24:02 +01:00
7004d73b9f
update libraries and kotlin
* kotlin 1.7.20 -> 1.8.10
* ktor 2.2.1 -> 2.2.4
* exo player 2.18.2 -> 2.18.3
* androidx.appcompat 1.6.0 -> 1.6.1
* androidx.security:security-crypto 1.1.0-alpha04 -> 1.1.0-alpha05
* com.google.android.material 1.7.0 -> 1.8.0
2023-03-01 17:17:23 +01:00
70 changed files with 835 additions and 591 deletions

View File

@ -26,4 +26,4 @@ Currently you need to have an Crunchyroll account to contribute to Teapod. Contr
#### Why is it called Teapod? #### Why is it called Teapod?
Teapod is a Acronym for "The ultimate anime app on demand", hence this project is called Teapod and not Teapot. Teapod is a Acronym for "The ultimate anime app on demand", hence this project is called Teapod and not Teapot.
Teapod © 2020-2022 [@Seil0](https://git.mosad.xyz/Seil0) Teapod © 2020-2023 [@Seil0](https://git.mosad.xyz/Seil0)

View File

@ -4,16 +4,23 @@ plugins {
id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version" id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"
} }
kotlin {
jvmToolchain 17
sourceSets.configureEach {
languageSettings.optIn("kotlin.RequiresOptIn")
}
}
android { android {
compileSdkVersion 33 compileSdk 34
buildToolsVersion "30.0.3" buildToolsVersion = '34.0.0'
defaultConfig { defaultConfig {
applicationId "org.mosad.teapod" applicationId "org.mosad.teapod"
minSdkVersion 23 minSdk 23
targetSdkVersion 32 targetSdk 33
versionCode 100990 //01.00.000 versionCode 100992 //01.00.000
versionName "1.1.0-beta1" versionName "1.1.0-beta3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_time", buildTime() resValue "string", "build_time", buildTime()
@ -22,6 +29,7 @@ android {
buildFeatures { buildFeatures {
viewBinding true viewBinding true
buildConfig true
} }
buildTypes { buildTypes {
@ -32,37 +40,28 @@ android {
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
kotlin.sourceSets.all {
languageSettings.optIn("kotlin.RequiresOptIn")
}
}
namespace 'org.mosad.teapod' namespace 'org.mosad.teapod'
} }
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3'
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.core:core-splashscreen:1.0.0' implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'androidx.appcompat:appcompat:1.6.0' implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' implementation 'androidx.navigation:navigation-fragment-ktx:2.8.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' implementation 'androidx.navigation:navigation-ui-ktx:2.8.3'
implementation 'androidx.security:security-crypto:1.1.0-alpha04' implementation 'androidx.security:security-crypto:1.1.0-alpha06'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.6'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.6'
implementation "androidx.paging:paging-runtime-ktx:3.3.2"
implementation 'com.google.android.material:material:1.7.0' implementation 'com.google.android.material:material:1.12.0'
implementation "com.google.android.exoplayer:exoplayer-core:$exo_version" implementation "com.google.android.exoplayer:exoplayer-core:$exo_version"
implementation "com.google.android.exoplayer:exoplayer-hls:$exo_version" implementation "com.google.android.exoplayer:exoplayer-hls:$exo_version"
implementation "com.google.android.exoplayer:exoplayer-dash:$exo_version" implementation "com.google.android.exoplayer:exoplayer-dash:$exo_version"
@ -71,7 +70,7 @@ dependencies {
implementation 'com.facebook.shimmer:shimmer:0.5.0' implementation 'com.facebook.shimmer:shimmer:0.5.0'
implementation 'com.github.bumptech.glide:glide:4.14.2' implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'jp.wasabeef:glide-transformations:4.3.0' implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation "io.ktor:ktor-client-core:$ktor_version" implementation "io.ktor:ktor-client-core:$ktor_version"
@ -80,8 +79,8 @@ dependencies {
implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version" implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
} }

View File

@ -10,7 +10,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme.Dark"> android:theme="@style/AppTheme">
<activity <activity
android:exported="true" android:exported="true"
android:name="org.mosad.teapod.ui.activity.main.MainActivity" android:name="org.mosad.teapod.ui.activity.main.MainActivity"

View File

@ -34,8 +34,6 @@ import io.ktor.http.*
import io.ktor.serialization.* import io.ktor.serialization.*
import io.ktor.serialization.kotlinx.json.* import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
@ -60,16 +58,12 @@ object Crunchyroll {
private lateinit var token: Token private lateinit var token: Token
private var tokenValidUntil: Long = 0 private var tokenValidUntil: Long = 0
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
private val tokenRefreshContext = newSingleThreadContext("TokenRefreshContext") private val tokenRefreshContext = newSingleThreadContext("TokenRefreshContext")
private var accountID = "" private var accountID = ""
private var externalID = "" private var externalID = ""
private var policy = ""
private var signature = ""
private var keyPairID = ""
private val browsingCache = hashMapOf<String, BrowseResult>() private val browsingCache = hashMapOf<String, BrowseResult>()
/** /**
@ -149,7 +143,7 @@ object Crunchyroll {
} }
return@coroutineScope (Dispatchers.IO) { return@coroutineScope (Dispatchers.IO) {
val response: T = client.request(url) { val response = client.request(url) {
method = httpMethod method = httpMethod
header("Authorization", "${token.tokenType} ${token.accessToken}") header("Authorization", "${token.tokenType} ${token.accessToken}")
params.forEach { params.forEach {
@ -161,9 +155,9 @@ object Crunchyroll {
setBody(bodyObject) setBody(bodyObject)
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
} }
}.body() }
response response.body<T>()
} }
} }
@ -214,27 +208,10 @@ object Crunchyroll {
} }
/** /**
* Basic functions: index, account * Basic functions: account
* Needed for other functions to work properly! * Needed for other functions to work properly!
*/ */
/**
* Retrieve the identifiers necessary for streaming. If the identifiers are
* retrieved, set the corresponding global var. The identifiers are valid for 24h.
*/
suspend fun index() {
val indexEndpoint = "/index/v2"
val index: Index = requestGet(indexEndpoint)
policy = index.cms.policy
signature = index.cms.signature
keyPairID = index.cms.keyPairId
Log.i(TAG, "Policy : $policy")
Log.i(TAG, "Signature : $signature")
Log.i(TAG, "Key Pair ID : $keyPairID")
}
/** /**
* Retrieve the account id and set the corresponding global var. * Retrieve the account id and set the corresponding global var.
* The account id is needed for other calls. * The account id is needed for other calls.
@ -262,24 +239,30 @@ object Crunchyroll {
/** /**
* Browse the media available on crunchyroll. * Browse the media available on crunchyroll.
* *
* @param sortBy * @param start start of the item list, used for pagination, default = 0
* @param n Number of items to return, defaults to 10 * @param n number of items to return, default = 10
* * @param sortBy the sort order, see **[SortBy]**
* @param ratings add user rating to the objects, default = false
* @param seasonTag filter by season tag, if present
* @param categories filter by category, if present
* @return A **[BrowseResult]** object is returned. * @return A **[BrowseResult]** object is returned.
*/ */
suspend fun browse( suspend fun browse(
categories: List<Categories> = emptyList(),
sortBy: SortBy = SortBy.ALPHABETICAL,
seasonTag: String = "",
start: Int = 0, start: Int = 0,
n: Int = 10 n: Int = 10,
sortBy: SortBy = SortBy.ALPHABETICAL,
ratings: Boolean = false,
seasonTag: String = "",
categories: List<Categories> = emptyList()
): BrowseResult { ): BrowseResult {
val browseEndpoint = "/content/v1/browse" val browseEndpoint = "/content/v2/discover/browse"
val parameters = mutableListOf( val parameters = mutableListOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"sort_by" to sortBy.str,
"start" to start, "start" to start,
"n" to n "n" to n,
"sort_by" to sortBy.str,
"ratings" to ratings,
"preferred_audio_language" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
) )
// if a season tag is present add it to the parameters // if a season tag is present add it to the parameters
@ -304,9 +287,10 @@ object Crunchyroll {
NoneBrowseResult NoneBrowseResult
} }
// if the cache has more than 100 entries clear it, so it doesn't become a memory problem
// if the cache has more than 10 entries clear it, so it doesn't become a memory problem
// Note: this value is totally guessed and should be replaced by a properly researched value // Note: this value is totally guessed and should be replaced by a properly researched value
// TODO 100 is way to high as it's not the number of items but BrowseResults
if (browsingCache.size > 10) { if (browsingCache.size > 10) {
browsingCache.clear() browsingCache.clear()
} }
@ -322,19 +306,20 @@ object Crunchyroll {
* Search fo a query term. * Search fo a query term.
* Note: currently this function only supports series/tv shows. * Note: currently this function only supports series/tv shows.
* *
* TODO migrate to v2
*
* @param query The query term as String * @param query The query term as String
* @param n The maximum number of results to return, default = 10 * @param n The maximum number of results to return, default = 10
* @param ratings add user rating to the objects, default = false
* @return A **[SearchResult]** object * @return A **[SearchResult]** object
*/ */
suspend fun search(query: String, n: Int = 10): SearchResult { suspend fun search(query: String, n: Int = 10, ratings: Boolean = false): SearchResult {
val searchEndpoint = "/content/v1/search" val searchEndpoint = "/content/v2/discover/search"
val parameters = listOf( val parameters = listOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"q" to query, "q" to query,
"n" to n, "n" to n,
"type" to "series" "type" to "series",
"ratings" to ratings,
"preferred_audio_language" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
) )
// TODO episodes have thumbnails as image, and not poster_tall/poster_tall, // TODO episodes have thumbnails as image, and not poster_tall/poster_tall,
@ -353,22 +338,22 @@ object Crunchyroll {
* Note: episode objects are currently not supported * Note: episode objects are currently not supported
* *
* @param objects The object IDs as list of Strings * @param objects The object IDs as list of Strings
* @param ratings add user rating to the objects
* @return A **[Collection]** of Panels * @return A **[Collection]** of Panels
*/ */
suspend fun objects(objects: List<String>): Collection<Item> { suspend fun objects(objects: List<String>, ratings: Boolean = false): CollectionV2<Item> {
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}" val episodesEndpoint = "/content/v2/cms/objects/${objects.joinToString(",")}"
val parameters = listOf( val parameters = listOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(), "ratings" to ratings,
"Signature" to signature, "preferred_audio_language" to Preferences.preferredAudioLocale.toLanguageTag(),
"Policy" to policy, "locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
"Key-Pair-Id" to keyPairID
) )
return try { return try {
requestGet(episodesEndpoint, parameters) requestGet(episodesEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in objects().", ex) Log.e(TAG, "Exception in objects().", ex)
NoneCollection NoneCollectionV2
} }
} }
@ -389,7 +374,7 @@ object Crunchyroll {
return try { return try {
requestGet(seriesEndpoint, parameters) requestGet(seriesEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in series().", ex) Log.e(TAG, "Exception in series() for id $seriesId.", ex)
NoneSeries NoneSeries
} }
} }
@ -418,7 +403,7 @@ object Crunchyroll {
Log.e(TAG, "JsonConvertException in upNextSeries() with seriesId=$seriesId", ex) Log.e(TAG, "JsonConvertException in upNextSeries() with seriesId=$seriesId", ex)
NoneUpNextSeriesList NoneUpNextSeriesList
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in upNextSeries().", ex) Log.e(TAG, "Exception in upNextSeries() for seriesId $seriesId.", ex)
NoneUpNextSeriesList NoneUpNextSeriesList
} }
} }
@ -439,7 +424,7 @@ object Crunchyroll {
return try { return try {
requestGet(seasonsEndpoint, parameters) requestGet(seasonsEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in seasons().", ex) Log.e(TAG, "Exception in seasons() for seriesId $seriesId.", ex)
NoneSeasons NoneSeasons
} }
} }
@ -460,7 +445,7 @@ object Crunchyroll {
return try { return try {
requestGet(episodesEndpoint, parameters) requestGet(episodesEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in episodes().", ex) Log.e(TAG, "Exception in episodes() for seasonId $seasonId.", ex)
NoneEpisodes NoneEpisodes
} }
} }
@ -480,7 +465,7 @@ object Crunchyroll {
return try { return try {
requestGet(url, parameters) requestGet(url, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in streams().", ex) Log.e(TAG, "Exception in streams() with url $url.", ex)
NoneStreams NoneStreams
} }
} }
@ -509,10 +494,10 @@ object Crunchyroll {
) )
return try { return try {
(requestGet(watchlistSeriesEndpoint, parameters) as Collection2<IsWatchlistItem>) (requestGet(watchlistSeriesEndpoint, parameters) as CollectionV2<IsWatchlistItem>)
.total == 1 .total == 1
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in isWatchlist() with seriesId = $seriesId", ex) Log.e(TAG, "Exception in isWatchlist() with seriesId $seriesId", ex)
false false
} }
} }
@ -536,7 +521,7 @@ object Crunchyroll {
try { try {
requestPost(watchlistPostEndpoint, parameters, json) requestPost(watchlistPostEndpoint, parameters, json)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in postWatchlist() with seriesId = $seriesId", ex) Log.e(TAG, "Exception in postWatchlist() with seriesId $seriesId", ex)
} }
} }
@ -555,7 +540,7 @@ object Crunchyroll {
try { try {
requestDelete(watchlistDeleteEndpoint, parameters) requestDelete(watchlistDeleteEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in deleteWatchlist() with seriesId = $seriesId", ex) Log.e(TAG, "Exception in deleteWatchlist() with seriesId $seriesId", ex)
} }
} }
@ -575,7 +560,6 @@ object Crunchyroll {
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag() "locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
) )
return try { return try {
requestGet(playheadsEndpoint, parameters) requestGet(playheadsEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
@ -632,14 +616,16 @@ object Crunchyroll {
* *
* @param seriesId The crunchyroll series id of the media * @param seriesId The crunchyroll series id of the media
* @param n The maximum number of results to return, default = 10 * @param n The maximum number of results to return, default = 10
* @param ratings add user rating to the objects
* @return A **[SimilarToResult]** object * @return A **[SimilarToResult]** object
*/ */
suspend fun similarTo(seriesId: String, n: Int = 10): SimilarToResult { suspend fun similarTo(seriesId: String, n: Int = 10, ratings: Boolean = false): SimilarToResult {
val similarToEndpoint = "/content/v1/$accountID/similar_to" val similarToEndpoint = "/content/v2/discover/$accountID/similar_to/$seriesId"
val parameters = listOf( val parameters = listOf(
"guid" to seriesId, "n" to n,
"ratings" to ratings,
"preferred_audio_language" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(), "locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"n" to n
) )
return try { return try {
@ -660,7 +646,7 @@ object Crunchyroll {
* @param n Number of items to return, defaults to 20. * @param n Number of items to return, defaults to 20.
* @return A **[Collection]** containing up to n **[Item]**. * @return A **[Collection]** containing up to n **[Item]**.
*/ */
suspend fun watchlist(n: Int = 20): Collection<Item> { suspend fun watchlist(n: Int = 20): CollectionV2<Item> {
val watchlistEndpoint = "/content/v2/discover/$accountID/watchlist" val watchlistEndpoint = "/content/v2/discover/$accountID/watchlist"
val parameters = listOf( val parameters = listOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(), "locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
@ -682,10 +668,10 @@ object Crunchyroll {
/** /**
* List the next up episodes for the logged in account. * List the next up episodes for the logged in account.
* *
* @param n Number of items to return, defaults to 20. * @param n Number of items to return, default = 20
* @return A **[HistoryList]** containing up to n **[UpNextAccountItem]**. * @return A **[HistoryList]** containing up to n **[UpNextAccountItem]**.
*/ */
suspend fun upNextAccount(n: Int = 20): HistoryList { suspend fun upNextAccount(n: Int = 10): HistoryList {
val watchlistEndpoint = "/content/v2/discover/$accountID/history" val watchlistEndpoint = "/content/v2/discover/$accountID/history"
val parameters = listOf( val parameters = listOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(), "locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
@ -700,13 +686,21 @@ object Crunchyroll {
} }
} }
suspend fun recommendations(n: Int = 20, start: Int = 0): RecommendationsList { /**
val recommendationsEndpoint = "/content/v1/$accountID/recommendations" * Returns a collection of recommendations for the currently logged in account.
*
* @param start start of the item list, used for pagination, default = 0
* @param n number of items to return, default = 10
* @param ratings add user rating to the objects, default = false
* @return A **[RecommendationsList]** containing up to n **[Item]**.
*/
suspend fun recommendations(start: Int = 0, n: Int = 10, ratings: Boolean = false): RecommendationsList {
val recommendationsEndpoint = "/content/v2/discover/$accountID/recommendations"
val parameters = listOf( val parameters = listOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
"n" to n,
"start" to start, "start" to start,
"variant_id" to 0 "n" to n,
"ratings" to ratings,
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
) )
return try { return try {
@ -742,7 +736,7 @@ object Crunchyroll {
* *
* @param languageTag the preferred language as language tag * @param languageTag the preferred language as language tag
*/ */
suspend fun postPrefSubLanguage(languageTag: String) { suspend fun setPreferredSubtitleLanguage(languageTag: String) {
val profileEndpoint = "/accounts/v1/me/profile" val profileEndpoint = "/accounts/v1/me/profile"
val json = buildJsonObject { val json = buildJsonObject {
put("preferred_content_subtitle_language", languageTag) put("preferred_content_subtitle_language", languageTag)
@ -751,6 +745,20 @@ object Crunchyroll {
requestPatch(profileEndpoint, bodyObject = json) requestPatch(profileEndpoint, bodyObject = json)
} }
/**
* Patch the preferred content audio language.
*
* @param languageTag the preferred language as language tag
*/
suspend fun setPreferredAudioLanguage(languageTag: String) {
val profileEndpoint = "/accounts/v1/me/profile"
val json = buildJsonObject {
put("preferred_content_audio_language", languageTag)
}
requestPatch(profileEndpoint, bodyObject = json)
}
/** /**
* Get additional profile (benefits) information for the currently logged in account. * Get additional profile (benefits) information for the currently logged in account.
* *

View File

@ -24,19 +24,47 @@ package org.mosad.teapod.parser.crunchyroll
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.* import java.util.Locale
val supportedLocals = listOf( val supportedAudioLocals = listOf(
Locale.forLanguageTag("ar-SA"), Locale.forLanguageTag("ar-SA"),
Locale.forLanguageTag("ca-ES"),
Locale.forLanguageTag("de-DE"),
Locale.forLanguageTag("en-US"),
Locale.forLanguageTag("en-IN"),
Locale.forLanguageTag("es-419"),
Locale.forLanguageTag("es-ES"),
Locale.forLanguageTag("fr-FR"),
Locale.forLanguageTag("hi-IN"),
Locale.forLanguageTag("it-IT"),
Locale.forLanguageTag("ko-KR"),
Locale.forLanguageTag("pl-PL"),
Locale.forLanguageTag("pt-BR"),
Locale.forLanguageTag("pt-PT"),
Locale.forLanguageTag("ru-RU"),
Locale.forLanguageTag("ta-IN"),
Locale.forLanguageTag("th-TH"),
Locale.forLanguageTag("zh-CN"),
Locale.forLanguageTag("zh-TW"),
Locale.ROOT
)
val supportedSubtitleLocals = listOf(
Locale.forLanguageTag("ar-SA"),
Locale.forLanguageTag("ca-ES"),
Locale.forLanguageTag("de-DE"), Locale.forLanguageTag("de-DE"),
Locale.forLanguageTag("en-US"), Locale.forLanguageTag("en-US"),
Locale.forLanguageTag("es-419"), Locale.forLanguageTag("es-419"),
Locale.forLanguageTag("es-ES"), Locale.forLanguageTag("es-ES"),
Locale.forLanguageTag("fr-FR"), Locale.forLanguageTag("fr-FR"),
Locale.forLanguageTag("hi-IN"),
Locale.forLanguageTag("it-IT"), Locale.forLanguageTag("it-IT"),
Locale.forLanguageTag("ms-MY"),
Locale.forLanguageTag("pl-PL"),
Locale.forLanguageTag("pt-BR"), Locale.forLanguageTag("pt-BR"),
Locale.forLanguageTag("pt-PT"), Locale.forLanguageTag("pt-PT"),
Locale.forLanguageTag("ru-RU"), Locale.forLanguageTag("ru-RU"),
Locale.forLanguageTag("tr-TR"),
Locale.ROOT Locale.ROOT
) )
@ -44,6 +72,10 @@ val supportedLocals = listOf(
* data classes for browse * data classes for browse
* TODO make class names more clear/possibly overlapping for now * TODO make class names more clear/possibly overlapping for now
*/ */
/**
* Enum of all supported sorting orders.
*/
enum class SortBy(val str: String) { enum class SortBy(val str: String) {
ALPHABETICAL("alphabetical"), ALPHABETICAL("alphabetical"),
NEWLY_ADDED("newly_added"), NEWLY_ADDED("newly_added"),
@ -112,23 +144,22 @@ val NoneAccount = Account("", "", false, "")
*/ */
@Serializable @Serializable
data class Collection<T>( data class CollectionV1<T>(
@SerialName("total") val total: Int, @SerialName("total") val total: Int,
@SerialName("items") val items: List<T> @SerialName("items") val items: List<T>
) )
@Serializable @Serializable
data class Collection2<T>( data class CollectionV2<T>(
@SerialName("total") val total: Int, @SerialName("total") val total: Int,
@SerialName("data") val data: List<T> @SerialName("data") val data: List<T>
) )
typealias SearchResult = Collection<SearchCollection> typealias SearchResult = CollectionV2<SearchTypedList<Item>>
typealias SearchCollection = Collection<Item> typealias BrowseResult = CollectionV2<Item>
typealias BrowseResult = Collection<Item> typealias SimilarToResult = CollectionV2<Item>
typealias SimilarToResult = Collection<Item> typealias RecommendationsList = CollectionV2<Item>
typealias RecommendationsList = Collection<Item> typealias Benefits = CollectionV1<Benefit>
typealias Benefits = Collection<Benefit>
/** /**
* panel data classes * panel data classes
@ -159,9 +190,9 @@ data class Poster(val height: Int, val width: Int, val source: String, val type:
* up next & watchlist data classes * up next & watchlist data classes
*/ */
typealias Watchlist = Collection2<WatchlistItem> typealias Watchlist = CollectionV2<WatchlistItem>
typealias HistoryList = Collection2<UpNextAccountItem> typealias HistoryList = CollectionV2<UpNextAccountItem>
typealias UpNextSeriesList = Collection2<UpNextSeriesItem> typealias UpNextSeriesList = CollectionV2<UpNextSeriesItem>
@Serializable @Serializable
data class WatchlistItem( data class WatchlistItem(
@ -221,7 +252,7 @@ data class EpisodeMetadata(
@SerialName("series_title") val seriesTitle: String, @SerialName("series_title") val seriesTitle: String,
) )
val NoneCollection = Collection<Item>(0, emptyList()) val NoneCollectionV2 = CollectionV2<Item>(0, emptyList())
val NoneSearchResult = SearchResult(0, emptyList()) val NoneSearchResult = SearchResult(0, emptyList())
val NoneBrowseResult = BrowseResult(0, emptyList()) val NoneBrowseResult = BrowseResult(0, emptyList())
val NoneSimilarToResult = SimilarToResult(0, emptyList()) val NoneSimilarToResult = SimilarToResult(0, emptyList())
@ -235,7 +266,7 @@ val NoneBenefits = Benefits(0, emptyList())
* series data class * series data class
*/ */
typealias Series = Collection2<SeriesItem> typealias Series = CollectionV2<SeriesItem>
@Serializable @Serializable
data class SeriesItem( data class SeriesItem(
@ -245,11 +276,11 @@ data class SeriesItem(
@SerialName("images") val images: Images, @SerialName("images") val images: Images,
@SerialName("is_simulcast") val isSimulcast: Boolean, @SerialName("is_simulcast") val isSimulcast: Boolean,
@SerialName("maturity_ratings") val maturityRatings: List<String>, @SerialName("maturity_ratings") val maturityRatings: List<String>,
@SerialName("audio_locales") val audioLocales: List<String> @SerialName("audio_locales") val audioLocales: List<String>,
@SerialName("episode_count") val episodeCount: Int
) )
val NoneSeriesItem = SeriesItem("", "", "", Images(emptyList(), emptyList()), false, emptyList(), emptyList()) val NoneSeriesItem = SeriesItem("", "", "", Images(emptyList(), emptyList()), false, emptyList(), emptyList(), 0)
val NoneSeries = Series(1, listOf(NoneSeriesItem)) val NoneSeries = Series(1, listOf(NoneSeriesItem))
/** /**
@ -302,7 +333,7 @@ data class Episode(
@SerialName("is_dubbed") val isDubbed: Boolean, @SerialName("is_dubbed") val isDubbed: Boolean,
@SerialName("images") val images: Thumbnail, @SerialName("images") val images: Thumbnail,
@SerialName("duration_ms") val durationMs: Int, @SerialName("duration_ms") val durationMs: Int,
@SerialName("versions") val versions: List<Version>, @SerialName("versions") val versions: List<Version>? = null,
@SerialName("streams_link") val streamsLink: String, @SerialName("streams_link") val streamsLink: String,
) )
@ -353,7 +384,7 @@ val NoneVersion = Version(
variant = "" variant = ""
) )
typealias Playheads = Collection2<PlayheadObject> typealias Playheads = CollectionV2<PlayheadObject>
@Serializable @Serializable
data class PlayheadObject( data class PlayheadObject(
@ -449,7 +480,18 @@ data class Benefit(
@SerialName("benefit") val benefit: String, @SerialName("benefit") val benefit: String,
@SerialName("source") val source: String, @SerialName("source") val source: String,
) )
@Suppress("unused")
val NoneBenefit = Benefit( val NoneBenefit = Benefit(
benefit = "", benefit = "",
source = "" source = ""
) )
/**
* search result typed list data class
*/
@Serializable
data class SearchTypedList<T>(
@SerialName("type") val type: String,
@SerialName("count") val count: Int,
@SerialName("items") val items: List<T>
)

View File

@ -12,13 +12,11 @@ object Preferences {
internal set internal set
var preferredSubtitleLocale: Locale = Locale.forLanguageTag("en-US") var preferredSubtitleLocale: Locale = Locale.forLanguageTag("en-US")
internal set internal set
var preferSubbed = false
internal set
var autoplay = true var autoplay = true
internal set internal set
var devSettings = false var devSettings = false
internal set internal set
var theme = DataTypes.Theme.DARK var theme = DataTypes.Theme.SYSTEM
internal set internal set
// dev settings // dev settings
@ -50,15 +48,6 @@ object Preferences {
this.preferredSubtitleLocale = preferredLocale this.preferredSubtitleLocale = preferredLocale
} }
fun savePreferSecondary(context: Context, preferSubbed: Boolean) {
with(getSharedPref(context).edit()) {
putBoolean(context.getString(R.string.save_key_prefer_secondary), preferSubbed)
apply()
}
this.preferSubbed = preferSubbed
}
fun saveAutoplay(context: Context, autoplay: Boolean) { fun saveAutoplay(context: Context, autoplay: Boolean) {
with(getSharedPref(context).edit()) { with(getSharedPref(context).edit()) {
putBoolean(context.getString(R.string.save_key_autoplay), autoplay) putBoolean(context.getString(R.string.save_key_autoplay), autoplay)
@ -111,9 +100,6 @@ object Preferences {
context.getString(R.string.save_key_preferred_local), "en-US" context.getString(R.string.save_key_preferred_local), "en-US"
) ?: "en-US" ) ?: "en-US"
) )
preferSubbed = sharedPref.getBoolean(
context.getString(R.string.save_key_prefer_secondary), false
)
autoplay = sharedPref.getBoolean( autoplay = sharedPref.getBoolean(
context.getString(R.string.save_key_autoplay), true context.getString(R.string.save_key_autoplay), true
) )
@ -122,8 +108,8 @@ object Preferences {
) )
theme = DataTypes.Theme.valueOf( theme = DataTypes.Theme.valueOf(
sharedPref.getString( sharedPref.getString(
context.getString(R.string.save_key_theme), DataTypes.Theme.DARK.toString() context.getString(R.string.save_key_theme), DataTypes.Theme.SYSTEM.toString()
) ?: DataTypes.Theme.DARK.toString() ) ?: DataTypes.Theme.SYSTEM.toString()
) )
// dev settings // dev settings

View File

@ -28,6 +28,7 @@ import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit import androidx.fragment.app.commit
@ -69,7 +70,14 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
load() // start the initial loading load() // start the initial loading
theme.applyStyle(getThemeResource(), true)
// theming
val mode = when (Preferences.theme) {
DataTypes.Theme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
DataTypes.Theme.DARK -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
AppCompatDelegate.setDefaultNightMode(mode)
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
binding.navView.setOnItemSelectedListener(this) binding.navView.setOnItemSelectedListener(this)
@ -122,12 +130,12 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
return ret return ret
} }
private fun getThemeResource(): Int { // private fun getThemeResource(): Int {
return when (Preferences.theme) { // return when (Preferences.theme) {
DataTypes.Theme.LIGHT -> R.style.AppTheme_Light // DataTypes.Theme.LIGHT -> R.style.AppTheme_Light
else -> R.style.AppTheme_Dark // else -> R.style.AppTheme_Dark
} // }
} // }
/** /**
* initial loading and login are run in parallel, as initial loading doesn't require * initial loading and login are run in parallel, as initial loading doesn't require
@ -165,7 +173,6 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
private fun initCrunchyroll(): List<Job> { private fun initCrunchyroll(): List<Job> {
val scope = CoroutineScope(Dispatchers.IO + CoroutineName("InitialCrunchyLoading")) val scope = CoroutineScope(Dispatchers.IO + CoroutineName("InitialCrunchyLoading"))
return listOf( return listOf(
scope.launch { Crunchyroll.index() },
scope.launch { Crunchyroll.account() }, scope.launch { Crunchyroll.account() },
scope.launch { scope.launch {
// update the local preferred content language, since it may have changed // update the local preferred content language, since it may have changed

View File

@ -8,17 +8,15 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Deferred import kotlinx.coroutines.*
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.mosad.teapod.BuildConfig import org.mosad.teapod.BuildConfig
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentAccountBinding import org.mosad.teapod.databinding.FragmentAccountBinding
import org.mosad.teapod.parser.crunchyroll.Benefits import org.mosad.teapod.parser.crunchyroll.Benefits
import org.mosad.teapod.parser.crunchyroll.Crunchyroll import org.mosad.teapod.parser.crunchyroll.Crunchyroll
import org.mosad.teapod.parser.crunchyroll.Profile import org.mosad.teapod.parser.crunchyroll.Profile
import org.mosad.teapod.parser.crunchyroll.supportedLocals import org.mosad.teapod.parser.crunchyroll.supportedAudioLocals
import org.mosad.teapod.parser.crunchyroll.supportedSubtitleLocals
import org.mosad.teapod.preferences.EncryptedPreferences import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.activity.main.MainActivity import org.mosad.teapod.ui.activity.main.MainActivity
@ -64,15 +62,18 @@ class AccountFragment : Fragment() {
// add preferred subtitles // add preferred subtitles
lifecycleScope.launch { lifecycleScope.launch {
binding.textSettingsContentLanguageDesc.text = Locale.forLanguageTag( binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag(
profile.await().preferredContentAudioLanguage
).displayLanguage
binding.textSettingsSubtitleLanguageDesc.text = Locale.forLanguageTag(
profile.await().preferredContentSubtitleLanguage profile.await().preferredContentSubtitleLanguage
).displayLanguage ).displayLanguage
} }
binding.switchSecondary.isChecked = Preferences.preferSubbed
binding.switchAutoplay.isChecked = Preferences.autoplay binding.switchAutoplay.isChecked = Preferences.autoplay
binding.textThemeSelected.text = when (Preferences.theme) { binding.textThemeSelected.text = when (Preferences.theme) {
Theme.SYSTEM -> getString(R.string.theme_system)
Theme.LIGHT -> getString(R.string.theme_light)
Theme.DARK -> getString(R.string.theme_dark) Theme.DARK -> getString(R.string.theme_dark)
else -> getString(R.string.theme_light)
} }
binding.linearDevSettings.isVisible = Preferences.devSettings binding.linearDevSettings.isVisible = Preferences.devSettings
@ -88,12 +89,12 @@ class AccountFragment : Fragment() {
showLoginDialog() showLoginDialog()
} }
binding.linearSettingsContentLanguage.setOnClickListener { binding.linearSettingsAudioLanguage.setOnClickListener {
showContentLanguageSelection() showAudioLanguageSelection()
} }
binding.switchSecondary.setOnClickListener { binding.linearSettingsSubtitleLanguage.setOnClickListener {
Preferences.savePreferSecondary(requireContext(), binding.switchSecondary.isChecked) showSubtitleLanguageSelection()
} }
binding.switchAutoplay.setOnClickListener { binding.switchAutoplay.setOnClickListener {
@ -138,43 +139,86 @@ class AccountFragment : Fragment() {
activity?.let { loginModal.show(it.supportFragmentManager, LoginModalBottomSheet.TAG) } activity?.let { loginModal.show(it.supportFragmentManager, LoginModalBottomSheet.TAG) }
} }
private fun showContentLanguageSelection() { private fun showAudioLanguageSelection() {
// we should be able to use the index of supportedLocals for language selection, items is GUI only // we should be able to use the index of supportedLocals for language selection, items is GUI only
val items = supportedLocals.map { val items = supportedAudioLocals.map {
it.toDisplayString(getString(R.string.settings_content_language_none)) it.toDisplayString(getString(R.string.settings_content_language_none))
}.toTypedArray() }.toTypedArray()
var initialSelection: Int var initialSelection: Int
// profile should be completed here, therefore blocking // profile should be completed here, therefore blocking
runBlocking { runBlocking {
initialSelection = supportedLocals.indexOf(Locale.forLanguageTag( initialSelection = supportedAudioLocals.indexOf(Locale.forLanguageTag(
profile.await().preferredContentSubtitleLanguage)) profile.await().preferredContentAudioLanguage))
if (initialSelection < 0) initialSelection = supportedLocals.lastIndex if (initialSelection < 0) initialSelection = supportedAudioLocals.lastIndex
} }
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.settings_content_language) .setTitle(R.string.settings_audio_language)
.setSingleChoiceItems(items, initialSelection){ dialog, which -> .setSingleChoiceItems(items, initialSelection){ dialog, which ->
updatePrefContentLanguage(supportedLocals[which]) updateAudioLanguage(supportedAudioLocals[which])
dialog.dismiss() dialog.dismiss()
} }
.show() .show()
} }
@kotlinx.coroutines.ExperimentalCoroutinesApi private fun showSubtitleLanguageSelection() {
private fun updatePrefContentLanguage(preferredLocale: Locale) { // we should be able to use the index of supportedLocals for language selection, items is GUI only
val items = supportedSubtitleLocals.map {
it.toDisplayString(getString(R.string.settings_content_language_none))
}.toTypedArray()
var initialSelection: Int
// profile should be completed here, therefore blocking
runBlocking {
initialSelection = supportedSubtitleLocals.indexOf(Locale.forLanguageTag(
profile.await().preferredContentSubtitleLanguage))
if (initialSelection < 0) initialSelection = supportedSubtitleLocals.lastIndex
}
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.settings_audio_language)
.setSingleChoiceItems(items, initialSelection){ dialog, which ->
updateSubtitleLanguage(supportedSubtitleLocals[which])
dialog.dismiss()
}
.show()
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun updateAudioLanguage(preferredLocale: Locale) {
lifecycleScope.launch { lifecycleScope.launch {
Crunchyroll.postPrefSubLanguage(preferredLocale.toLanguageTag()) Crunchyroll.setPreferredAudioLanguage(preferredLocale.toLanguageTag())
}.invokeOnCompletion { }.invokeOnCompletion {
// update the local preferred content language // update the local preferred audio language
Preferences.savePreferredSubtitleLocal(requireContext(), preferredLocale) Preferences.savePreferredAudioLocal(requireContext(), preferredLocale)
// update profile since the language selection might have changed // update profile since the language selection might have changed
profile = lifecycleScope.async { Crunchyroll.profile() } profile = lifecycleScope.async { Crunchyroll.profile() }
profile.invokeOnCompletion { profile.invokeOnCompletion {
// update language once loading profile is completed // update language once loading profile is completed
binding.textSettingsContentLanguageDesc.text = Locale.forLanguageTag( binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag(
profile.getCompleted().preferredContentAudioLanguage
).displayLanguage
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun updateSubtitleLanguage(preferredLocal: Locale) {
lifecycleScope.launch {
Crunchyroll.setPreferredSubtitleLanguage(preferredLocal.toLanguageTag())
}.invokeOnCompletion {
// update the local preferred subtitle language
Preferences.savePreferredSubtitleLocal(requireContext(), preferredLocal)
// update profile since the language selection might have changed
profile = lifecycleScope.async { Crunchyroll.profile() }
profile.invokeOnCompletion {
// update language once loading profile is completed
binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag(
profile.getCompleted().preferredContentSubtitleLanguage profile.getCompleted().preferredContentSubtitleLanguage
).displayLanguage ).displayLanguage
} }
@ -183,17 +227,19 @@ class AccountFragment : Fragment() {
private fun showThemeDialog() { private fun showThemeDialog() {
val items = arrayOf( val items = arrayOf(
resources.getString(R.string.theme_system),
resources.getString(R.string.theme_light), resources.getString(R.string.theme_light),
resources.getString(R.string.theme_dark) resources.getString(R.string.theme_dark)
) )
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.settings_content_language) .setTitle(R.string.theme)
.setSingleChoiceItems(items, Preferences.theme.ordinal){ _, which -> .setSingleChoiceItems(items, Preferences.theme.ordinal){ _, which ->
when(which) { when(which) {
0 -> Preferences.saveTheme(requireContext(), Theme.LIGHT) 0 -> Preferences.saveTheme(requireContext(), Theme.SYSTEM)
1 -> Preferences.saveTheme(requireContext(), Theme.DARK) 1 -> Preferences.saveTheme(requireContext(), Theme.LIGHT)
else -> Preferences.saveTheme(requireContext(), Theme.DARK) 2 -> Preferences.saveTheme(requireContext(), Theme.DARK)
else -> Preferences.saveTheme(requireContext(), Theme.SYSTEM)
} }
(activity as MainActivity).restart() (activity as MainActivity).restart()

View File

@ -47,7 +47,6 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
playerFinishedCallback() playerFinishedCallback()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentMediaBinding.inflate(inflater, container, false) binding = FragmentMediaBinding.inflate(inflater, container, false)
return binding.root return binding.root
@ -91,6 +90,7 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
// load poster and backdrop // load poster and backdrop
Glide.with(requireContext()).load(posterUrl) Glide.with(requireContext()).load(posterUrl)
.apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY)))
.into(binding.imagePoster) .into(binding.imagePoster)
Glide.with(requireContext()).load(backdropUrl) Glide.with(requireContext()).load(backdropUrl)
.apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY))) .apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY)))
@ -147,14 +147,14 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
// episodes count // episodes count
binding.textEpisodesOrRuntime.text = resources.getQuantityString( binding.textEpisodesOrRuntime.text = resources.getQuantityString(
R.plurals.text_episodes_count, R.plurals.text_episodes_count,
episodesCrunchy.total, seriesCrunchy.episodeCount,
episodesCrunchy.total seriesCrunchy.episodeCount
) )
} }
is TMDBMovie -> { is TMDBMovie -> {
val tmdbMovie = (tmdbResult as TMDBMovie?) val tmdbMovie = tmdbResult as TMDBMovie
if (tmdbMovie?.runtime != null) { if (tmdbMovie.runtime != null) {
binding.textEpisodesOrRuntime.text = resources.getQuantityString( binding.textEpisodesOrRuntime.text = resources.getQuantityString(
R.plurals.text_runtime, R.plurals.text_runtime,
tmdbMovie.runtime, tmdbMovie.runtime,

View File

@ -1,17 +1,21 @@
package org.mosad.teapod.ui.activity.main.fragments package org.mosad.teapod.ui.activity.main.fragments
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentMyListsBinding import org.mosad.teapod.databinding.FragmentMyListsBinding
import org.mosad.teapod.parser.crunchyroll.Crunchyroll import org.mosad.teapod.ui.activity.main.viewmodel.MyListsFragmentViewModel
import org.mosad.teapod.util.toItemMediaList import org.mosad.teapod.util.toItemMediaList
class MyListsFragment : Fragment() { class MyListsFragment : Fragment() {
@ -19,6 +23,8 @@ class MyListsFragment : Fragment() {
private lateinit var binding: FragmentMyListsBinding private lateinit var binding: FragmentMyListsBinding
private lateinit var pagerAdapter: FragmentStateAdapter private lateinit var pagerAdapter: FragmentStateAdapter
private val model: MyListsFragmentViewModel by viewModels()
private val fragments = arrayListOf<Fragment>() private val fragments = arrayListOf<Fragment>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -33,7 +39,6 @@ class MyListsFragment : Fragment() {
pagerAdapter = ScreenSlidePagerAdapter(this) pagerAdapter = ScreenSlidePagerAdapter(this)
binding.pagerMyLists.adapter = pagerAdapter binding.pagerMyLists.adapter = pagerAdapter
// TODO is position 0 always episodes? (and 1 always similar titles)
TabLayoutMediator(binding.tabMyLists, binding.pagerMyLists) { tab, position -> TabLayoutMediator(binding.tabMyLists, binding.pagerMyLists) { tab, position ->
tab.text = when(position) { tab.text = when(position) {
0 -> getString(R.string.my_list) 0 -> getString(R.string.my_list)
@ -43,15 +48,33 @@ class MyListsFragment : Fragment() {
} }
}.attach() }.attach()
lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
val items = Crunchyroll.watchlist(50) viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
model.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
MediaFragmentSimilar(items.toItemMediaList()).also { when (uiState) {
fragments.add(it) is MyListsFragmentViewModel.UiState.Normal -> bindUiStateNormal(uiState)
pagerAdapter.notifyItemInserted(fragments.indexOf(it)) is MyListsFragmentViewModel.UiState.Loading -> bindUiStateLoading()
is MyListsFragmentViewModel.UiState.Error -> bindUiStateError(uiState)
}
}
} }
} }
}
private fun bindUiStateNormal(uiState: MyListsFragmentViewModel.UiState.Normal) {
MediaFragmentSimilar(uiState.watchlistItems.toItemMediaList()).also {
fragments.add(it)
pagerAdapter.notifyItemInserted(fragments.indexOf(it))
}
}
private fun bindUiStateLoading() {
// currently not used
}
private fun bindUiStateError(uiState: MyListsFragmentViewModel.UiState.Error) {
// currently not used
Log.e(javaClass.name, "A error occurred while loading a UiState: ${uiState.message}")
} }
/** /**

View File

@ -32,7 +32,7 @@ import kotlinx.coroutines.launch
import org.mosad.teapod.parser.crunchyroll.* import org.mosad.teapod.parser.crunchyroll.*
import kotlin.random.Random import kotlin.random.Random
class HomeViewModel : ViewModel() { class HomeViewModel : ViewModel() {
private val WATCHLIST_LENGTH = 50 private val WATCHLIST_LENGTH = 50
@ -66,16 +66,16 @@ class HomeViewModel : ViewModel() {
uiState.emit(UiState.Loading) uiState.emit(UiState.Loading)
try { try {
// run the loading in parallel to speed up the process // run the loading in parallel to speed up the process
val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount().data } val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount(n = 20).data }
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).items } val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).data }
val recommendationsJob = viewModelScope.async { val recommendationsJob = viewModelScope.async {
Crunchyroll.recommendations(20).items Crunchyroll.recommendations(n = 20).data
} }
val recentlyAddedJob = viewModelScope.async { val recentlyAddedJob = viewModelScope.async {
Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50).items Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50).data
} }
val topTenJob = viewModelScope.async { val topTenJob = viewModelScope.async {
Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).items Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).data
} }
val recentlyAddedItems = recentlyAddedJob.await() val recentlyAddedItems = recentlyAddedJob.await()
@ -114,7 +114,7 @@ class HomeViewModel : ViewModel() {
} }
// update the watchlist after a item has been added/removed // update the watchlist after a item has been added/removed
val watchlistItems = Crunchyroll.watchlist(WATCHLIST_LENGTH).items val watchlistItems = Crunchyroll.watchlist(WATCHLIST_LENGTH).data
currentUiState.copy( currentUiState.copy(
watchlistItems = watchlistItems, watchlistItems = watchlistItems,
@ -133,7 +133,7 @@ class HomeViewModel : ViewModel() {
viewModelScope.launch { viewModelScope.launch {
uiState.update { currentUiState -> uiState.update { currentUiState ->
if (currentUiState is UiState.Normal) { if (currentUiState is UiState.Normal) {
val upNextItems = Crunchyroll.upNextAccount().data val upNextItems = Crunchyroll.upNextAccount(n = 20).data
currentUiState.copy(upNextItems = upNextItems) currentUiState.copy(upNextItems = upNextItems)
} else { } else {
currentUiState currentUiState

View File

@ -90,7 +90,7 @@ class LibraryFragmentViewModel : ViewModel() {
delay(250) delay(250)
val results = Crunchyroll.search(query, 50) val results = Crunchyroll.search(query, 50)
.items.firstOrNull()?.items?.toItemMediaList() .data.firstOrNull()?.items?.toItemMediaList()
?: listOf() ?: listOf()
uiState.emit(UiState.Search(results)) uiState.emit(UiState.Search(results))
} }

View File

@ -76,9 +76,9 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
currentEpisodesCrunchy.clear() currentEpisodesCrunchy.clear()
currentEpisodesCrunchy.addAll(episodesCrunchy.data) currentEpisodesCrunchy.addAll(episodesCrunchy.data)
// set media type // set media type, for movies the episode field is empty
mediaType = episodesCrunchy.data.firstOrNull()?.let { mediaType = episodesCrunchy.data.firstOrNull()?.let {
if (it.episodeNumber != null) MediaType.TVSHOW else MediaType.MOVIE if (it.episode.isNotEmpty()) MediaType.TVSHOW else MediaType.MOVIE
} ?: MediaType.OTHER } ?: MediaType.OTHER
// load playheads and tmdb in parallel // load playheads and tmdb in parallel

View File

@ -0,0 +1,50 @@
package org.mosad.teapod.ui.activity.main.viewmodel
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
import org.mosad.teapod.parser.crunchyroll.Item
class MyListsFragmentViewModel : ViewModel() {
private val WATCHLIST_LENGTH = 50
private val uiState = MutableStateFlow<UiState>(UiState.Loading)
sealed class UiState {
object Loading : UiState()
data class Normal(
val watchlistItems: List<Item>
) : UiState()
data class Error(val message: String?) : UiState()
}
init {
load()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
scope.launch { uiState.collect { collector(it) } }
}
fun load() {
viewModelScope.launch {
uiState.emit(UiState.Loading)
try {
// run the loading in parallel to speed up the process
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).data }
uiState.emit(
UiState.Normal(watchlistJob.await())
)
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
}
}
}
}

View File

@ -157,9 +157,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
if (newAudioLocale != currentAudioLocale) { if (newAudioLocale != currentAudioLocale) {
currentAudioLocale = newAudioLocale currentAudioLocale = newAudioLocale
currentVersion = currentEpisode.versions.firstOrNull { currentVersion = currentEpisode.versions?.firstOrNull {
it.audioLocale == currentAudioLocale.toLanguageTag() it.audioLocale == currentAudioLocale.toLanguageTag()
} ?: currentEpisode.versions.first() } ?: currentEpisode.versions?.first() ?: NoneVersion
viewModelScope.launch { viewModelScope.launch {
currentStreams = Crunchyroll.streamsFromMediaGUID(currentVersion.mediaGUID) currentStreams = Crunchyroll.streamsFromMediaGUID(currentVersion.mediaGUID)
@ -222,15 +222,16 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
// needs to be blocking, currentPlayback must be present when calling playCurrentMedia() // needs to be blocking, currentPlayback must be present when calling playCurrentMedia()
joinAll( joinAll(
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
currentVersion = if (Preferences.preferSubbed) { currentVersion = currentEpisode.versions?.firstOrNull {
currentEpisode.versions.first { it.original } it.audioLocale == currentAudioLocale.toLanguageTag()
} else { } ?: currentEpisode.versions?.first() ?: NoneVersion
currentEpisode.versions
.firstOrNull { it.audioLocale == currentAudioLocale.toLanguageTag() }
?: currentEpisode.versions.first()
}
currentStreams = Crunchyroll.streamsFromMediaGUID(currentVersion.mediaGUID) // get the current streams object, if no version is set, use streamsLink
currentStreams = if (currentVersion != NoneVersion) {
Crunchyroll.streamsFromMediaGUID(currentVersion.mediaGUID)
} else {
Crunchyroll.streams(currentEpisode.streamsLink)
}
Log.d(classTag, currentVersion.toString()) Log.d(classTag, currentVersion.toString())
}, },
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
@ -246,7 +247,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
} }
}, },
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
currentIntroMetadata = NoneDatalabIntro //Crunchyroll.datalabIntro(currentEpisode.id) currentIntroMetadata = Crunchyroll.datalabIntro(currentEpisode.id)
} }
) )
Log.d(classTag, "streams: ${currentEpisode.streamsLink}") Log.d(classTag, "streams: ${currentEpisode.streamsLink}")

View File

@ -14,6 +14,8 @@ import android.widget.TextView
import androidx.core.view.children import androidx.core.view.children
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.databinding.PlayerLanguageSettingsBinding import org.mosad.teapod.databinding.PlayerLanguageSettingsBinding
import org.mosad.teapod.ui.activity.player.PlayerViewModel import org.mosad.teapod.ui.activity.player.PlayerViewModel
@ -64,7 +66,7 @@ class LanguageSettingsDialogFragment : DialogFragment() {
val currentAudioLocal = Locale.forLanguageTag(model.currentVersion.audioLocale) val currentAudioLocal = Locale.forLanguageTag(model.currentVersion.audioLocale)
var selectedAudioView: TextView? = null var selectedAudioView: TextView? = null
model.currentEpisode.versions.forEach { version -> model.currentEpisode.versions?.forEach { version ->
val locale = Locale.forLanguageTag(version.audioLocale) val locale = Locale.forLanguageTag(version.audioLocale)
val audioView = addLanguage(binding.linearAudioLanguages, locale) { v -> val audioView = addLanguage(binding.linearAudioLanguages, locale) { v ->
selectedAudioLocale = locale selectedAudioLocale = locale
@ -81,8 +83,10 @@ class LanguageSettingsDialogFragment : DialogFragment() {
binding.buttonCloseLanguageSettings.setOnClickListener { dismiss() } binding.buttonCloseLanguageSettings.setOnClickListener { dismiss() }
binding.buttonCancel.setOnClickListener { dismiss() } binding.buttonCancel.setOnClickListener { dismiss() }
binding.buttonSelect.setOnClickListener { binding.buttonSelect.setOnClickListener {
model.setLanguage(selectedAudioLocale, selectedSubtitleLocale) lifecycleScope.launch {
dismiss() model.setLanguage(selectedAudioLocale, selectedSubtitleLocale)
dismiss()
}
} }
// initially hide the status and navigation bar // initially hide the status and navigation bar
@ -109,7 +113,7 @@ class LanguageSettingsDialogFragment : DialogFragment() {
gravity = Gravity.CENTER_VERTICAL gravity = Gravity.CENTER_VERTICAL
text = if (locale == Locale.ROOT) context.getString(R.string.no_subtitles) else locale.displayLanguage text = if (locale == Locale.ROOT) context.getString(R.string.no_subtitles) else locale.displayLanguage
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f) setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
setTextColor(context.resources.getColor(R.color.textSecondaryDark, context.theme)) setTextColor(context.resources.getColor(R.color.player_text, context.theme))
setPadding(75, 0, 0, 0) setPadding(75, 0, 0, 0)
setOnClickListener(onClick) setOnClickListener(onClick)
@ -131,7 +135,7 @@ class LanguageSettingsDialogFragment : DialogFragment() {
languageLayout.children.forEach { child -> languageLayout.children.forEach { child ->
if (child is TextView) { if (child is TextView) {
child.apply { child.apply {
setTextColor(context.resources.getColor(R.color.textPrimaryDark, context.theme)) setTextColor(context.resources.getColor(R.color.player_text, context.theme))
setTypeface(null, Typeface.NORMAL) setTypeface(null, Typeface.NORMAL)
setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
setPadding(75, 0, 0, 0) setPadding(75, 0, 0, 0)

View File

@ -10,6 +10,7 @@ class DataTypes {
} }
enum class Theme(val str: String) { enum class Theme(val str: String) {
SYSTEM("System"),
LIGHT("Light"), LIGHT("Light"),
DARK("Dark") DARK("Dark")
} }

View File

@ -9,12 +9,11 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.parser.crunchyroll.Collection import org.mosad.teapod.parser.crunchyroll.CollectionV2
import org.mosad.teapod.parser.crunchyroll.Collection2
import org.mosad.teapod.parser.crunchyroll.Item import org.mosad.teapod.parser.crunchyroll.Item
import org.mosad.teapod.parser.crunchyroll.PlayheadObject import org.mosad.teapod.parser.crunchyroll.PlayheadObject
import org.mosad.teapod.ui.activity.player.PlayerActivity import org.mosad.teapod.ui.activity.player.PlayerActivity
import java.util.* import java.util.Locale
/** /**
* Create a Intent for PlayerActivity with season and episode id. * Create a Intent for PlayerActivity with season and episode id.
@ -36,8 +35,8 @@ fun <T> concatenate(vararg lists: List<T>): List<T> {
} }
// TODO move to correct location // TODO move to correct location
fun Collection<Item>.toItemMediaList(): List<ItemMedia> { fun CollectionV2<Item>.toItemMediaList(): List<ItemMedia> {
return this.items.map { return this.data.map {
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source) ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
} }
} }
@ -59,7 +58,7 @@ fun Locale.toDisplayString(fallback: String): String {
} }
} }
fun Collection2<PlayheadObject>.toPlayheadsMap(): Map<String, PlayheadObject> { fun CollectionV2<PlayheadObject>.toPlayheadsMap(): Map<String, PlayheadObject> {
return this.data.associateBy { it.contentId } return this.data.associateBy { it.contentId }
} }

View File

@ -67,7 +67,7 @@ class TMDBApiController {
): T = coroutineScope { ): T = coroutineScope {
val path = "$apiUrl$endpoint" val path = "$apiUrl$endpoint"
val params = concatenate( val params = concatenate(
listOf("api_key" to apiKey, "language" to Preferences.preferredSubtitleLocale.language), listOf("api_key" to apiKey, "language" to Preferences.preferredSubtitleLocale.toLanguageTag()),
parameters parameters
) )

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_checked="true"/>
<item android:color="?attr/iconColor"/>
</selector>

View File

@ -6,7 +6,7 @@
android:shape="ring" android:shape="ring"
android:thickness="4dp" android:thickness="4dp"
android:useLevel="false"> android:useLevel="false">
<solid android:color="?iconColor"/> <solid android:color="?colorOutline"/>
</shape> </shape>
</item> </item>
</layer-list> </layer-list>

View File

@ -6,7 +6,7 @@
android:shape="ring" android:shape="ring"
android:thickness="4dp" android:thickness="4dp"
android:useLevel="false"> android:useLevel="false">
<solid android:color="@color/colorAccent" /> <solid android:color="?colorSecondary" />
</shape> </shape>
</item> </item>
</layer-list> </layer-list>

View File

@ -1,6 +1,13 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/> android:viewportWidth="24"
<path android:fillColor="@android:color/white" android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/> android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
</vector> </vector>

View File

@ -4,7 +4,7 @@
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="?attr/colorControlNormal"> android:tint="?attr/colorControlNormal">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector> </vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
</vector>

View File

@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
<path android:fillColor="@android:color/white" android:pathData="M9.4,16.6L4.8,12l4.6,-4.6L8,6l-6,6 6,6 1.4,-1.4zM14.6,16.6l4.6,-4.6 -4.6,-4.6L16,6l6,6 -6,6 -1.4,-1.4z"/> android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M9.4,16.6L4.8,12l4.6,-4.6L8,6l-6,6 6,6 1.4,-1.4zM14.6,16.6l4.6,-4.6 -4.6,-4.6L16,6l6,6 -6,6 -1.4,-1.4z"/>
</vector> </vector>

View File

@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
<path android:fillColor="@android:color/white" android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/> android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/>
</vector> </vector>

View File

@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/> android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
</vector> </vector>

View File

@ -1,5 +1,8 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path android:fillColor="@android:color/white" android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/> <path android:fillColor="@android:color/white" android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
</vector> </vector>

View File

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="?attr/iconColor"> android:tint="?attr/colorControlNormal">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>

View File

@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
<path android:fillColor="@android:color/white" android:pathData="M2.53,19.65l1.34,0.56v-9.03l-2.43,5.86c-0.41,1.02 0.08,2.19 1.09,2.61zM22.03,15.95L17.07,3.98c-0.31,-0.75 -1.04,-1.21 -1.81,-1.23 -0.26,0 -0.53,0.04 -0.79,0.15L7.1,5.95c-0.75,0.31 -1.21,1.03 -1.23,1.8 -0.01,0.27 0.04,0.54 0.15,0.8l4.96,11.97c0.31,0.76 1.05,1.22 1.83,1.23 0.26,0 0.52,-0.05 0.77,-0.15l7.36,-3.05c1.02,-0.42 1.51,-1.59 1.09,-2.6zM7.88,8.75c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM5.88,19.75c0,1.1 0.9,2 2,2h1.45l-3.45,-8.34v6.34z"/> android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M2.53,19.65l1.34,0.56v-9.03l-2.43,5.86c-0.41,1.02 0.08,2.19 1.09,2.61zM22.03,15.95L17.07,3.98c-0.31,-0.75 -1.04,-1.21 -1.81,-1.23 -0.26,0 -0.53,0.04 -0.79,0.15L7.1,5.95c-0.75,0.31 -1.21,1.03 -1.23,1.8 -0.01,0.27 0.04,0.54 0.15,0.8l4.96,11.97c0.31,0.76 1.05,1.22 1.83,1.23 0.26,0 0.52,-0.05 0.77,-0.15l7.36,-3.05c1.02,-0.42 1.51,-1.59 1.09,-2.6zM7.88,8.75c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM5.88,19.75c0,1.1 0.9,2 2,2h1.45l-3.45,-8.34v6.34z"/>
</vector> </vector>

View File

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" /> android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" />

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?shapeTextBackground"/> <solid android:color="?colorSurfaceVariant"/>
<size <size
android:width="1920px" android:width="1920px"
android:height="1080px"/> android:height="1080px"/>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?colorSurfaceVariant"/>
<size
android:width="400px"
android:height="600px"/>
</shape>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/shapeTextBackground"/> <solid android:color="?colorSurfaceVariant"/>
<corners android:radius="3dp"/> <corners android:radius="3dp"/>
</shape> </shape>

View File

@ -9,8 +9,6 @@
android:id="@+id/nav_view" android:id="@+id/nav_view"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?themeSecondary"
app:itemIconTint="@color/bottom_nav_item_tint"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"

View File

@ -1,11 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary"
tools:context=".ui.activity.main.fragments.AboutFragment"> tools:context=".ui.activity.main.fragments.AboutFragment">
<LinearLayout <LinearLayout
@ -67,8 +64,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_outline_info_24" android:src="@drawable/ic_outline_info_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -89,8 +85,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/version_desc" android:text="@string/version_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -112,8 +107,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_people_24" android:src="@drawable/ic_baseline_people_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -134,8 +128,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/author_desc" android:text="@string/author_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -157,8 +150,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_code_24" android:src="@drawable/ic_baseline_code_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -179,8 +171,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/teapod_repo" android:text="@string/teapod_repo" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -202,8 +193,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_description_24" android:src="@drawable/ic_baseline_description_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -224,8 +214,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/license_desc" android:text="@string/license_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -267,8 +256,7 @@
android:layout_marginEnd="7dp" android:layout_marginEnd="7dp"
android:paddingBottom="5dp" android:paddingBottom="5dp"
android:text="@string/tmdb_notice" android:text="@string/tmdb_notice"
android:textAlignment="center" android:textAlignment="center" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@ -4,12 +4,12 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary"
tools:context=".ui.activity.main.fragments.AccountFragment"> tools:context=".ui.activity.main.fragments.AccountFragment">
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -23,7 +23,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:background="?themeSecondary"
android:elevation="5dp" android:elevation="5dp"
android:orientation="vertical"> android:orientation="vertical">
@ -34,7 +33,7 @@
android:paddingStart="7dp" android:paddingStart="7dp"
android:paddingEnd="7dp" android:paddingEnd="7dp"
android:text="@string/account" android:text="@string/account"
android:textSize="16sp" android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textStyle="bold" /> android:textStyle="bold" />
<LinearLayout <LinearLayout
@ -55,8 +54,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_account_box_24" android:src="@drawable/ic_baseline_account_box_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -69,15 +67,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/account_login_ex" android:text="@string/account_login_ex"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_account_login_desc" android:id="@+id/text_account_login_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/account_login_desc" android:text="@string/account_login_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -99,8 +96,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_access_time_24" android:src="@drawable/ic_baseline_access_time_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -113,15 +109,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/loading" android:text="@string/loading"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_account_subscription_desc" android:id="@+id/text_account_subscription_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/account_tier" android:text="@string/account_tier" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -132,7 +127,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:background="?themeSecondary"
android:elevation="5dp" android:elevation="5dp"
android:orientation="vertical"> android:orientation="vertical">
@ -143,11 +137,11 @@
android:paddingStart="7dp" android:paddingStart="7dp"
android:paddingEnd="7dp" android:paddingEnd="7dp"
android:text="@string/settings" android:text="@string/settings"
android:textSize="16sp" android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textStyle="bold" /> android:textStyle="bold" />
<LinearLayout <LinearLayout
android:id="@+id/linear_settings_content_language" android:id="@+id/linear_settings_audio_language"
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"
@ -157,13 +151,12 @@
android:id="@+id/imageView4" android:id="@+id/imageView4"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/settings_content_language" android:contentDescription="@string/settings_audio_language"
android:minWidth="48dp" android:minWidth="48dp"
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_language_24" android:src="@drawable/ic_baseline_audiotrack_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -174,82 +167,53 @@
android:id="@+id/text_settings_content_language" android:id="@+id/text_settings_content_language"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/settings_content_language" android:text="@string/settings_audio_language"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_settings_content_language_desc" android:id="@+id/text_settings_audio_language_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/settings_content_language_desc" android:text="@string/settings_content_language_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/linear_settings_secondary" android:id="@+id/linear_settings_subtitle_language"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="7dp"> android:padding="7dp">
<ImageView <ImageView
android:id="@+id/imageView3" android:id="@+id/imageView7"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/settings_prefer_subbed" android:contentDescription="@string/settings_subtitle_language"
android:minWidth="48dp" android:minWidth="48dp"
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_subtitles_24" android:src="@drawable/ic_baseline_subtitles_24" />
app:tint="?iconColor" />
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout <TextView
android:id="@+id/linearLayout" android:id="@+id/text_settings_subtitle_language"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:text="@string/settings_subtitle_language"
app:layout_constraintBottom_toBottomOf="parent" android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
app:layout_constraintEnd_toStartOf="@+id/switch_secondary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/text_settings_secondary" android:id="@+id/text_settings_subtitle_language_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_prefer_subbed"
android:textSize="16sp" />
<TextView
android:id="@+id/text_settings_secondary_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="2"
android:text="@string/settings_prefer_subbed_desc"
android:textColor="?textSecondary" />
</LinearLayout>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="true" android:text="@string/settings_content_language_desc" />
android:contentDescription="@string/settings_prefer_subbed" </LinearLayout>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@ -268,8 +232,7 @@
android:minWidth="48dp" android:minWidth="48dp"
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:src="@drawable/ic_baseline_autorenew_24" android:src="@drawable/ic_baseline_autorenew_24" />
app:tint="?iconColor" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -290,14 +253,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/settings_autoplay" android:text="@string/settings_autoplay"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_settings_auoplay_desc" android:id="@+id/text_settings_auoplay_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/settings_autoplay_desc" android:text="@string/settings_autoplay_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
@ -331,8 +293,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_style_24" android:src="@drawable/ic_baseline_style_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -345,15 +306,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/theme" android:text="@string/theme"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_theme_selected" android:id="@+id/text_theme_selected"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/theme_light" android:text="@string/theme_light" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -365,7 +325,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:background="?themeSecondary"
android:clipToPadding="false" android:clipToPadding="false"
android:elevation="5dp" android:elevation="5dp"
android:orientation="vertical"> android:orientation="vertical">
@ -377,7 +336,7 @@
android:paddingStart="7dp" android:paddingStart="7dp"
android:paddingEnd="7dp" android:paddingEnd="7dp"
android:text="@string/dev_settings" android:text="@string/dev_settings"
android:textSize="16sp" android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textStyle="bold" /> android:textStyle="bold" />
<LinearLayout <LinearLayout
@ -397,8 +356,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_baseline_access_time_24" android:src="@drawable/ic_baseline_access_time_24" />
app:tint="?iconColor" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -419,14 +377,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/update_playhead" android:text="@string/update_playhead"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_update_playhead_desc" android:id="@+id/text_update_playhead_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/update_playhead_desc" android:text="@string/update_playhead_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
@ -462,8 +419,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
app:srcCompat="@drawable/ic_outline_upload_24" app:srcCompat="@drawable/ic_outline_upload_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -483,8 +439,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/export_data_desc" android:text="@string/export_data_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -508,8 +463,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
app:srcCompat="@drawable/ic_outline_download_24" app:srcCompat="@drawable/ic_outline_download_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -529,8 +483,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/import_data_desc" android:text="@string/import_data_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -542,7 +495,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:background="?themeSecondary"
android:clipToPadding="false" android:clipToPadding="false"
android:elevation="5dp" android:elevation="5dp"
android:orientation="vertical"> android:orientation="vertical">
@ -554,7 +506,7 @@
android:paddingStart="7dp" android:paddingStart="7dp"
android:paddingEnd="7dp" android:paddingEnd="7dp"
android:text="@string/info" android:text="@string/info"
android:textSize="16sp" android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textStyle="bold" /> android:textStyle="bold" />
<LinearLayout <LinearLayout
@ -575,8 +527,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:padding="9dp" android:padding="9dp"
android:scaleType="fitXY" android:scaleType="fitXY"
app:srcCompat="@drawable/ic_outline_info_24" app:srcCompat="@drawable/ic_outline_info_24" />
app:tint="?iconColor" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -589,15 +540,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/info_about" android:text="@string/info_about"
android:textSize="16sp" /> android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView <TextView
android:id="@+id/text_info_about_desc" android:id="@+id/text_info_about_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/info_about_desc" android:text="@string/info_about_desc" />
android:textColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -5,12 +5,12 @@
android:id="@+id/ff_test" android:id="@+id/ff_test"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary"
tools:context=".ui.activity.main.fragments.HomeFragment"> tools:context=".ui.activity.main.fragments.HomeFragment">
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -70,9 +70,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:text="@string/my_list" android:text="@string/my_list"
android:textColor="?textSecondary"
android:textSize="12sp" android:textSize="12sp"
app:drawableTint="?buttonBackground"
app:drawableTopCompat="@drawable/ic_baseline_add_24" /> app:drawableTopCompat="@drawable/ic_baseline_add_24" />
<Space <Space
@ -87,12 +85,9 @@
android:gravity="center" android:gravity="center"
android:text="@string/button_play" android:text="@string/button_play"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="?themePrimary"
android:textSize="16sp" android:textSize="16sp"
app:backgroundTint="?buttonBackground"
app:icon="@drawable/ic_baseline_play_arrow_24" app:icon="@drawable/ic_baseline_play_arrow_24"
app:iconGravity="textStart" app:iconGravity="textStart" />
app:iconTint="?themePrimary" />
<Space <Space
android:layout_width="0dp" android:layout_width="0dp"
@ -105,9 +100,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:text="@string/info" android:text="@string/info"
android:textColor="?textSecondary"
android:textSize="12sp" android:textSize="12sp"
app:drawableTint="?buttonBackground"
app:drawableTopCompat="@drawable/ic_outline_info_24" /> app:drawableTopCompat="@drawable/ic_outline_info_24" />
<Space <Space

View File

@ -4,14 +4,12 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary"
tools:context=".ui.activity.main.fragments.LibraryFragment"> tools:context=".ui.activity.main.fragments.LibraryFragment">
<org.mosad.teapod.ui.components.EmptySubmitSearchView <org.mosad.teapod.ui.components.EmptySubmitSearchView
android:id="@+id/search_text" android:id="@+id/search_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:background="?themeSecondary"
android:elevation="8dp" android:elevation="8dp"
android:iconifiedByDefault="false" android:iconifiedByDefault="false"
android:paddingBottom="5dp" android:paddingBottom="5dp"

View File

@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary"
tools:context=".ui.activity.main.fragments.MediaFragment"> tools:context=".ui.activity.main.fragments.MediaFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
@ -14,8 +13,7 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_layout" android:id="@+id/app_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:background="?themePrimary">
<LinearLayout <LinearLayout
android:id="@+id/linear_media" android:id="@+id/linear_media"
@ -44,7 +42,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/media_poster_backdrop_desc" android:contentDescription="@string/media_poster_backdrop_desc"
android:scaleType="fitCenter" android:scaleType="fitCenter"
tools:srcCompat="@drawable/placeholder_image" /> tools:srcCompat="@android:color/darker_gray" />
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_poster" android:id="@+id/image_poster"
@ -55,7 +53,7 @@
android:layout_marginBottom="7dp" android:layout_marginBottom="7dp"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:shapeAppearance="@style/ShapeAppearance.Teapod.RoundedPoster" app:shapeAppearance="@style/ShapeAppearance.Teapod.RoundedPoster"
tools:src="@drawable/ic_launcher_background" /> tools:src="@drawable/placeholder_image_2_3" />
</FrameLayout> </FrameLayout>
@ -108,12 +106,9 @@
android:gravity="center" android:gravity="center"
android:text="@string/button_play" android:text="@string/button_play"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="?themePrimary"
android:textSize="16sp" android:textSize="16sp"
app:backgroundTint="?buttonBackground"
app:icon="@drawable/ic_baseline_play_arrow_24" app:icon="@drawable/ic_baseline_play_arrow_24"
app:iconGravity="textStart" app:iconGravity="textStart" />
app:iconTint="?themePrimary" />
<TextView <TextView
android:id="@+id/text_title" android:id="@+id/text_title"
@ -163,15 +158,13 @@
android:paddingTop="11dp" android:paddingTop="11dp"
android:paddingEnd="11dp" android:paddingEnd="11dp"
android:paddingBottom="7dp" android:paddingBottom="7dp"
android:src="@drawable/ic_baseline_add_24" android:src="@drawable/ic_baseline_add_24" />
app:tint="?buttonBackground" />
<TextView <TextView
android:id="@+id/text_my_list_action" android:id="@+id/text_my_list_action"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/my_list" android:text="@string/my_list"
android:textColor="?textSecondary"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@ -186,9 +179,7 @@
android:layout_marginEnd="7dp" android:layout_marginEnd="7dp"
android:background="@android:color/transparent" android:background="@android:color/transparent"
app:tabGravity="start" app:tabGravity="start"
app:tabMode="scrollable" app:tabMode="scrollable" />
app:tabSelectedTextColor="?textPrimary"
app:tabTextColor="?textSecondary" />
</LinearLayout> </LinearLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -207,7 +198,7 @@
android:id="@+id/frame_loading" android:id="@+id/frame_loading"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary" android:background="?android:colorBackground"
android:visibility="gone"> android:visibility="gone">
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator

View File

@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themePrimary"
tools:context=".ui.activity.main.fragments.MyListsFragment"> tools:context=".ui.activity.main.fragments.MyListsFragment">
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
@ -15,9 +14,8 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:tabMode="fixed" app:tabMode="fixed">
app:tabSelectedTextColor="?textPrimary" <!-- TODO app:tabTextColor="?colorOnPrimary" -->
app:tabTextColor="?textSecondary">
<com.google.android.material.tabs.TabItem <com.google.android.material.tabs.TabItem
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -2,8 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="?themePrimary">
<ImageView <ImageView
android:id="@+id/image_login" android:id="@+id/image_login"
@ -11,12 +10,12 @@
android:layout_height="128dp" android:layout_height="128dp"
android:contentDescription="@string/app_name" android:contentDescription="@string/app_name"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:tint="?colorTeapodIcon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_foreground" app:srcCompat="@drawable/ic_launcher_foreground" />
app:tint="?buttonBackground" />
<LinearLayout <LinearLayout
android:id="@+id/linear_login" android:id="@+id/linear_login"

View File

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="?themePrimary">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -17,12 +15,12 @@
android:layout_height="128dp" android:layout_height="128dp"
android:contentDescription="@string/app_name" android:contentDescription="@string/app_name"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:tint="?colorTeapodIcon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_foreground" app:srcCompat="@drawable/ic_launcher_foreground" />
app:tint="?buttonBackground" />
<LinearLayout <LinearLayout
android:id="@+id/linearLayout3" android:id="@+id/linearLayout3"

View File

@ -24,8 +24,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/component_poster_desc" android:contentDescription="@string/component_poster_desc"
app:shapeAppearance="@style/ShapeAppearance.Teapod.RoundedPoster" android:src="@drawable/placeholder_image"
app:srcCompat="@color/imagePlaceholder" /> app:shapeAppearance="@style/ShapeAppearance.Teapod.RoundedPoster" />
<ImageView <ImageView
android:id="@+id/image_episode_play" android:id="@+id/image_episode_play"
@ -56,7 +56,6 @@
android:ellipsize="end" android:ellipsize="end"
android:maxLines="3" android:maxLines="3"
android:text="@string/component_episode_title" android:text="@string/component_episode_title"
android:textColor="?textPrimary"
android:textSize="16sp" /> android:textSize="16sp" />
<ImageView <ImageView
@ -65,8 +64,7 @@
android:layout_height="30dp" android:layout_height="30dp"
android:layout_margin="2dp" android:layout_margin="2dp"
android:contentDescription="@string/component_watched_desc" android:contentDescription="@string/component_watched_desc"
app:srcCompat="@drawable/ic_baseline_check_circle_24" app:srcCompat="@drawable/ic_baseline_check_circle_24" />
app:tint="?iconColor" />
</LinearLayout> </LinearLayout>
<TextView <TextView
@ -74,6 +72,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="3" android:maxLines="3" />
android:textColor="?textSecondary" /> <!-- TODO android:textColor="?textSecondary" -->
</LinearLayout> </LinearLayout>

View File

@ -15,8 +15,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/component_poster_desc" android:contentDescription="@string/component_poster_desc"
app:shapeAppearance="@style/ShapeAppearance.Teapod.RoundedPoster" android:src="@drawable/placeholder_image"
app:srcCompat="@color/imagePlaceholder" /> app:shapeAppearance="@style/ShapeAppearance.Teapod.RoundedPoster" />
<ImageView <ImageView
android:id="@+id/image_episode_play" android:id="@+id/image_episode_play"
@ -44,7 +44,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="7dp" android:layout_marginTop="7dp"
android:text="@string/component_episode_title" android:text="@string/component_episode_title"
android:textColor="@color/textPrimaryDark" android:textColor="@color/player_text"
android:textSize="16sp" /> android:textSize="16sp" />
<View <View
@ -53,7 +53,7 @@
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:background="@color/textSecondaryDark" /> android:background="@color/player_text_secondary" />
<TextView <TextView
android:id="@+id/text_episode_desc2" android:id="@+id/text_episode_desc2"
@ -62,6 +62,6 @@
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:maxLines="10" android:maxLines="10"
android:text="@string/text_overview_ex" android:text="@string/text_overview_ex"
android:textColor="@color/textPrimaryDark" /> android:textColor="@color/player_text" />
</LinearLayout> </LinearLayout>

View File

@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:background="?themePrimary">
<ImageView <ImageView
android:id="@+id/shimmer_image_highlight" android:id="@+id/shimmer_image_highlight"
@ -21,7 +20,6 @@
android:id="@+id/shimmer_linear_highlight" android:id="@+id/shimmer_linear_highlight"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?themePrimary"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="7dp" android:paddingBottom="7dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -56,7 +54,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:textSize="12sp" android:textSize="12sp"
app:drawableTint="?shapeTextBackground"
app:drawableTopCompat="@drawable/ic_baseline_add_24" /> app:drawableTopCompat="@drawable/ic_baseline_add_24" />
<Space <Space
@ -69,8 +66,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:textSize="16sp" android:textSize="16sp" />
app:backgroundTint="?shapeTextBackground" />
<Space <Space
android:layout_width="0dp" android:layout_width="0dp"
@ -82,7 +78,6 @@
android:layout_width="64dp" android:layout_width="64dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
app:drawableTint="?shapeTextBackground"
app:drawableTopCompat="@drawable/ic_outline_info_24" /> app:drawableTopCompat="@drawable/ic_outline_info_24" />
<Space <Space

View File

@ -8,7 +8,6 @@
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:backgroundTint="?themeSecondary"
app:cardCornerRadius="7dp" app:cardCornerRadius="7dp"
app:cardElevation="4dp" app:cardElevation="4dp"
app:cardUseCompatPadding="true" app:cardUseCompatPadding="true"

View File

@ -8,7 +8,6 @@
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:backgroundTint="?themeSecondary"
app:cardCornerRadius="7dp" app:cardCornerRadius="7dp"
app:cardElevation="4dp" app:cardElevation="4dp"
app:cardUseCompatPadding="true" app:cardUseCompatPadding="true"
@ -35,7 +34,6 @@
android:id="@+id/image_poster" android:id="@+id/image_poster"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?shapeTextBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>

View File

@ -4,7 +4,6 @@
android:id="@+id/standard_bottom_sheet" android:id="@+id/standard_bottom_sheet"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?themeSecondary"
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="24dp" android:paddingTop="24dp"
android:paddingStart="24dp" android:paddingStart="24dp"
@ -61,8 +60,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:text="@string/cancel" android:text="@string/cancel" />
android:textColor="?colorPrimary" />
<Button <Button
android:id="@+id/positive_button" android:id="@+id/positive_button"
@ -70,8 +68,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:text="@string/save" android:text="@string/save" />
android:textColor="?colorPrimary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -138,9 +138,9 @@
android:layout_marginEnd="7dp" android:layout_marginEnd="7dp"
android:text="@string/cancel" android:text="@string/cancel"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@color/player_white" android:textColor="@color/button_text_color_light"
android:textSize="16sp" android:textSize="16sp"
app:backgroundTint="@color/buttonBackgroundLight" app:backgroundTint="@color/button_background_light"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -151,9 +151,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/apply" android:text="@string/apply"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@color/themePrimaryDark" android:textColor="@color/button_text_color_dark"
android:textSize="16sp" android:textSize="16sp"
app:backgroundTint="@color/buttonBackgroundDark" app:backgroundTint="@color/button_background_dark"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View File

@ -45,7 +45,8 @@
<string name="info">Info</string> <string name="info">Info</string>
<string name="info_about_desc">Version %1$s (%2$s)</string> <string name="info_about_desc">Version %1$s (%2$s)</string>
<string name="settings">Einstellungen</string> <string name="settings">Einstellungen</string>
<string name="settings_content_language">Bevorzuge Inhaltssprache</string> <string name="settings_audio_language">Audio Sprache</string>
<string name="settings_subtitle_language">Untertielsprache</string>
<string name="settings_content_language_desc">Englisch</string> <string name="settings_content_language_desc">Englisch</string>
<string name="settings_content_language_none">Keine</string> <string name="settings_content_language_none">Keine</string>
<string name="settings_prefer_subbed">Bevorzuge OmU</string> <string name="settings_prefer_subbed">Bevorzuge OmU</string>
@ -55,6 +56,7 @@
<string name="theme">Design</string> <string name="theme">Design</string>
<string name="theme_light">Hell</string> <string name="theme_light">Hell</string>
<string name="theme_dark">Dunkel</string> <string name="theme_dark">Dunkel</string>
<string name="theme_system">System</string>
<string name="dev_settings">Entwickler Einstellungen</string> <string name="dev_settings">Entwickler Einstellungen</string>
<string name="update_playhead">Playhead Updates</string> <string name="update_playhead">Playhead Updates</string>
<string name="update_playhead_desc">Fortschritt bei Episoden auf cr updaten</string> <string name="update_playhead_desc">Fortschritt bei Episoden auf cr updaten</string>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Widget.App.Button" parent="Widget.Material3.Button">
<item name="backgroundTint">@color/button_background_dark</item>
<item name="android:textColor">@color/button_text_color_dark</item>
<item name="iconTint">@color/button_text_color_dark</item>
</style>
<style name="AppTheme" parent="Theme.Material3.Dark.NoActionBar">
<!-- <item name="materialButtonStyle">@style/Widget.App.Button</item>-->
<item name="searchViewStyle">@style/SearchViewStyle</item>
<item name="materialCardViewStyle">?attr/materialCardViewElevatedStyle</item>
<item name="colorPrimary">@color/seed</item>
<item name="colorOnPrimary">@color/md_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_background</item>
<item name="colorOnBackground">@color/md_theme_dark_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_inversePrimary</item>
<item name="colorTeapodIcon">@color/button_background_dark</item>
</style>
</resources>

View File

@ -1,10 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<attr format="color" name="themePrimary"/> <attr format="color" name="colorTeapodIcon"/>
<attr format="color" name="themeSecondary"/>
<attr format="color" name="textPrimary"/>
<attr format="color" name="textSecondary"/>
<attr format="color" name="iconColor"/>
<attr format="color" name="buttonBackground"/>
<attr format="color" name="shapeTextBackground"/>
</resources> </resources>

View File

@ -1,34 +1,83 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- base theme colors -->
<color name="colorPrimary">#66aa00</color>
<color name="colorPrimaryLight">#99dc45</color>
<color name="colorPrimaryDark">#317a00</color>
<color name="colorAccent">#607d8b</color>
<color name="imagePlaceholder">#c2c2c2</color>
<!-- light theme colors --> <!-- light theme colors -->
<color name="themePrimaryLight">#ffffff</color> <color name="button_background_light">#000000</color>
<color name="themeSecondaryLight">#ffffff</color> <color name="button_text_color_light">#ffffff</color>
<color name="textPrimaryLight">#de000000</color>
<color name="textSecondaryLight">#99000000</color>
<color name="textBackgroundLight">#55000000</color>
<color name="iconColorLight">#99000000</color>
<color name="buttonBackgroundLight">#000000</color>
<!-- dark theme colors --> <!-- dark theme colors -->
<color name="themePrimaryDark">#121212</color> <color name="button_background_dark">#ffffff</color>
<color name="themeSecondaryDark">#202020</color> <color name="button_text_color_dark">#000000</color>
<color name="textPrimaryDark">#deffffff</color>
<color name="textSecondaryDark">#99ffffff</color> <!-- material3 colors -->
<color name="textBackgroundDark">#55ffffff</color> <color name="seed">#66aa00</color> <!-- base/primary color -->
<color name="iconColorDark">#99ffffff</color> <color name="md_theme_light_primary">#3E6A00</color>
<color name="buttonBackgroundDark">#ffffff</color> <color name="md_theme_light_onPrimary">#FFFFFF</color>
<color name="controlHighlightDark">#11ffffff</color> <color name="md_theme_light_primaryContainer">#99d853</color>
<color name="md_theme_light_onPrimaryContainer">#0F2000</color>
<color name="md_theme_light_secondary">#416916</color>
<color name="md_theme_light_onSecondary">#FFFFFF</color>
<color name="md_theme_light_secondaryContainer">#C1F18E</color>
<color name="md_theme_light_onSecondaryContainer">#0E2000</color>
<color name="md_theme_light_tertiary">#006783</color>
<color name="md_theme_light_onTertiary">#FFFFFF</color>
<color name="md_theme_light_tertiaryContainer">#BDE9FF</color>
<color name="md_theme_light_onTertiaryContainer">#001F2A</color>
<color name="md_theme_light_error">#BA1A1A</color>
<color name="md_theme_light_errorContainer">#FFDAD6</color>
<color name="md_theme_light_onError">#FFFFFF</color>
<color name="md_theme_light_onErrorContainer">#410002</color>
<color name="md_theme_light_background">#FDFCF5</color>
<color name="md_theme_light_onBackground">#1B1C18</color>
<color name="md_theme_light_surface">#FDFCF5</color>
<color name="md_theme_light_onSurface">#1B1C18</color>
<color name="md_theme_light_surfaceVariant">#E1E4D5</color>
<color name="md_theme_light_onSurfaceVariant">#44483D</color>
<color name="md_theme_light_outline">#75796C</color>
<color name="md_theme_light_inverseOnSurface">#F2F1E9</color>
<color name="md_theme_light_inverseSurface">#30312C</color>
<color name="md_theme_light_inversePrimary">#92DA3E</color>
<color name="md_theme_light_shadow">#000000</color>
<color name="md_theme_light_surfaceTint">#3E6A00</color>
<color name="md_theme_light_outlineVariant">#C5C8BA</color>
<color name="md_theme_light_scrim">#000000</color>
<color name="md_theme_dark_primary">#92DA3E</color>
<color name="md_theme_dark_onPrimary">#1E3700</color>
<color name="md_theme_dark_primaryContainer">#2D5000</color>
<color name="md_theme_dark_onPrimaryContainer">#ACF758</color>
<color name="md_theme_dark_secondary">#A6D475</color>
<color name="md_theme_dark_onSecondary">#1D3700</color>
<color name="md_theme_dark_secondaryContainer">#2C5000</color>
<color name="md_theme_dark_onSecondaryContainer">#C1F18E</color>
<color name="md_theme_dark_tertiary">#65D3FF</color>
<color name="md_theme_dark_onTertiary">#003546</color>
<color name="md_theme_dark_tertiaryContainer">#004D64</color>
<color name="md_theme_dark_onTertiaryContainer">#BDE9FF</color>
<color name="md_theme_dark_error">#FFB4AB</color>
<color name="md_theme_dark_errorContainer">#93000A</color>
<color name="md_theme_dark_onError">#690005</color>
<color name="md_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_background">#1B1C18</color>
<color name="md_theme_dark_onBackground">#E3E3DB</color>
<color name="md_theme_dark_surface">#1B1C18</color>
<color name="md_theme_dark_onSurface">#E3E3DB</color>
<color name="md_theme_dark_surfaceVariant">#44483D</color>
<color name="md_theme_dark_onSurfaceVariant">#C5C8BA</color>
<color name="md_theme_dark_outline">#8E9285</color>
<color name="md_theme_dark_inverseOnSurface">#1B1C18</color>
<color name="md_theme_dark_inverseSurface">#E3E3DB</color>
<color name="md_theme_dark_inversePrimary">#3E6A00</color>
<color name="md_theme_dark_shadow">#000000</color>
<color name="md_theme_dark_surfaceTint">#92DA3E</color>
<color name="md_theme_dark_outlineVariant">#44483D</color>
<color name="md_theme_dark_scrim">#000000</color>
<!-- player colors --> <!-- player colors -->
<color name="player_white">#ffffff</color> <color name="player_white">#ffffff</color>
<color name="player_text">#deffffff</color>
<color name="player_text_secondary">#99ffffff</color>
<!-- launcher/splash screen colors -->
<color name="ic_launcher_background">#ffffff</color> <color name="ic_launcher_background">#ffffff</color>
<color name="ic_splash_background">#ffffff</color> <color name="ic_splash_background">#ffffff</color>
</resources> </resources>

View File

@ -59,7 +59,8 @@
<string name="account_tier_mega_fan" translatable="false">Mega Fan</string> <string name="account_tier_mega_fan" translatable="false">Mega Fan</string>
<string name="account_tier_ultimate_fan" translatable="false">Ultimate Fan</string> <string name="account_tier_ultimate_fan" translatable="false">Ultimate Fan</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="settings_content_language">Preferred content language</string> <string name="settings_audio_language">Audio language</string>
<string name="settings_subtitle_language">Subtitle language</string>
<string name="settings_content_language_desc">English</string> <string name="settings_content_language_desc">English</string>
<string name="settings_content_language_none">None</string> <string name="settings_content_language_none">None</string>
<string name="settings_prefer_subbed">Prefer subbed</string> <string name="settings_prefer_subbed">Prefer subbed</string>
@ -69,6 +70,7 @@
<string name="theme">Theme</string> <string name="theme">Theme</string>
<string name="theme_light">Light</string> <string name="theme_light">Light</string>
<string name="theme_dark">Dark</string> <string name="theme_dark">Dark</string>
<string name="theme_system">System</string>
<string name="dev_settings">Developer Settings</string> <string name="dev_settings">Developer Settings</string>
<string name="update_playhead">Playhead updates</string> <string name="update_playhead">Playhead updates</string>
<string name="update_playhead_desc">Update episode playhead on cr</string> <string name="update_playhead_desc">Update episode playhead on cr</string>

View File

@ -1,57 +1,6 @@
<resources> <resources>
<!-- application themes -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="popupMenuStyle">@style/Widget.App.PopupMenu</item>
<item name="searchViewStyle">@style/SearchViewStyle</item>
</style>
<style name="AppTheme.Light" parent="AppTheme"> <!-- search view style -->
<item name="themePrimary">@color/themePrimaryLight</item>
<item name="themeSecondary">@color/themeSecondaryLight</item>
<item name="textPrimary">@color/textPrimaryLight</item>
<item name="textSecondary">@color/textSecondaryLight</item>
<item name="android:textColor">@color/textPrimaryLight</item>
<item name="android:textColorPrimary">@color/textPrimaryLight</item>
<item name="android:textColorHint">@color/textSecondaryLight</item>
<item name="shapeTextBackground">@color/textBackgroundLight</item>
<item name="iconColor">@color/iconColorLight</item>
<item name="buttonBackground">@color/buttonBackgroundLight</item>
</style>
<style name="AppTheme.Dark" parent="AppTheme">
<item name="themePrimary">@color/themePrimaryDark</item>
<item name="themeSecondary">@color/themeSecondaryDark</item>
<item name="textPrimary">@color/textPrimaryDark</item>
<item name="textSecondary">@color/textSecondaryDark</item>
<item name="android:textColor">@color/textPrimaryDark</item>
<item name="android:textColorPrimary">@color/textPrimaryDark</item>
<item name="android:textColorHint">@color/textSecondaryDark</item>
<item name="shapeTextBackground">@color/textBackgroundDark</item>
<item name="iconColor">@color/iconColorDark</item>
<item name="buttonBackground">@color/buttonBackgroundDark</item>
<item name="materialAlertDialogTheme">@style/ThemeOverlay.App.MaterialAlertDialog.Dark</item>
<!-- change on click indicator color for manually set components -->
<item name="colorControlHighlight">@color/controlHighlightDark</item>
</style>
<!-- dialog themes -->
<style name="ThemeOverlay.App.MaterialAlertDialog.Dark" parent="ThemeOverlay.MaterialComponents.MaterialAlertDialog">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorSurface">@color/themeSecondaryDark</item>
<item name="colorOnSurface">@color/textPrimaryDark</item>
<item name="android:colorControlNormal">@color/textSecondaryDark</item> <!-- Radio button unchecked-->
<item name="materialAlertDialogTitleTextStyle">@style/MaterialAlertDialog.App.Title.Text</item>
</style>
<style name="MaterialAlertDialog.App.Title.Text" parent="MaterialAlertDialog.MaterialComponents.Title.Text">
<item name="android:textColor">?textPrimary</item>
</style>
<!-- search view theme -->
<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView.ActionBar"> <style name="SearchViewStyle" parent="Widget.AppCompat.SearchView.ActionBar">
<item name="iconifiedByDefault">false</item> <item name="iconifiedByDefault">false</item>
<item name="searchIcon">@drawable/ic_baseline_search_24</item> <item name="searchIcon">@drawable/ic_baseline_search_24</item>
@ -79,7 +28,7 @@
<item name="windowSplashScreenAnimationDuration">200</item> <item name="windowSplashScreenAnimationDuration">200</item>
<!-- Set the theme of the Activity that directly follows your splash screen. --> <!-- Set the theme of the Activity that directly follows your splash screen. -->
<item name="postSplashScreenTheme">@style/AppTheme.Dark</item> # Required. <item name="postSplashScreenTheme">@style/AppTheme</item> <!-- Required -->
</style> </style>
<!-- shapes --> <!-- shapes -->
@ -88,11 +37,6 @@
<item name="cornerSize">5dp</item> <item name="cornerSize">5dp</item>
</style> </style>
<!-- popup menus -->
<style name="Widget.App.PopupMenu" parent="Widget.MaterialComponents.PopupMenu">
<item name="android:popupBackground">?themeSecondary</item>
</style>
<!-- fullscreen dialog fragments --> <!-- fullscreen dialog fragments -->
<style name="FullScreenDialogStyle" parent="AppTheme"> <style name="FullScreenDialogStyle" parent="AppTheme">
<item name="android:windowFullscreen">true</item> <item name="android:windowFullscreen">true</item>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Widget.App.Button" parent="Widget.Material3.Button">
<item name="backgroundTint">@color/button_background_light</item>
<item name="android:textColor">@color/button_text_color_light</item>
<item name="iconTint">@color/button_text_color_light</item>
</style>
<style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
<!-- <item name="materialButtonStyle">@style/Widget.App.Button</item>-->
<item name="searchViewStyle">@style/SearchViewStyle</item>
<item name="materialCardViewStyle">?attr/materialCardViewElevatedStyle</item>
<item name="colorPrimary">@color/seed</item>
<item name="colorOnPrimary">@color/md_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_light_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_light_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_light_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_light_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_light_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_light_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_light_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_light_error</item>
<item name="colorErrorContainer">@color/md_theme_light_errorContainer</item>
<item name="colorOnError">@color/md_theme_light_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_light_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_light_background</item>
<item name="colorOnBackground">@color/md_theme_light_onBackground</item>
<item name="colorSurface">@color/md_theme_light_surface</item>
<item name="colorOnSurface">@color/md_theme_light_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_light_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_light_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_light_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_inversePrimary</item>
<item name="colorTeapodIcon">@color/button_background_light</item>
</style>
</resources>

View File

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = "1.7.20" ext.kotlin_version = "2.0.20"
ext.ktor_version = "2.2.1" ext.ktor_version = "3.0.0"
ext.exo_version = "2.18.2" ext.exo_version = "2.18.7"
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.4.1' classpath 'com.android.tools.build:gradle:8.7.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
@ -23,6 +23,6 @@ allprojects {
} }
} }
task clean(type: Delete) { tasks.register('clean', Delete) {
delete rootProject.buildDir delete rootProject.layout.buildDirectory
} }

View File

@ -3,7 +3,7 @@ Dies ist der erste beta Release von Teapod 1.1.
* Unterstützung für Crunchyroll v2 API * Unterstützung für Crunchyroll v2 API
* Intro überspringen hinzugefügt * Intro überspringen hinzugefügt
* Seperaten Screen für Meine Liste * Seperaten Screen für Meine Liste
* Dynamische SPaltenanzahl für alle Screes um große Bildschirme besser zu unterstützen * Dynamische Spaltenanzahl für alle Screens um große Bildschirme besser zu unterstützen
* Kleine UI/UX Verbesserungen * Kleine UI/UX Verbesserungen
Alle Änderungen: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta1 Alle Änderungen: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta1

View File

@ -0,0 +1,10 @@
Dies ist der zweite beta Release von Teapod 1.1.
* Neues App Design (Material Design 3)
* Unterstützung für Crunchyroll v2 API
* Intro überspringen hinzugefügt
* Seperaten Screen für "Meine Liste"
* Dynamische Spaltenanzahl für alle Screens um große Bildschirme besser zu unterstützen
* Kleine UI/UX Verbesserungen
Alle Änderungen: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta2

View File

@ -0,0 +1,10 @@
Dies ist der dritte beta Release von Teapod 1.1.
* Neues App Design (Material Design 3)
* Unterstützung für Crunchyroll v2 API
* Intro überspringen hinzugefügt
* Seperaten Screen für "Meine Liste"
* Dynamische Spaltenanzahl für alle Screens um große Bildschirme besser zu unterstützen
* Kleine UI/UX Verbesserungen
Alle Änderungen: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta3

View File

@ -3,7 +3,7 @@ This is the first beta release of Teapod 1.1.
* Migrate crunchyroll parser to v2 (fixes crunchyroll) * Migrate crunchyroll parser to v2 (fixes crunchyroll)
* Add skip intro function * Add skip intro function
* Add a separate Watchlist fragment * Add a separate Watchlist fragment
* Dynamically set coulmn count based on the display size * Dynamically set column count based on the display size
* Minor UI/UX improvements * Minor UI/UX improvements
Full changelog: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta1 Full changelog: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta1

View File

@ -0,0 +1,10 @@
This is the second beta release of Teapod 1.1.
* Migrate to material design 3
* Migrate crunchyroll parser to v2 (fixes crunchyroll)
* Add skip intro function
* Add a separate Watchlist fragment
* Dynamically set column count based on the display size
* Minor UI/UX improvements
Full changelog: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta2

View File

@ -0,0 +1,10 @@
This is the third beta release of Teapod 1.1.
* Migrate to material design 3
* Migrate crunchyroll parser to v2 (fixes crunchyroll)
* Add skip intro function
* Add a separate Watchlist fragment
* Dynamically set column count based on the display size
* Minor UI/UX improvements
Full changelog: https://git.mosad.xyz/Seil0/teapod/compare/1.0.0...1.1.0-beta3

View File

@ -16,6 +16,8 @@ org.gradle.jvmargs=-Xmx2048m
# https://developer.android.com/topic/libraries/support-library/androidx-rn # https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX # Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true android.enableJetifier=false
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
android.nonTransitiveRClass=false
android.nonFinalResIds=false

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

38
gradlew vendored
View File

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +82,12 @@ do
esac esac
done done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # This is normally unused
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' ' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -133,22 +134,29 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# shell script including quotes and variable substitutions, so put them in DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded. # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \

23
gradlew.bat vendored
View File

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -42,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail